diff --git a/CHANGELOG.md b/CHANGELOG.md index 11023b28..af0ff12b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,4 @@ * [UIMARCAUTH-6](https://issues.folio.org/browse/UIMARCAUTH-6) Add View MARC authority record permission. * [UIMARCAUTH-5](https://issues.folio.org/browse/UIMARCAUTH-5) Add Edit MARC authority record permission. * [UIMARCAUTH-16](https://issues.folio.org/browse/UIMARCAUTH-16) Implement MARC Authorities Search Box. +* [UIMARCAUTH-20](https://issues.folio.org/browse/UIMARCAUTH-20) Implement Results List Column Chooser. diff --git a/package.json b/package.json index 326678e4..4351abc2 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "inflected": "^2.0.4", "jest": "^26.6.3", "jest-junit": "^12.0.0", + "lodash": "^4.17.4", "react": "^17.0.2", "react-dom": "^17.0.2", "react-intl": "^5.8.0", diff --git a/src/components/SearchResultsList/SearchResultsList.js b/src/components/SearchResultsList/SearchResultsList.js index b85f4a96..6ac6ca53 100644 --- a/src/components/SearchResultsList/SearchResultsList.js +++ b/src/components/SearchResultsList/SearchResultsList.js @@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl'; import { MultiColumnList } from '@folio/stripes/components'; import { AuthorityShape } from '../../constants/shapes'; +import { searchResultListColumns } from '../../constants'; const propTypes = { authorities: PropTypes.arrayOf(AuthorityShape).isRequired, @@ -11,22 +12,18 @@ const propTypes = { onNeedMoreData: PropTypes.func.isRequired, pageSize: PropTypes.number.isRequired, totalResults: PropTypes.number, + visibleColumns: PropTypes.arrayOf(PropTypes.string).isRequired, }; const authRef = 'Auth/Ref'; -const searchResultListColumns = { - AUTH_REF_TYPE: 'authRefType', - HEADING_REF: 'headingRef', - HEADING_TYPE: 'headingType', -}; - const SearchResultsList = ({ authorities, totalResults, loading, pageSize, onNeedMoreData, + visibleColumns, }) => { const columnMapping = { [searchResultListColumns.AUTH_REF_TYPE]: , @@ -45,11 +42,6 @@ const SearchResultsList = ({ : authority.authRefType; }, }; - const visibleColumns = [ - searchResultListColumns.AUTH_REF_TYPE, - searchResultListColumns.HEADING_REF, - searchResultListColumns.HEADING_TYPE, - ]; return ( render( { expect(getAllByText('Twain, Mark')).toHaveLength(15); }); + + it('should display 3 columns', () => { + const { getByText } = renderSearchResultsList(); + + expect(getByText('ui-marc-authorities.search-results-list.authRefType')).toBeDefined(); + expect(getByText('ui-marc-authorities.search-results-list.headingRef')).toBeDefined(); + expect(getByText('ui-marc-authorities.search-results-list.headingType')).toBeDefined(); + }); + + describe('when show columns checkbox for "Type of Heading" is not checked', () => { + it('should display 2 columns', () => { + const { queryByText } = renderSearchResultsList({ + visibleColumns: [ + searchResultListColumns.AUTH_REF_TYPE, + searchResultListColumns.HEADING_REF, + ], + }); + + expect(queryByText('ui-marc-authorities.search-results-list.authRefType')).toBeDefined(); + expect(queryByText('ui-marc-authorities.search-results-list.headingRef')).toBeDefined(); + expect(queryByText('ui-marc-authorities.search-results-list.headingType')).toBeNull(); + }); + }); }); diff --git a/src/constants/index.js b/src/constants/index.js index 6994aae6..4edb3e6d 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -1,3 +1,4 @@ export * from './searchableIndexesValues'; export * from './rawSearchableIndexes'; export * from './searchableIndexesMap'; +export * from './searchResultsListColumns'; diff --git a/src/constants/searchResultsListColumns.js b/src/constants/searchResultsListColumns.js new file mode 100644 index 00000000..4df39454 --- /dev/null +++ b/src/constants/searchResultsListColumns.js @@ -0,0 +1,5 @@ +export const searchResultListColumns = { + AUTH_REF_TYPE: 'authRefType', + HEADING_REF: 'headingRef', + HEADING_TYPE: 'headingType', +}; diff --git a/src/views/AuthoritiesSearch/AuthoritiesSearch.js b/src/views/AuthoritiesSearch/AuthoritiesSearch.js index f6b9e3af..ad815b7c 100644 --- a/src/views/AuthoritiesSearch/AuthoritiesSearch.js +++ b/src/views/AuthoritiesSearch/AuthoritiesSearch.js @@ -6,7 +6,10 @@ import { useHistory, useLocation, } from 'react-router-dom'; -import { useIntl } from 'react-intl'; +import { + FormattedMessage, + useIntl, +} from 'react-intl'; import queryString from 'query-string'; import { useLocalStorage, @@ -24,6 +27,8 @@ import { CollapseFilterPaneButton, ExpandFilterPaneButton, PersistedPaneset, + useColumnManager, + ColumnManagerMenu, } from '@folio/stripes/smart-components'; import { @@ -36,10 +41,15 @@ import { SearchResultsList, } from '../../components'; import { useAuthorities } from '../../hooks/useAuthorities'; -import { rawSearchableIndexes } from '../../constants'; +import { + rawSearchableIndexes, + searchResultListColumns, +} from '../../constants'; import css from './AuthoritiesSearch.css'; +const prefix = 'authorities'; + const AuthoritiesSearch = () => { const intl = useIntl(); const [, getNamespace] = useNamespace(); @@ -55,6 +65,16 @@ const AuthoritiesSearch = () => { const [searchDropdownValue, setSearchDropdownValue] = useState(''); const [searchIndex, setSearchIndex] = useState(''); + const columnMapping = { + [searchResultListColumns.AUTH_REF_TYPE]: , + [searchResultListColumns.HEADING_REF]: , + [searchResultListColumns.HEADING_TYPE]: , + }; + const { + visibleColumns, + toggleColumn, + } = useColumnManager(prefix, columnMapping); + useEffect(() => { const locationSearchParams = queryString.parse(location.search); @@ -121,6 +141,18 @@ const AuthoritiesSearch = () => { ); }; + const renderActionMenu = () => { + return ( + + ); + }; + const searchableIndexes = rawSearchableIndexes.map(index => ({ label: intl.formatMessage({ id: index.label }), value: index.value, @@ -189,12 +221,14 @@ const AuthoritiesSearch = () => { defaultWidth="fill" paneTitle={intl.formatMessage({ id: 'ui-marc-authorities.meta.title' })} firstMenu={renderResultsFirstMenu()} + actionMenu={renderActionMenu} > diff --git a/src/views/AuthoritiesSearch/AuthoritiesSearch.test.js b/src/views/AuthoritiesSearch/AuthoritiesSearch.test.js index e5a930c8..784a6323 100644 --- a/src/views/AuthoritiesSearch/AuthoritiesSearch.test.js +++ b/src/views/AuthoritiesSearch/AuthoritiesSearch.test.js @@ -5,6 +5,7 @@ import { fireEvent, } from '@testing-library/react'; +import mockMapValues from 'lodash/mapValues'; import routeData from 'react-router'; import { createMemoryHistory } from 'history'; @@ -13,7 +14,10 @@ import '../../../test/jest/__mock__'; import Harness from '../../../test/jest/helpers/harness'; import AuthoritiesSearch from './AuthoritiesSearch'; -import { searchableIndexesValues } from '../../constants'; +import { + searchableIndexesValues, + searchResultListColumns, +} from '../../constants'; const history = createMemoryHistory(); const historyReplaceSpy = jest.spyOn(history, 'replace'); @@ -22,6 +26,15 @@ jest.mock('../../hooks/useAuthorities', () => ({ useAuthorities: () => ({ authorities: [] }), })); +jest.mock('../../components', () => ({ + ...jest.requireActual('../../components'), + SearchResultsList: (props) => { + const mapedProps = mockMapValues(props, (prop) => ((typeof prop === 'object') ? JSON.stringify(prop) : prop)); + + return (
); + }, +})); + const renderAuthoritiesSearch = (props = {}) => render( @@ -67,6 +80,12 @@ describe('Given AuthoritiesSearch', () => { expect(getByRole('button', { name: 'stripes-smart-components.resetAll' })).toBeDefined(); }); + it('display "Action" button', () => { + const { getByRole } = renderAuthoritiesSearch(); + + expect(getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })).toBeDefined(); + }); + describe('when textarea is not empty and Reset all button is clicked', () => { it('should clear textarea', () => { const { @@ -178,4 +197,52 @@ describe('Given AuthoritiesSearch', () => { }); }); }); + + describe('when click on "Action" button', () => { + it('should display "Show columns" section', () => { + const { + getByRole, + getByText, + } = renderAuthoritiesSearch(); + + fireEvent.click(getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(getByText('stripes-smart-components.columnManager.showColumns')).toBeDefined(); + }); + + it('should display "Authorized/Reference" and "Type of heading" checkboxes', () => { + const { getByRole } = renderAuthoritiesSearch(); + + fireEvent.click(getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(getByRole('checkbox', { name: 'ui-marc-authorities.search-results-list.authRefType' })).toBeDefined(); + expect(getByRole('checkbox', { name: 'ui-marc-authorities.search-results-list.headingType' })).toBeDefined(); + }); + + it('should be checked by the default', () => { + const { getByRole } = renderAuthoritiesSearch(); + + fireEvent.click(getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + + expect(getByRole('checkbox', { name: 'ui-marc-authorities.search-results-list.authRefType' })).toBeChecked(); + expect(getByRole('checkbox', { name: 'ui-marc-authorities.search-results-list.headingType' })).toBeChecked(); + }); + + describe('when click on "Type of Heading" checkbox', () => { + it('should hide "Type of Heading" column', () => { + const { + getByRole, + getByTestId, + } = renderAuthoritiesSearch(); + + fireEvent.click(getByRole('button', { name: 'stripes-components.paneMenuActionsToggleLabel' })); + fireEvent.click(getByRole('checkbox', { name: 'ui-marc-authorities.search-results-list.headingType' })); + + expect(getByTestId('SearchResultsList')).toHaveAttribute('visibleColumns', JSON.stringify([ + searchResultListColumns.AUTH_REF_TYPE, + searchResultListColumns.HEADING_REF, + ])); + }); + }); + }); });