diff --git a/CHANGELOG.md b/CHANGELOG.md
index e841e4d8a..8e6c98ef8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -104,6 +104,7 @@
* Provide an instance `tenantId` to the PO line form when creating an order from the instance. Refs UIIN-2614.
* Bump @folio/stripes-acq-components dependency version to 5.0.0. Refs UIIN-2620.
* ECS: Check when sharing instance with source=MARC is complete before re-fetching it. Refs UIIN-2605.
+* Update all facets after changing a term or selecting a facet option. Refs UIIN-2610.
## [9.4.12](https://github.com/folio-org/ui-inventory/tree/v9.4.12) (2023-09-21)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.11...v9.4.12)
diff --git a/src/common/hooks/useFacets.js b/src/common/hooks/useFacets.js
index ae4d17ce8..a8ef5e659 100644
--- a/src/common/hooks/useFacets.js
+++ b/src/common/hooks/useFacets.js
@@ -4,15 +4,27 @@ import _ from 'lodash';
import { useFacetSettings } from '../../stores/facetsStore';
+// Facets behavior (useFacets and withFacets):
+// - when the user opens a facet, the first 6 options must be fetched for it;
+// - when the user clicks the "+More" button under the options, all options for that facet must be fetched;
+// - when the user places the cursor in a facet's input field, all options for it must be fetched;
+// - when multiple facets are open and the user enters a value in the search box, options must be fetched for all open facets.
+// - when multiple facets are open and the user selects an option of any facet, options must be fetched for all open facets.
+
const useFacets = (
segmentAccordions,
segmentOptions,
selectedFacetFilters,
getNewRecords,
data,
+ isFetchFacetsAfterReset = true,
) => {
const {
- query: { query, filters = '' },
+ query: {
+ query,
+ qindex,
+ filters = '',
+ },
onFetchFacets,
parentResources: { facets },
} = data;
@@ -30,7 +42,9 @@ const useFacets = (
const prevAccordionsState = useRef(accordions);
const prevFilters = useRef({});
const prevUrl = useRef({});
- const prevQuery = useRef('');
+ const prevQindex = useRef('');
+ const isSearchOptionChanged = useRef(false);
+ const isReset = useRef(false);
const onToggleSection = useCallback(({ id }) => {
setAccordions(curState => {
@@ -155,6 +169,12 @@ const useFacets = (
showLoadingForAllFacets
]);
+ useEffect(() => {
+ isSearchOptionChanged.current = !query && !filters && qindex && prevQindex.current && qindex !== prevQindex.current;
+ isReset.current = !query && !filters && !qindex;
+ prevQindex.current = qindex;
+ }, [qindex, filters, query]);
+
useEffect(() => {
if (!_.isEmpty(records)) {
const newRecords = getNewRecords(records);
@@ -192,10 +212,16 @@ const useFacets = (
useEffect(() => {
if (!_.isEmpty(accordionsData)) {
- const isNoFilterSelected = _.every(accordionsData, value => !value?.isSelected);
- if (!query && prevQuery.current && isNoFilterSelected) return;
+ // When there is a value in the search box and any facet option is selected and the user resets the search,
+ // and `isFetchFacetsAfterReset` is true, then two useEffects are called, one is tracking `query` and another is
+ // tracking `accordionsData`, hence 2 calls are fired, so let's check url to make only 1 call.
+ const areOpenFacetsAlreadyFetched = prevUrl.current.all === location.search;
- handleFetchFacets({ focusedFacet: facetNameToOpen });
+ if (isSearchOptionChanged.current || (!isFetchFacetsAfterReset && isReset.current) || areOpenFacetsAlreadyFetched) {
+ return;
+ }
+ handleFetchFacets();
+ prevUrl.current.all = location.search;
}
}, [accordionsData]);
@@ -207,15 +233,14 @@ const useFacets = (
useEffect(() => {
const isSomeFacetOpened = _.some(accordions, isFacetOpened => isFacetOpened);
- const isValidQuery = (query && query !== prevQuery.current) || (query !== undefined && prevQuery.current);
+
+ if (isSearchOptionChanged.current || (!isFetchFacetsAfterReset && isReset.current)) {
+ return;
+ }
if (isSomeFacetOpened) {
- if (isValidQuery) {
- prevQuery.current = query;
- handleFetchFacets({ facetToOpen: facetNameToOpen });
- }
- } else if (isValidQuery) {
- prevQuery.current = query;
+ handleFetchFacets();
+ prevUrl.current.all = location.search;
}
}, [query]);
diff --git a/src/common/hooks/useFacets.test.js b/src/common/hooks/useFacets.test.js
index 66f8bb28d..17d01ba0b 100644
--- a/src/common/hooks/useFacets.test.js
+++ b/src/common/hooks/useFacets.test.js
@@ -43,6 +43,378 @@ describe('useFacets', () => {
useFacetSettings.mockReturnValue([selectedFacetFilters, jest.fn()]);
});
+ describe('onFetchFacets', () => {
+ const _segmentAccordions = {
+ resource: false,
+ format: false,
+ };
+
+ const _segmentOptions = {
+ resourceTypeOptions: [],
+ instanceFormatOptions: [],
+ };
+
+ const _selectedFacetFilters = {
+ resource: undefined,
+ format: undefined,
+ };
+
+ const _getNewRecords = jest.fn();
+
+ const _data = {
+ instanceFormats: [
+ {
+ 'id': '8d511d33-5e85-4c5d-9bce-6e3c9cd0c324',
+ 'name': 'unmediated -- volume',
+ 'code': 'nc',
+ 'source': 'rdacarrier',
+ },
+ {
+ 'id': 'f5e8210f-7640-459b-a71f-552567f92369',
+ 'name': 'computer -- online resource',
+ 'code': 'cr',
+ 'source': 'rdacarrier',
+ }, {
+ 'id': '5cb91d15-96b1-4b8a-bf60-ec310538da66',
+ 'name': 'audio -- audio disc',
+ 'code': 'sd',
+ 'source': 'rdacarrier',
+ }
+ ],
+ resourceTypes: [
+ {
+ 'id': '6312d172-f0cf-40f6-b27d-9fa8feaf332f',
+ 'name': 'text',
+ 'code': 'txt',
+ 'source': 'rdacontent',
+ },
+ {
+ 'id': '30fffe0e-e985-4144-b2e2-1e8179bdb41f',
+ 'name': 'unspecified',
+ 'code': 'zzz',
+ 'source': 'rdacontent',
+ },
+ {
+ 'id': '535e3160-763a-42f9-b0c0-d8ed7df6e2a2',
+ 'name': 'still image',
+ 'code': 'sti',
+ 'source': 'rdacontent',
+ },
+ ],
+ query: {},
+ parentResources: {
+ facets: { records: [] },
+ },
+ onFetchFacets: jest.fn(),
+ };
+
+ describe('when user opens a facet', () => {
+ it('should fetch only 6 options for this facet', () => {
+ useLocation.mockReturnValue({ search: '?segment=instances&sort=title' });
+
+ const { result } = renderHook(() => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ _selectedFacetFilters,
+ _getNewRecords,
+ _data,
+ ));
+
+ const onToggleSection = result.current[1];
+
+ act(() => { onToggleSection({ id: 'resource' }); });
+
+ expect(_data.onFetchFacets).toHaveBeenCalledWith({ facetToOpen: 'resource' });
+ });
+ });
+
+ describe('when user clicks the "+More" button under options', () => {
+ it('should fetch all options for this facet', () => {
+ const mockSetFacetSettings = jest.fn();
+ useFacetSettings.mockReturnValue([{}, mockSetFacetSettings]);
+
+ const { result } = renderHook(() => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ _selectedFacetFilters,
+ _getNewRecords,
+ _data,
+ ));
+
+ const handleFetchFacets = result.current[2];
+ act(() => { handleFetchFacets({ onMoreClickedFacet: 'resource' }); });
+
+ expect(mockSetFacetSettings).toHaveBeenCalledWith('resource', { isOnMoreClicked: true });
+ expect(_data.onFetchFacets).toHaveBeenCalledWith({ onMoreClickedFacet: 'resource' });
+ });
+ });
+
+ describe('when user places the cursor in the facet`s input field', () => {
+ it('should fetch all options for this facet', () => {
+ const { result } = renderHook(() => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ _selectedFacetFilters,
+ _getNewRecords,
+ _data,
+ ));
+
+ const handleFetchFacets = result.current[2];
+
+ act(() => { handleFetchFacets({ focusedFacet: 'resource' }); });
+
+ expect(_data.onFetchFacets).toHaveBeenCalledWith({ focusedFacet: 'resource' });
+ });
+ });
+
+ describe('when several facets are open and user enters a value in the search box', () => {
+ it('should fetch options for all open facets', () => {
+ useFacetSettings.mockReturnValue([{}, jest.fn()]);
+ useLocation.mockReturnValue({ search: '' });
+
+ const { result, rerender } = renderHook(({ newData }) => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ _selectedFacetFilters,
+ _getNewRecords,
+ newData,
+ ), {
+ initialProps: { newData: _data },
+ });
+
+ const onToggleSection = result.current[1];
+
+ act(() => { onToggleSection({ id: 'resource' }); });
+ act(() => { onToggleSection({ id: 'format' }); });
+
+ _data.onFetchFacets.mockClear();
+
+ useLocation.mockReturnValue({ search: '?query=Mark' });
+
+ rerender({
+ newData: {
+ ..._data,
+ query: { ..._data.query, query: 'Mark' },
+ },
+ });
+
+ expect(_data.onFetchFacets).toHaveBeenCalledWith({
+ accordions: { resource: true, format: true },
+ accordionsData: {},
+ });
+ });
+ });
+
+ describe('when several facets are open and user selects an option of any facet', () => {
+ it('should fetch options for all open facets', () => {
+ useFacetSettings.mockReturnValue([{}, jest.fn()]);
+ useLocation.mockReturnValue({ search: '' });
+ const isFetchFacetsAfterReset = false;
+
+ const { result, rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ newSelectedFacetFilters,
+ _getNewRecords,
+ newData,
+ isFetchFacetsAfterReset,
+ ), {
+ initialProps: {
+ newData: _data,
+ newSelectedFacetFilters: _selectedFacetFilters,
+ },
+ });
+
+ const onToggleSection = result.current[1];
+
+ act(() => { onToggleSection({ id: 'resource' }); });
+ act(() => { onToggleSection({ id: 'format' }); });
+
+ _data.onFetchFacets.mockClear();
+
+ useLocation.mockReturnValue({ search: '?callNumbersTenantId=university&qindex=callNumbers' });
+
+ rerender({
+ newData: {
+ ..._data,
+ query: { ..._data.query, filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f' },
+ },
+ newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] },
+ });
+
+ expect(_data.onFetchFacets).toHaveBeenCalledWith({
+ accordions: { resource: true, format: true },
+ accordionsData: {
+ resource: { isSelected: true },
+ },
+ });
+ });
+ });
+
+ describe('when there is a value in the search box and any facet option is selected', () => {
+ describe('and the user selects another search option', () => {
+ it('should not call onFetchFacets', () => {
+ useFacetSettings.mockReturnValue([{}, jest.fn()]);
+ useLocation.mockReturnValue({ search: '?callNumbersTenantId=university&qindex=callNumbers&query=Mark' });
+ const isFetchFacetsAfterReset = false;
+
+ const { rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ newSelectedFacetFilters,
+ _getNewRecords,
+ newData,
+ isFetchFacetsAfterReset,
+ ), {
+ initialProps: {
+ newData: {
+ ..._data,
+ query: {
+ ..._data.query,
+ query: 'Mark',
+ qindex: 'callNumbers',
+ filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f',
+ },
+ },
+ newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] },
+ },
+ });
+
+ _data.onFetchFacets.mockClear();
+
+ useLocation.mockReturnValue('');
+
+ rerender({
+ newData: {
+ ..._data,
+ query: {
+ ..._data.query,
+ query: undefined,
+ qindex: 'local',
+ filters: undefined,
+ },
+ },
+ newSelectedFacetFilters: _selectedFacetFilters,
+ });
+
+ expect(_data.onFetchFacets).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('when the "Search" lookup and there is a value in the search box and facet option is selected', () => {
+ describe('and the user reset the search', () => {
+ it('should call onFetchFacets for all open facets once', () => {
+ useFacetSettings.mockReturnValue([{}, jest.fn()]);
+ useLocation.mockReturnValue({ search: '?segment=instances&sort=title&query=Mark' });
+
+ const { result, rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ newSelectedFacetFilters,
+ _getNewRecords,
+ newData,
+ ), {
+ initialProps: {
+ newData: {
+ ..._data,
+ query: {
+ ..._data.query,
+ query: 'Mark',
+ qindex: '',
+ filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f',
+ },
+ },
+ newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] },
+ },
+ });
+
+ const onToggleSection = result.current[1];
+
+ act(() => { onToggleSection({ id: 'resource' }); });
+
+ _data.onFetchFacets.mockClear();
+
+ useLocation.mockReturnValue({ search: '' });
+
+ rerender({
+ newData: {
+ ..._data,
+ query: {
+ ..._data.query,
+ query: undefined,
+ qindex: undefined,
+ filters: undefined,
+ },
+ },
+ newSelectedFacetFilters: _selectedFacetFilters,
+ });
+
+ expect(_data.onFetchFacets).toHaveBeenCalledTimes(1);
+ expect(_data.onFetchFacets).toHaveBeenCalledWith({
+ accordions: { format: false, resource: true },
+ accordionsData: expect.anything(),
+ });
+ });
+ });
+ });
+
+ describe('when the "Browse" lookup and there is a value in the search box and facet option is selected', () => {
+ describe('and the user reset the search', () => {
+ it('should not call onFetchFacets', () => {
+ useFacetSettings.mockReturnValue([{}, jest.fn()]);
+ useLocation.mockReturnValue({ search: '?query=Mark' });
+ const isFetchFacetsAfterReset = false;
+
+ const { result, rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets(
+ _segmentAccordions,
+ _segmentOptions,
+ newSelectedFacetFilters,
+ _getNewRecords,
+ newData,
+ isFetchFacetsAfterReset,
+ ), {
+ initialProps: {
+ newData: {
+ ..._data,
+ query: {
+ ..._data.query,
+ query: 'Mark',
+ qindex: '',
+ filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f',
+ },
+ },
+ newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] },
+ },
+ });
+
+ const onToggleSection = result.current[1];
+
+ act(() => { onToggleSection({ id: 'resource' }); });
+
+ _data.onFetchFacets.mockClear();
+
+ useLocation.mockReturnValue({ search: '' });
+
+ rerender({
+ newData: {
+ ..._data,
+ query: {
+ ..._data.query,
+ query: undefined,
+ qindex: undefined,
+ filters: undefined,
+ },
+ },
+ newSelectedFacetFilters: _selectedFacetFilters,
+ });
+
+ expect(_data.onFetchFacets).not.toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
it('returns initial state', () => {
const { result } = renderHook(() => useFacets(
segmentAccordions,
diff --git a/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js b/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js
index 55ad1ad28..d0e40898c 100644
--- a/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js
+++ b/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js
@@ -19,14 +19,16 @@ const BrowseInventoryFilters = ({
const data = useContext(DataContext);
const filters = omit(activeFilters || {}, ['qindex', 'query']);
+ const isBrowseLookup = true;
const filtersData = {
...data,
browseType: searchIndex,
- onFetchFacets: fetchFacets(data),
+ onFetchFacets: fetchFacets(data, isBrowseLookup),
parentResources: resources,
query: {
query: activeFilters.query,
filters: parseFiltersToStr(filters),
+ qindex: searchIndex,
}
};
diff --git a/src/components/CheckboxFacet/CheckboxFacet.js b/src/components/CheckboxFacet/CheckboxFacet.js
index 818a55f9a..aa169ecfc 100644
--- a/src/components/CheckboxFacet/CheckboxFacet.js
+++ b/src/components/CheckboxFacet/CheckboxFacet.js
@@ -49,10 +49,6 @@ export default class CheckboxFacet extends React.Component {
) {
this.updateMore();
}
-
- if (prevDataLength > currentDataLength && currentDataLength === DEFAULT_FILTERS_NUMBER) {
- this.setDefaultMore();
- }
}
onMoreClick = (totalOptions) => {
@@ -105,10 +101,6 @@ export default class CheckboxFacet extends React.Component {
});
}
- setDefaultMore = () => {
- this.setState(({ more: SHOW_OPTIONS_COUNT }));
- }
-
render() {
const {
dataOptions,
diff --git a/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js b/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js
index b2e0bd573..1fa52e953 100644
--- a/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js
+++ b/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js
@@ -119,7 +119,8 @@ const InstanceFiltersBrowse = props => {
segmentOptions,
selectedFacetFilters,
getNewRecords,
- props.data
+ props.data,
+ false,
);
return (
diff --git a/src/constants.js b/src/constants.js
index 4b963ee7b..118aaf841 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -372,50 +372,6 @@ export const FACETS_TO_REQUEST = {
[FACETS.HOLDINGS_TYPE]: FACETS_CQL.HOLDINGS_TYPE,
};
-const INSTANCES_FACET_ENDPOINT = 'search/instances/facets';
-const CONTRIBUTORS_FACET_ENDPOINT = 'search/contributors/facets';
-const SUBJECTS_FACET_ENDPOINT = 'search/subjects/facets';
-
-export const FACETS_ENDPOINTS = {
- [FACETS.SHARED]: INSTANCES_FACET_ENDPOINT,
- [FACETS.CONTRIBUTORS_SHARED]: CONTRIBUTORS_FACET_ENDPOINT,
- [FACETS.SUBJECTS_SHARED]: SUBJECTS_FACET_ENDPOINT,
- [FACETS.HELD_BY]: INSTANCES_FACET_ENDPOINT,
- [FACETS.CALL_NUMBERS_HELD_BY]: INSTANCES_FACET_ENDPOINT,
- [FACETS.CONTRIBUTORS_HELD_BY]: CONTRIBUTORS_FACET_ENDPOINT,
- [FACETS.SUBJECTS_HELD_BY]: SUBJECTS_FACET_ENDPOINT,
- [FACETS.EFFECTIVE_LOCATION]: INSTANCES_FACET_ENDPOINT,
- [FACETS.LANGUAGE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.RESOURCE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.FORMAT]: INSTANCES_FACET_ENDPOINT,
- [FACETS.MODE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.NATURE_OF_CONTENT]: INSTANCES_FACET_ENDPOINT,
- [FACETS.STAFF_SUPPRESS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.INSTANCES_DISCOVERY_SUPPRESS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_DISCOVERY_SUPPRESS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.ITEMS_DISCOVERY_SUPPRESS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.SOURCE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.STATUS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.INSTANCES_TAGS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.ITEMS_TAGS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_TAGS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.MATERIAL_TYPE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.ITEM_STATUS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_PERMANENT_LOCATION]: INSTANCES_FACET_ENDPOINT,
- [FACETS.CREATED_DATE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.UPDATED_DATE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_CREATED_DATE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_UPDATED_DATE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_SOURCE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.ITEMS_CREATED_DATE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.ITEMS_UPDATED_DATE]: INSTANCES_FACET_ENDPOINT,
- [FACETS.STATISTICAL_CODE_IDS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.HOLDINGS_STATISTICAL_CODE_IDS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.ITEMS_STATISTICAL_CODE_IDS]: INSTANCES_FACET_ENDPOINT,
- [FACETS.NAME_TYPE]: CONTRIBUTORS_FACET_ENDPOINT,
- [FACETS.HOLDINGS_TYPE]: INSTANCES_FACET_ENDPOINT,
-};
-
export const FACETS_OPTIONS = {
SHARED_OPTIONS: 'sharedOptions',
HELD_BY_OPTIONS: 'heldByOptions',
diff --git a/src/withFacets.js b/src/withFacets.js
index 85123e20d..1675d2fcf 100644
--- a/src/withFacets.js
+++ b/src/withFacets.js
@@ -1,8 +1,12 @@
-import { reduce } from 'lodash';
+import {
+ reduce,
+ omit,
+} from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { makeQueryFunction } from '@folio/stripes/smart-components';
+import { buildFilterQuery } from '@folio/stripes-acq-components';
import {
getQueryTemplate,
@@ -13,9 +17,7 @@ import {
} from './filterConfig';
import {
DEFAULT_FILTERS_NUMBER,
- FACETS,
FACETS_TO_REQUEST,
- FACETS_ENDPOINTS,
CQL_FIND_ALL,
browseModeOptions,
browseModeMap,
@@ -114,7 +116,62 @@ function withFacets(WrappedComponent) {
}, '');
};
- fetchFacets = (data) => async (properties = {}) => {
+ getEndpoint = (queryIndex) => {
+ if (queryIndex === browseModeOptions.CONTRIBUTORS) {
+ return 'search/contributors/facets';
+ }
+
+ if (queryIndex === browseModeOptions.SUBJECTS) {
+ return 'search/subjects/facets';
+ }
+
+ return 'search/instances/facets';
+ }
+
+ getCqlQuery = (isBrowseLookup, query, queryIndex, data) => {
+ if (isBrowseLookup) {
+ const normalizedFilters = {
+ ...Object.entries(query).reduce((acc, [key, value]) => ({
+ ...acc,
+ [FACETS_TO_REQUEST[key] || key]: value,
+ }), {}),
+ query: query.query || undefined,
+ };
+
+ const otherFilters = omit(normalizedFilters, 'query', 'qindex');
+ const hasSelectedFacetOption = Object.values(otherFilters).some(Boolean);
+
+ let queryForBrowseFacets = '';
+
+ const isTypedCallNumber = Object.values(browseCallNumberOptions).includes(queryIndex)
+ && queryIndex !== browseCallNumberOptions.CALL_NUMBERS;
+
+ if (hasSelectedFacetOption) {
+ if (isTypedCallNumber) {
+ queryForBrowseFacets = `callNumberType="${queryIndex}"`;
+ }
+ } else if (isTypedCallNumber) {
+ queryForBrowseFacets = `callNumberType="${queryIndex}"`;
+ } else {
+ queryForBrowseFacets = 'cql.allRecords=1';
+ }
+
+ return buildFilterQuery(
+ {
+ query: queryForBrowseFacets,
+ qindex: normalizedFilters.qindex,
+ ...otherFilters,
+ },
+ _query => _query,
+ undefined,
+ false,
+ );
+ }
+
+ return buildQuery(query, {}, { ...data, query }, { log: () => null }) || '';
+ }
+
+ fetchFacets = (data, isBrowseLookup) => async (properties = {}) => {
const {
onMoreClickedFacet,
focusedFacet,
@@ -136,24 +193,13 @@ function withFacets(WrappedComponent) {
// temporary query value
const params = { query: 'id = *' };
- const cqlQuery = buildQuery(query, {}, { ...data, query }, { log: () => null }) || '';
const facetName = facetToOpen || onMoreClickedFacet || focusedFacet;
const facetNameToRequest = FACETS_TO_REQUEST[facetName];
const paramsUrl = new URLSearchParams(window.location.search);
const queryIndex = paramsUrl.get('qindex') || query?.qindex;
+ const cqlQuery = this.getCqlQuery(isBrowseLookup, query, queryIndex, data);
- if (facetName === FACETS.NAME_TYPE) {
- params.query = 'contributorNameTypeId=*';
- } else if ([FACETS.CONTRIBUTORS_SHARED, FACETS.CONTRIBUTORS_HELD_BY].includes(facetName)) {
- params.query = 'name=*';
- } else if ([FACETS.SUBJECTS_SHARED, FACETS.SUBJECTS_HELD_BY].includes(facetName)) {
- params.query = 'value=*';
- } else if (cqlQuery
- && Object.values(browseCallNumberOptions).includes(queryIndex)
- && queryIndex !== browseCallNumberOptions.CALL_NUMBERS
- ) {
- params.query = `callNumberType="${queryIndex}"`;
- } else if (cqlQuery && queryIndex !== browseModeOptions.CALL_NUMBERS) {
+ if (cqlQuery) {
params.query = cqlQuery;
}
@@ -174,7 +220,7 @@ function withFacets(WrappedComponent) {
try {
reset();
- const requestPath = FACETS_ENDPOINTS[facetName] || 'search/instances/facets';
+ const requestPath = this.getEndpoint(queryIndex);
await GET({ path: requestPath, params });
} catch (error) {
throw new Error(error);
diff --git a/src/withFacets.test.js b/src/withFacets.test.js
index 7f0e553b0..b8c789e99 100644
--- a/src/withFacets.test.js
+++ b/src/withFacets.test.js
@@ -9,17 +9,19 @@ import {
queryIndexes,
FACETS_CQL,
browseCallNumberOptions,
+ browseModeOptions,
} from './constants';
const WrappedComponent = ({
fetchFacets,
data = {},
properties = {},
+ isBrowseLookup,
}) => {
return (
@@ -36,12 +38,392 @@ const mutator = {
};
describe('withFacets', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
describe('when opening a contributots shared facet', () => {
- it('should make a request with correct params', () => {
+ describe('and there are no selected options in other facets', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseModeOptions.CONTRIBUTORS,
+ query: '',
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
+ query: '(cql.allRecords=1)',
+ },
+ path: 'search/contributors/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseModeOptions.CONTRIBUTORS,
+ query: '',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
+ query: 'instances.tenantId==("college")',
+ },
+ path: 'search/contributors/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet and there is a value in the search box', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseModeOptions.CONTRIBUTORS,
+ query: 'Marc Twain',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
+ query: 'instances.tenantId==("college")',
+ },
+ path: 'search/contributors/facets',
+ }));
+ });
+ });
+ });
+
+ describe('when opening a subjects shared facet', () => {
+ describe('and there are no selected options in other facets', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseModeOptions.SUBJECTS,
+ query: '',
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
+ query: '(cql.allRecords=1)',
+ },
+ path: 'search/subjects/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseModeOptions.SUBJECTS,
+ query: '',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
+ query: 'instances.tenantId==("college")',
+ },
+ path: 'search/subjects/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet and there is a value in the search box', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseModeOptions.SUBJECTS,
+ query: 'Marc Twain',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
+ query: 'instances.tenantId==("college")',
+ },
+ path: 'search/subjects/facets',
+ }));
+ });
+ });
+ });
+
+ describe('when opening call numbers browse shared facet', () => {
+ describe('and there are no selected options in other facets', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseCallNumberOptions.CALL_NUMBERS,
+ query: '',
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: '(cql.allRecords=1)',
+ },
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseCallNumberOptions.CALL_NUMBERS,
+ query: '',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: 'instances.tenantId==("college")',
+ },
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet and there is a value in the search box', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseCallNumberOptions.CALL_NUMBERS,
+ query: 'Marc Twain',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: 'instances.tenantId==("college")',
+ },
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+ });
+
+ describe('when opening call numbers sub-type browse shared facet', () => {
+ describe('and there are no selected options in other facets', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseCallNumberOptions.DEWEY,
+ query: '',
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: '(callNumberType="dewey")',
+ },
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseCallNumberOptions.DEWEY,
+ query: '',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: '(callNumberType="dewey") and instances.tenantId==("college")',
+ },
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+
+ describe('and there is a selected option in another facet and there is a value in the search box', () => {
+ it('should make a request with correct request options', () => {
+ const resources = {
+ query: {
+ qindex: browseCallNumberOptions.DEWEY,
+ query: 'Marc Twain',
+ contributorsTenantId: ['college'],
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: '(callNumberType="dewey") and instances.tenantId==("college")',
+ },
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+ });
+
+ describe('when Advanced search is used', () => {
+ it('should fetch facets with the correct params', async () => {
const resources = {
query: {
- qindex: queryIndexes.CONTRIBUTOR,
- query: '',
+ qindex: queryIndexes.ADVANCED_SEARCH,
+ query: 'isbn containsAll test1 or title exactPhrase test2 or keyword startsWith test3',
},
};
@@ -49,7 +431,7 @@ describe('withFacets', () => {
);
@@ -57,27 +439,24 @@ describe('withFacets', () => {
expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
params: {
- facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
- query: 'name=*',
+ facet: 'source:6',
+ query: 'isbn="*test1*" or title=="test2" or keyword all "test3*"',
},
}));
});
});
- describe('when opening a subjects shared facet', () => {
- it('should make a request with correct params', () => {
+ describe('when user opens a facet', () => {
+ it('should fetch only 6 options for this facet', () => {
const resources = {
- query: {
- qindex: queryIndexes.SUBJECT,
- query: '',
- },
+ query: {},
};
const { getByText } = render(
);
@@ -85,27 +464,51 @@ describe('withFacets', () => {
expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
params: {
- facet: `${FACETS_CQL.INSTANCES_SHARED}:6`,
- query: 'value=*',
+ facet: `${FACETS_CQL.SHARED}:6`,
+ query: 'id = *',
},
+ path: 'search/instances/facets',
}));
});
});
- describe('when using call numbers browse', () => {
- it('should make a request with correct params', () => {
+ describe('when user clicks the "+More" button under options', () => {
+ it('should fetch all options for this facet', () => {
const resources = {
- query: {
- qindex: browseCallNumberOptions.CALL_NUMBERS,
- query: 'test',
+ query: {},
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.INSTANCE_TYPE}`,
+ query: 'id = *',
},
+ path: 'search/instances/facets',
+ }));
+ });
+ });
+
+ describe('when user places the cursor in the facet`s input field', () => {
+ it('should fetch all options for this facet', () => {
+ const resources = {
+ query: {},
};
const { getByText } = render(
);
@@ -113,27 +516,33 @@ describe('withFacets', () => {
expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
params: {
- facet: `${FACETS_CQL.SHARED}:6`,
+ facet: `${FACETS_CQL.INSTANCE_TYPE}`,
query: 'id = *',
},
+ path: 'search/instances/facets',
}));
});
});
- describe('when using call numbers sub-type browse', () => {
- it('should make a request with correct params', () => {
+ describe('when several facets are open and user enters a value in the search box', () => {
+ it('should fetch options for all open facets', () => {
const resources = {
query: {
- qindex: browseCallNumberOptions.DEWEY,
- query: 'test',
+ qindex: 'title',
+ query: 'Marc',
},
};
+ const properties = {
+ accordions: { resource: true, format: true },
+ accordionsData: {},
+ };
+
const { getByText } = render(
);
@@ -141,19 +550,27 @@ describe('withFacets', () => {
expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
params: {
- facet: `${FACETS_CQL.SHARED}:6`,
- query: 'callNumberType="dewey"',
+ facet: `${FACETS_CQL.INSTANCE_TYPE}:6,${FACETS_CQL.INSTANCE_FORMAT}:6`,
+ query: 'title all "Marc"',
},
+ path: 'search/instances/facets',
}));
});
});
- describe('when Advanced search is used', () => {
- it('should fetch facets with the correct params', async () => {
+ describe('when several facets are open and user selects an option of any facet', () => {
+ it('should fetch options for all open facets', () => {
const resources = {
query: {
- qindex: queryIndexes.ADVANCED_SEARCH,
- query: 'isbn containsAll test1 or title exactPhrase test2 or keyword startsWith test3',
+ query: '',
+ filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f',
+ },
+ };
+
+ const properties = {
+ accordions: { resource: true, format: true },
+ accordionsData: {
+ resource: { isSelected: true },
},
};
@@ -161,7 +578,7 @@ describe('withFacets', () => {
);
@@ -169,10 +586,49 @@ describe('withFacets', () => {
expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
params: {
- facet: 'source:6',
- query: 'isbn="*test1*" or title=="test2" or keyword all "test3*"',
+ facet: `${FACETS_CQL.INSTANCE_TYPE},${FACETS_CQL.INSTANCE_FORMAT}:6`,
+ query: 'instanceTypeId=="6312d172-f0cf-40f6-b27d-9fa8feaf332f"',
},
+ path: 'search/instances/facets',
}));
});
});
+
+ describe('when several facets are open and user selects an option of one facet', () => {
+ describe('and for another one the "+More" button is clicked', () => {
+ it('should fetch all options for the facet with selected option and for the facet with +More clicked', () => {
+ const resources = {
+ query: {
+ query: '',
+ filters: 'format.8d511d33-5e85-4c5d-9bce-6e3c9cd0c324',
+ },
+ };
+
+ const properties = {
+ accordions: { shared: true, resource: true, format: true },
+ accordionsData: {
+ resource: { isOnMoreClicked: true },
+ format: { isSelected: true },
+ },
+ };
+
+ const { getByText } = render(
+
+ );
+
+ fireEvent.click(getByText('fetchFacetsButton'));
+
+ expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({
+ params: {
+ facet: `${FACETS_CQL.SHARED}:6,${FACETS_CQL.INSTANCE_TYPE},${FACETS_CQL.INSTANCE_FORMAT}`,
+ query: expect.anything(),
+ },
+ }));
+ });
+ });
+ });
});