diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf6cba1d..943853786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Reset CheckboxFacet state.more when user resets search form and fewer facet options are loaded. Fixes UIIN-2531. * Edit instance success toast no longer shows the instance HRID. Fixes UIIN-2588. * Show facet options, if they exist, after clicking the +More button. Refs UIIN-2533. +* If Shared & Held by facets were selected in the Browse search, then retain them in the Search lookup after clicking the record. Refs UIIN-2608. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/components/BrowseResultsList/BrowseResultsList.js b/src/components/BrowseResultsList/BrowseResultsList.js index 9a3f068d5..d387e4be2 100644 --- a/src/components/BrowseResultsList/BrowseResultsList.js +++ b/src/components/BrowseResultsList/BrowseResultsList.js @@ -49,6 +49,7 @@ const BrowseResultsList = ({ onNeedMoreData, }, totalRecords, + filters, }) => { const data = useContext(DataContext); const { search } = useLocation(); @@ -75,7 +76,7 @@ const BrowseResultsList = ({ id={listId} totalCount={totalRecords} contentData={browseData} - formatter={getBrowseResultsFormatter({ data, browseOption })} + formatter={getBrowseResultsFormatter({ data, browseOption, filters })} visibleColumns={VISIBLE_COLUMNS_MAP[browseOption]} isEmptyMessage={isEmptyMessage} isSelected={isSelected} @@ -101,6 +102,7 @@ const BrowseResultsList = ({ BrowseResultsList.propTypes = { browseData: PropTypes.arrayOf(PropTypes.object), + filters: PropTypes.object.isRequired, isEmptyMessage: PropTypes.node.isRequired, isLoading: PropTypes.bool, pagination: PropTypes.shape({ diff --git a/src/components/BrowseResultsList/BrowseResultsList.test.js b/src/components/BrowseResultsList/BrowseResultsList.test.js index ade758be1..9c5833edc 100644 --- a/src/components/BrowseResultsList/BrowseResultsList.test.js +++ b/src/components/BrowseResultsList/BrowseResultsList.test.js @@ -15,6 +15,8 @@ import { browseModeOptions, BROWSE_INVENTORY_ROUTE, INVENTORY_ROUTE, + browseCallNumberOptions, + FACETS, } from '../../constants'; import { DataContext } from '../../contexts'; import BrowseResultsList from './BrowseResultsList'; @@ -59,14 +61,37 @@ const defaultProps = { pageConfig: [0, null, null], }, totalRecords: 1, + filters: {}, }; const mockContext = { contributorNameTypes: [{ id: '2b94c631-fca9-4892-a730-03ee529ffe2a', }], + contributorTypes: [{ + id: '6e09d47d-95e2-4d8a-831b-f777b8ef6d81', + name: 'Author', + }], }; +const contributorsData = [ + { + 'name': 'Toth, Josh', + 'contributorTypeId': [ + '6e09d47d-95e2-4d8a-831b-f777b8ef6d81' + ], + 'contributorNameTypeId': '2b94c631-fca9-4892-a730-03ee529ffe2a', + 'isAnchor': false, + 'totalRecords': 1 + }, +]; +const subjectsData = [ + { + 'value': 'Trivia and miscellanea', + 'totalRecords': 8 + }, +]; + const renderBrowseResultsList = (props = {}) => renderWithIntl( @@ -103,6 +128,94 @@ describe('BrowseResultsList', () => { ); }); + describe.each([ + { searchOption: browseCallNumberOptions.CALL_NUMBERS, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + { searchOption: browseCallNumberOptions.DEWEY, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + { searchOption: browseCallNumberOptions.LIBRARY_OF_CONGRESS, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + { searchOption: browseCallNumberOptions.LOCAL, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + { searchOption: browseCallNumberOptions.NATIONAL_LIBRARY_OF_MEDICINE, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + { searchOption: browseCallNumberOptions.OTHER, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + { searchOption: browseCallNumberOptions.SUPERINTENDENT, shared: FACETS.SHARED, heldBy: FACETS.CALL_NUMBERS_HELD_BY }, + ])('when the search option is $searchOption and the Shared and/or HeldBy facets are selected', ({ searchOption, shared, heldBy }) => { + describe('and the user clicks on a record in the list', () => { + it('should be navigated to the Search lookup with those filters', async () => { + history = createMemoryHistory({ + initialEntries: [{ + pathname: BROWSE_INVENTORY_ROUTE, + search: `${heldBy}=college&qindex=${searchOption}&query=a&${shared}=true&${shared}=false`, + }], + }); + + renderBrowseResultsList({ + filters: { + qindex: searchOption, + query: 'a', + [shared]: ['true', 'false'], + [heldBy]: ['college'], + }, + }); + + fireEvent.click(screen.getByText(defaultProps.browseData[2].fullCallNumber)); + + expect(history.location.search).toContain('?filters=shared.true%2Cshared.false%2CtenantId.college'); + }); + }); + }); + + describe('when the search option is Contributors and the Shared and/or HeldBy facets are selected', () => { + describe('and the user clicks on a record in the list', () => { + it('should be navigated to the Search lookup with those filters', async () => { + history = createMemoryHistory({ + initialEntries: [{ + pathname: BROWSE_INVENTORY_ROUTE, + search: 'contributorsShared=true&contributorsShared=false&contributorsTenantId=college&qindex=contributors', + }], + }); + + renderBrowseResultsList({ + filters: { + qindex: 'contributors', + contributorsShared: ['true', 'false'], + contributorsTenantId: ['college'], + }, + browseData: contributorsData, + }); + + fireEvent.click(screen.getByText(contributorsData[0].name)); + + expect(history.location.search).toContain( + '?filters=searchContributors.2b94c631-fca9-4892-a730-03ee529ffe2a%2Cshared.true%2Cshared.false%2CtenantId.college' + ); + }); + }); + }); + + describe('when the search option is Subjects and the Shared and/or HeldBy facets are selected', () => { + describe('and the user clicks on a record in the list', () => { + it('should be navigated to the Search lookup with those filters', async () => { + history = createMemoryHistory({ + initialEntries: [{ + pathname: BROWSE_INVENTORY_ROUTE, + search: 'qindex=browseSubjects&subjectsShared=true&subjectsShared=false&subjectsTenantId=college', + }], + }); + + renderBrowseResultsList({ + filters: { + qindex: 'browseSubjects', + subjectsShared: ['true', 'false'], + subjectsTenantId: ['college'], + }, + browseData: subjectsData, + }); + + fireEvent.click(screen.getByText(subjectsData[0].value)); + + expect(history.location.search).toContain('?filters=shared.true%2Cshared.false%2CtenantId.college'); + }); + }); + }); + describe('when Instance record is linked to an authority record', () => { describe('by clicking on the icon of an authority app', () => { const record = { diff --git a/src/components/BrowseResultsList/getBrowseResultsFormatter.js b/src/components/BrowseResultsList/getBrowseResultsFormatter.js index 101fa6526..723a3975d 100644 --- a/src/components/BrowseResultsList/getBrowseResultsFormatter.js +++ b/src/components/BrowseResultsList/getBrowseResultsFormatter.js @@ -31,9 +31,10 @@ const getTargetRecord = ( item, row, browseOption, + filters, ) => { const record = getFullMatchRecord(item, row.isAnchor); - const searchParams = getSearchParams(row, browseOption); + const searchParams = getSearchParams(row, browseOption, filters); const isNotClickable = isRowPreventsClick(row, browseOption); if (isNotClickable) return record; @@ -98,12 +99,13 @@ const renderMarcAuthoritiesLink = (authorityId, content) => { const getBrowseResultsFormatter = ({ data, browseOption, + filters, }) => { return { title: r => getFullMatchRecord(r.instance?.title, r.isAnchor), subject: r => { if (r?.totalRecords) { - const subject = getTargetRecord(r?.value, r, browseOption); + const subject = getTargetRecord(r?.value, r, browseOption, filters); if (browseOption === browseModeOptions.SUBJECTS && r.authorityId) { return renderMarcAuthoritiesLink(r.authorityId, subject); } @@ -114,13 +116,13 @@ const getBrowseResultsFormatter = ({ }, callNumber: r => { if (r?.instance || r?.totalRecords) { - return getTargetRecord(r?.fullCallNumber, r, browseOption); + return getTargetRecord(r?.fullCallNumber, r, browseOption, filters); } return ; }, contributor: r => { if (r?.totalRecords) { - const fullMatchRecord = getTargetRecord(r.name, r, browseOption); + const fullMatchRecord = getTargetRecord(r.name, r, browseOption, filters); if (browseOption === browseModeOptions.CONTRIBUTORS && r.authorityId) { return renderMarcAuthoritiesLink(r.authorityId, fullMatchRecord); diff --git a/src/components/BrowseResultsList/utils.js b/src/components/BrowseResultsList/utils.js index ab55c744d..6a38054ee 100644 --- a/src/components/BrowseResultsList/utils.js +++ b/src/components/BrowseResultsList/utils.js @@ -1,4 +1,7 @@ +import omit from 'lodash/omit'; + import { + browseCallNumberOptions, browseModeOptions, FACETS, queryIndexes, @@ -14,45 +17,89 @@ export const isRowPreventsClick = (row, browseOption) => { ); }; -export const getSearchParams = (row, qindex) => { +const facetsToString = (filters, facetNameInBrowse, facetNameInSearch) => { + return filters[facetNameInBrowse]?.map(value => `${facetNameInSearch}.${value}`).join(','); +}; + +const getExtraFilters = (row, qindex, allFilters) => { + const filtersOnly = omit(allFilters, 'qindex', 'query'); + const extraFacets = []; + + let sharedFacetName; + let heldByFacetName; + + if (qindex === browseModeOptions.SUBJECTS) { + sharedFacetName = FACETS.SUBJECTS_SHARED; + heldByFacetName = FACETS.SUBJECTS_HELD_BY; + + if (row.authorityId) { + extraFacets.push(`${FACETS.AUTHORITY_ID}.${row.authorityId}`); + } + } else if (qindex === browseModeOptions.CONTRIBUTORS) { + sharedFacetName = FACETS.CONTRIBUTORS_SHARED; + heldByFacetName = FACETS.CONTRIBUTORS_HELD_BY; + + extraFacets.push(`${FACETS.SEARCH_CONTRIBUTORS}.${row.contributorNameTypeId}`); + } else if (Object.values(browseCallNumberOptions).includes(qindex)) { + sharedFacetName = FACETS.SHARED; + heldByFacetName = FACETS.CALL_NUMBERS_HELD_BY; + } + + const sharedExtraFacets = facetsToString(filtersOnly, sharedFacetName, FACETS.SHARED); + const heldByExtraFacets = facetsToString(filtersOnly, heldByFacetName, FACETS.HELD_BY); + const extraFacetsString = [...extraFacets, sharedExtraFacets, heldByExtraFacets].filter(Boolean).join(','); + + return extraFacetsString ? { filters: extraFacetsString } : {}; +}; + +export const getSearchParams = (row, qindex, allFilters) => { + const filters = getExtraFilters(row, qindex, allFilters); + const optionsMap = { [browseModeOptions.CALL_NUMBERS]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.DEWEY]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.LIBRARY_OF_CONGRESS]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.LOCAL]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.NATIONAL_LIBRARY_OF_MEDICINE]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.OTHER]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.SUPERINTENDENT]: { qindex: queryIndexes.CALL_NUMBER, query: row.shelfKey, + ...filters, }, [browseModeOptions.CONTRIBUTORS]: { qindex: queryIndexes.CONTRIBUTOR, query: row.name, - filters: `${FACETS.SEARCH_CONTRIBUTORS}.${row.contributorNameTypeId}`, + ...filters, }, [browseModeOptions.SUBJECTS]: { qindex: queryIndexes.SUBJECT, query: row.value, - ...(row.authorityId && { filters: `${FACETS.AUTHORITY_ID}.${row.authorityId}` }), + ...filters, }, }; diff --git a/src/components/BrowseResultsPane/BrowseResultsPane.js b/src/components/BrowseResultsPane/BrowseResultsPane.js index 3c93d14b3..a9d64aca1 100644 --- a/src/components/BrowseResultsPane/BrowseResultsPane.js +++ b/src/components/BrowseResultsPane/BrowseResultsPane.js @@ -91,6 +91,7 @@ const BrowseResultsPane = ({ isLoading={isFetching} pagination={pagination} totalRecords={totalRecords} + filters={filters} /> );