diff --git a/help-docs/chaise/facet-panel.md b/help-docs/chaise/facet-panel.md
new file mode 100644
index 000000000..0fdcf9b2d
--- /dev/null
+++ b/help-docs/chaise/facet-panel.md
@@ -0,0 +1,56 @@
+# Filter panel
+
+Filters are the data constraints or restrictions that are applied during search operations. For example, search for "mouse" data, or data submitted by certain "principal investigators".
+
+The filter panel (displayed on the left under "Refine search") contains a list of filter controls used to set these constraints. This page provides details on how the filter panel works. Please find the topics discussed in the table of contents below:
+
+- [Customizing the order of filters](#customizing-the-order-of-filters)
+ - [How to move filters](#how-to-move-filters)
+ - [How to save the order](#how-to-save-the-order)
+ - [How to apply the default order](#how-to-apply-the-default-order)
+ - [How to apply the saved order](#how-to-apply-the-saved-order)
+
+
+## Customizing the order of filters {id=customizing-the-order-of-filters}
+
+You can change the order of filters in the filter panel. This section goes over how you could move filters, save the customized order, and, if needed, discard the customized order.
+
+### How to move filters {id=how-to-move-filters}
+
+To move the filters,
+
+ 1. Left click on the grab ( :span::/span:{.fa-solid .fa-grip-vertical .help-page-icon} ) icon.
+ 2. While holding the left click, move your mouse to the desired location.
+ 3. Release the left click to finish the drag movement.
+
+Keep in mind that this order is not going to be persistent and will change back after refreshing the page. To save the order, please refer to the [How to save the order](#how-to-save-the-order) section.
+
+### How to save the order {id=how-to-save-the-order}
+
+If you would like to save the order in your browser,
+
+ 1. Click on the menu icon (:span::/span:{.fa-solid .fa-bars .help-page-icon}) besides the "Refine search".
+
+ 2. In the opened menu, select the ":span::/span:{.fa-solid .fa-check-to-slot .help-page-icon} Save filter order" option.
+
+This will ensure your customized order is saved in your browser. If you would like to apply the default order, please refer to the [How to discard the saved order](#how-to-apply-the-default-order) section.
+
+### How to apply the default order {id=how-to-apply-the-default-order}
+
+If you've customized the order, or applied the saved state, you may go back to the default order by following these steps:
+
+ 1. Click on the menu icon ( :span::/span:{.fa-solid .fa-bars .help-page-icon} ) besides the "Refine search".
+
+ 2. In the opened menu, select the ":span::/span:{.fa-solid .fa-undo .help-page-icon} Reset to default" option.
+
+With this, the filter panel will reset to the default order. Some filters move, and some might get opened/closed depending on the current state of the page. If you want to return to your saved order, please follow the steps described [here](#how-to-apply-the-saved-order).
+
+### How to apply the saved order {id=how-to-apply-the-saved-order}
+
+If you have saved the filter order before, we will apply this order when you load the page. If you made some modifications to this order, follow these steps to go back to the saved order:
+
+ 1. Click on the menu icon ( :span::/span:{.fa-solid .fa-bars .help-page-icon} ) besides the "Refine search".
+
+ 2. In the opened menu, select the ":span::/span:{.fa-solid .fa-check .help-page-icon} Apply saved state" option.
+
+With this, the filter panel will reset to the saved order. Some filters move, and some might get opened/closed depending on the current state of the page.
diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss
index 6698b4961..7434d5463 100644
--- a/src/assets/scss/_buttons.scss
+++ b/src/assets/scss/_buttons.scss
@@ -95,6 +95,18 @@
&.chaise-btn-no-padding {
padding: 0;
}
+
+ // this is currently designed mostly for
+ &.chaise-btn-with-indicator:before {
+ content: '';
+ background-color: map-get(variables.$color-map, 'primary');
+ position: absolute;
+ top: -1px;
+ right: -1px;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ }
}
//TODO
@@ -113,8 +125,3 @@
.dropdown-item:active {
background-color: unset;
}
-
-// override the default behavior of dropdown toggle btn in bootstrap
-.show > button.chaise-btn.btn.dropdown-toggle {
- @include helpers.chaise-btn-primary();
-}
diff --git a/src/assets/scss/_dropdown.scss b/src/assets/scss/_dropdown.scss
index f7843ecfd..a34814c6d 100644
--- a/src/assets/scss/_dropdown.scss
+++ b/src/assets/scss/_dropdown.scss
@@ -1,5 +1,10 @@
@use 'sass:map';
@use 'variables';
+@use 'helpers';
+
+.dropdown.chaise-dropdown-no-icon .dropdown-toggle::after{
+ display: none;
+}
// the .dropdown selector is neede to ensure overriding default bootstrap and chaise styles
.chaise-dropdown.dropdown {
@@ -111,6 +116,7 @@
// style to override react-bootstrap
.dropdown-item {
padding: 0;
+
}
// change the default link behaviors to not show the underline
@@ -132,6 +138,13 @@
padding: 3px 15px 3px 15px;
line-height: 1.4;
+ &.dropdown-item-w-icon {
+ padding: 3px 15px 3px 8px;
+ .dropdown-item-icon {
+ margin-right: 7px;
+ }
+ }
+
&:focus,
&:hover {
color: lighten(map-get(variables.$color-map, 'black'), 10%);
@@ -166,7 +179,7 @@
right: unset;
}
- .disable-link {
+ .disable-link, .disabled {
pointer-events: none !important;
cursor: default !important;
color: map-get(variables.$color-map, 'disabled') !important;
@@ -177,3 +190,18 @@
}
}
}
+
+// override the default behavior of dropdown toggle btn in bootstrap
+.dropdown.show > button.chaise-btn.dropdown-toggle {
+ &.chaise-btn-primary {
+ @include helpers.chaise-btn-primary();
+ }
+
+ &.chaise-btn-secondary, &.chaise-btn-tertiary {
+ @include helpers.chaise-btn-secondary();
+
+ &:focus {
+ background-color: none;
+ }
+ }
+}
diff --git a/src/assets/scss/_faceting.scss b/src/assets/scss/_faceting.scss
index b48cae95d..3b37793cf 100644
--- a/src/assets/scss/_faceting.scss
+++ b/src/assets/scss/_faceting.scss
@@ -1,6 +1,14 @@
@use 'sass:map';
@use 'variables';
+.side-panel-container {
+ .side-panel-heading-menu {
+ position: absolute;
+ top: -37px;
+ left: 125px;
+ }
+}
+
.faceting-columns-container {
// disable the animation from the accordion
.accordion-collapse.collapsing {
diff --git a/src/assets/scss/app.scss b/src/assets/scss/app.scss
index 33030119e..30c3525a7 100644
--- a/src/assets/scss/app.scss
+++ b/src/assets/scss/app.scss
@@ -596,6 +596,10 @@ html {
padding: 0;
padding-bottom: 3px;
}
+
+ .pull-left {
+ display: flex;
+ }
}
}
@@ -1076,6 +1080,10 @@ html {
padding-top: 20px;
padding-bottom: 20px;
}
+
+ .help-page-icon {
+ margin: 0 5px;
+ }
}
/****** switch user accounts help page *******/
diff --git a/src/components/faceting/faceting.tsx b/src/components/faceting/faceting.tsx
index d8ba4241e..54edbac83 100644
--- a/src/components/faceting/faceting.tsx
+++ b/src/components/faceting/faceting.tsx
@@ -1,8 +1,15 @@
import '@isrd-isi-edu/chaise/src/assets/scss/_faceting.scss';
+//react-beatiful-dnd
+import {
+ DragDropContext, Draggable, DraggableProvided, DroppableProvided, DropResult
+} from 'react-beautiful-dnd';
+
// Components
import Accordion from 'react-bootstrap/Accordion';
+import ChaiseDroppable from '@isrd-isi-edu/chaise/src/components/chaise-droppable';
import ChaiseTooltip from '@isrd-isi-edu/chaise/src/components/tooltip';
+import Dropdown from 'react-bootstrap/Dropdown';
import FacetChoicePicker from '@isrd-isi-edu/chaise/src/components/faceting/facet-choice-picker';
import FacetCheckPresence from '@isrd-isi-edu/chaise/src/components/faceting/facet-check-presence';
import FacetHeader from '@isrd-isi-edu/chaise/src/components/faceting/facet-header';
@@ -24,13 +31,14 @@ import { ConfigService } from '@isrd-isi-edu/chaise/src/services/config';
import { LogService } from '@isrd-isi-edu/chaise/src/services/log';
import $log from '@isrd-isi-edu/chaise/src/services/logger';
-//react-beatiful-dnd
+// utils
+import { HELP_PAGES } from '@isrd-isi-edu/chaise/src/utils/constants';
import {
- DragDropContext, Draggable, DraggableProvided, DroppableProvided, DropResult
-} from 'react-beautiful-dnd';
-import ChaiseDroppable from '@isrd-isi-edu/chaise/src/components/chaise-droppable';
-import { getFacetOrderStorageKey, getInitialFacetOpenStatus, getInitialFacetOrder } from '@isrd-isi-edu/chaise/src/utils/faceting-utils';
+ getFacetOrderStorageKey, getInitialFacetOpenStatus, getInitialFacetOrder,
+ hasStoredFacetOrder
+} from '@isrd-isi-edu/chaise/src/utils/faceting-utils';
import LocalStorage from '@isrd-isi-edu/chaise/src/utils/storage';
+import { getHelpPageURL } from '@isrd-isi-edu/chaise/src/utils/uri-utils';
type FacetingProps = {
@@ -46,7 +54,7 @@ type FacetingProps = {
registerRecordsetCallbacks: (
getAppliedFilters: () => FacetCheckBoxRow[][],
removeAppliedFilters: (index?: number | 'filters' | 'cfacets') => void,
- focusOnFacet: (index: number, dontUpdate?: boolean) => void
+ focusOnFacet: (index: number, dontUpdate?: boolean) => void,
) => void,
/**
* the recordset's log stack path
@@ -126,6 +134,12 @@ const Faceting = ({
* when this is set to true, we should save the changes in the local storage and then change it back to false.
*/
const [facetListModified, setFacetListModified] = useState(false);
+ /**
+ * whether the current order is based on teh stored facet order or not.
+ */
+ const [isStoredFacetOrderApplied, setIsStoredFacetOrderApplied] = useState(() => {
+ return hasStoredFacetOrder(reference);
+ });
const setFacetModelByIndex = (index: number, updatedVals: { [key: string]: boolean }) => {
setFacetModels((prevFacetModels: FacetModel[]) => {
@@ -260,29 +274,12 @@ const Faceting = ({
*/
useEffect(() => {
registerFacetCallbacks(updateFacetStates, updateFacets);
- registerRecordsetCallbacks(getAppliedFiltersFromRS, removeAppliedFiltersFromRS, focusOnFacet);
}, [facetModels]);
- /**
- * store the facet order in the local stroage if any changes happened to the facets
- */
useEffect(() => {
- if (!facetOrders || !facetOrders.length) return;
- if (!facetListModified) return;
- /**
- * store isOpen state for facets to localStorage
- */
- LocalStorage.setStorage(getFacetOrderStorageKey(reference), facetOrders.map((i) => {
- return {
- name: reference.facetColumns[i].sourceObjectWrapper.name,
- open: facetModelsRef.current[i].isOpen
- };
- }));
-
- // now that the state is saved, just set it to false so we don't update this until the next user action
- setFacetListModified(false);
-
- }, [facetListModified, facetModels, facetOrders])
+ console.log('calling registered in faceting');
+ registerRecordsetCallbacks(getAppliedFiltersFromRS, removeAppliedFiltersFromRS, focusOnFacet);
+ }, [facetModels, facetOrders, facetListModified, isStoredFacetOrderApplied]);
//------------------- flow-control related functions: --------------------//
@@ -629,6 +626,7 @@ const Faceting = ({
// make sure we're saving the new state
setFacetListModified(true);
+ setIsStoredFacetOrderApplied(false);
};
/**
@@ -684,8 +682,63 @@ const Faceting = ({
}
setFacetOrders(items);
- setFacetListModified(true)
+ setFacetListModified(true);
+ setIsStoredFacetOrderApplied(false);
+ };
+
+ const storeFacetOrder = () => {
+ LocalStorage.setStorage(getFacetOrderStorageKey(reference), facetOrders.map((i) => {
+ return {
+ name: reference.facetColumns[i].sourceObjectWrapper.name,
+ open: facetModelsRef.current[i].isOpen
+ };
+ }));
+ setFacetListModified(false);
+ setIsStoredFacetOrderApplied(true);
+ };
+
+ const applyDefaultOrStoredFacetOrder = (useDefault: boolean) => {
+ // change their order
+ setFacetOrders(() => {
+ return getInitialFacetOrder(reference, useDefault).map((o) => o.facetIndex);
+ });
+
+ // open or close facets
+ setFacetModels((prevFacetModels) => {
+ const { openStatus: newOpenStatus } = getInitialFacetOpenStatus(reference, useDefault);
+ return prevFacetModels.map((fm: FacetModel, fmIndex: number) => {
+ const isOpen = newOpenStatus[`${fmIndex}`];
+
+ // if open status has not changed, just return it
+ if (fm.isOpen === isOpen) return fm;
+
+ // if we are closing
+ if (!isOpen) {
+ return { ...fm, isOpen,
+ // hide the spinner:
+ isLoading: false,
+ // if we were waiting for data, make sure to fetch it later
+ initialized: !fm.isLoading
+ }
+ }
+
+ // if we're opening and it's not initialized, initiate the request
+ if (!fm.initialized) {
+ // send a request
+ dispatchFacetUpdate(fmIndex, false);
+ return { ...fm, isOpen, isLoading: true };
+ }
+
+ // otherwise just open it
+ return { ...fm, isOpen };
+ });
+ });
+
+ // set the boolean states
+ setIsStoredFacetOrderApplied(!useDefault);
+ setFacetListModified(false);
}
+
//------------------- render logic: --------------------//
const renderFacetList = () => {
@@ -755,6 +808,61 @@ const Faceting = ({
}
};
+ const renderFacetDropdownMenu = () => {
+ const storedIsAvailable = hasStoredFacetOrder(reference);
+ const showChangeIndicator = facetListModified || (storedIsAvailable && !isStoredFacetOrderApplied);
+ const allowSave = showChangeIndicator;
+ const allowApplyDefault = facetListModified || isStoredFacetOrderApplied;
+ const allowApplySaved = storedIsAvailable && showChangeIndicator;
+
+ return (
+