diff --git a/CHANGELOG.md b/CHANGELOG.md index e18f92d55..680d4960b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 10.0.1 IN PROGRESS +* Instance 3rd pane: Adjust behavior when returning to instance from holdings/item full screen. Refs UIIN-2453. +* Consortial holdings accordion is not appearing after the sharing of Instance. Fixes UIIN-2629. * Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) diff --git a/src/Instance/HoldingsList/Holding/Holding.js b/src/Instance/HoldingsList/Holding/Holding.js index cac25c97a..4456a45f0 100644 --- a/src/Instance/HoldingsList/Holding/Holding.js +++ b/src/Instance/HoldingsList/Holding/Holding.js @@ -26,6 +26,8 @@ const Holding = ({ isViewHoldingsDisabled, isAddItemDisabled, isBarcodeAsHotlink, + instanceId, + pathToAccordionsState, }) => { return (
@@ -57,6 +59,8 @@ const Holding = ({ tenantId={tenantId} isViewHoldingsDisabled={isViewHoldingsDisabled} isAddItemDisabled={isAddItemDisabled} + instanceId={instanceId} + pathToAccordionsState={pathToAccordionsState} > ( ( isHoldingDragSelected={isHoldingDragSelected} isDraggable={isDraggable} isItemsDroppable={isItemsDroppable} + pathToAccordionsState={['holdings']} /> ); diff --git a/src/Instance/HoldingsList/Holding/HoldingAccordion.js b/src/Instance/HoldingsList/Holding/HoldingAccordion.js index e8d64bfda..ed21a6b01 100644 --- a/src/Instance/HoldingsList/Holding/HoldingAccordion.js +++ b/src/Instance/HoldingsList/Holding/HoldingAccordion.js @@ -17,6 +17,11 @@ import { } from '../../../hooks'; import HoldingButtonsGroup from './HoldingButtonsGroup'; +import useHoldingItemsQuery from '../../../hooks/useHoldingItemsQuery'; +import { + useHoldingsAccordionState, + useLocationsQuery, +} from '../../../hooks'; const HoldingAccordion = ({ children, @@ -28,13 +33,16 @@ const HoldingAccordion = ({ tenantId, isViewHoldingsDisabled, isAddItemDisabled, + instanceId, + pathToAccordionsState, }) => { const searchParams = { limit: 0, offset: 0, }; - const [open, setOpen] = useState(false); + const pathToAccordion = [...pathToAccordionsState, holding?.id]; + const [open, setOpen] = useHoldingsAccordionState({ instanceId, pathToAccordion }); const [openFirstTime, setOpenFirstTime] = useState(false); const { totalRecords, isFetching } = useHoldingItemsQuery(holding.id, { searchParams, key: 'itemCount', tenantId }); const { data: locations } = useLocationsQuery({ tenantId }); @@ -121,12 +129,16 @@ HoldingAccordion.propTypes = { holding: PropTypes.object.isRequired, onViewHolding: PropTypes.func.isRequired, onAddItem: PropTypes.func.isRequired, + instanceId: PropTypes.string.isRequired, holdings: PropTypes.arrayOf(PropTypes.object), withMoveDropdown: PropTypes.bool, children: PropTypes.object, tenantId: PropTypes.string, isViewHoldingsDisabled: PropTypes.bool, isAddItemDisabled: PropTypes.bool, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; +HoldingAccordion.defaultProps = { pathToAccordionsState: [] }; + export default HoldingAccordion; diff --git a/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js b/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js index fef613db5..a8e6bead9 100644 --- a/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js +++ b/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js @@ -39,6 +39,8 @@ const HoldingAccordionSetup = () => ( onViewHolding={noop} onAddItem={noop} withMoveDropdown={false} + instanceId="instanceId" + pathToAccordionsState={['holdings']} > <> diff --git a/src/Instance/HoldingsList/Holding/HoldingContainer.js b/src/Instance/HoldingsList/Holding/HoldingContainer.js index 19295eda9..2eca4d8f8 100644 --- a/src/Instance/HoldingsList/Holding/HoldingContainer.js +++ b/src/Instance/HoldingsList/Holding/HoldingContainer.js @@ -41,6 +41,8 @@ const DraggableHolding = ({ isViewHoldingsDisabled, isAddItemDisabled, isBarcodeAsHotlink, + instanceId, + pathToAccordionsState, ...rest }) => { const rowStyles = useMemo(() => ( @@ -76,6 +78,8 @@ const DraggableHolding = ({ isViewHoldingsDisabled={isViewHoldingsDisabled} isAddItemDisabled={isAddItemDisabled} isBarcodeAsHotlink={isBarcodeAsHotlink} + instanceId={instanceId} + pathToAccordionsState={pathToAccordionsState} /> ) } @@ -91,6 +95,7 @@ DraggableHolding.propTypes = { draggingHoldingsCount: PropTypes.number, provided: PropTypes.object.isRequired, snapshot: PropTypes.object.isRequired, + instanceId: PropTypes.string.isRequired, holding: PropTypes.object, onViewHolding: PropTypes.func, onAddItem: PropTypes.func, @@ -98,8 +103,11 @@ DraggableHolding.propTypes = { isViewHoldingsDisabled: PropTypes.bool, isAddItemDisabled: PropTypes.bool, isBarcodeAsHotlink: PropTypes.bool, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; +DraggableHolding.defaultProps = { pathToAccordionsState: [] }; + const HoldingContainer = ({ location, history, @@ -112,6 +120,7 @@ const HoldingContainer = ({ holdingIndex, draggingHoldingsCount, tenantId, + pathToAccordionsState, ...rest }) => { const stripes = useStripes(); @@ -156,6 +165,8 @@ const HoldingContainer = ({ isViewHoldingsDisabled={isViewHoldingsDisabled} isAddItemDisabled={isAddItemDisabled} isBarcodeAsHotlink={isBarcodeAsHotlink} + instanceId={instance?.id} + pathToAccordionsState={pathToAccordionsState} {...rest} /> )} @@ -170,6 +181,8 @@ const HoldingContainer = ({ isViewHoldingsDisabled={isViewHoldingsDisabled} isAddItemDisabled={isAddItemDisabled} isBarcodeAsHotlink={isBarcodeAsHotlink} + instanceId={instance?.id} + pathToAccordionsState={pathToAccordionsState} /> ); }; @@ -188,6 +201,9 @@ HoldingContainer.propTypes = { isViewHoldingsDisabled: PropTypes.bool, isAddItemDisabled: PropTypes.bool, isBarcodeAsHotlink: PropTypes.bool, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; +HoldingContainer.defaultProps = { pathToAccordionsState: [] }; + export default withRouter(HoldingContainer); diff --git a/src/Instance/HoldingsList/Holding/HoldingContainer.test.js b/src/Instance/HoldingsList/Holding/HoldingContainer.test.js index 1cb2c6129..d053d5e37 100644 --- a/src/Instance/HoldingsList/Holding/HoldingContainer.test.js +++ b/src/Instance/HoldingsList/Holding/HoldingContainer.test.js @@ -54,6 +54,7 @@ const renderHoldingContainer = (props = {}) => renderWithIntl( provided={{ draggableProps: { style: true } }} onViewHolding={jest.fn()} onAddItem={jest.fn()} + pathToAccordionsState={['holdings']} {...props} /> diff --git a/src/Instance/HoldingsList/HoldingsList.js b/src/Instance/HoldingsList/HoldingsList.js index a7d43cd80..570c4eb20 100644 --- a/src/Instance/HoldingsList/HoldingsList.js +++ b/src/Instance/HoldingsList/HoldingsList.js @@ -10,6 +10,7 @@ const HoldingsList = ({ isViewHoldingsDisabled, isAddItemDisabled, isBarcodeAsHotlink, + pathToAccordionsState, draggable, droppable, }) => holdings.map(holding => ( @@ -24,6 +25,7 @@ const HoldingsList = ({ isViewHoldingsDisabled={isViewHoldingsDisabled} isAddItemDisabled={isAddItemDisabled} isBarcodeAsHotlink={isBarcodeAsHotlink} + pathToAccordionsState={pathToAccordionsState} /> )); @@ -34,12 +36,14 @@ HoldingsList.propTypes = { isViewHoldingsDisabled: PropTypes.bool, isAddItemDisabled: PropTypes.bool, isBarcodeAsHotlink: PropTypes.bool, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), draggable: PropTypes.bool, droppable: PropTypes.bool, }; HoldingsList.defaultProps = { holdings: [], + pathToAccordionsState: [], }; export default HoldingsList; diff --git a/src/Instance/HoldingsList/HoldingsListContainer.js b/src/Instance/HoldingsList/HoldingsListContainer.js index b12e2f186..30de47e44 100644 --- a/src/Instance/HoldingsList/HoldingsListContainer.js +++ b/src/Instance/HoldingsList/HoldingsListContainer.js @@ -2,9 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useStripes } from '@folio/stripes/core'; -import { - Loading, -} from '@folio/stripes/components'; +import { Loading } from '@folio/stripes/components'; import HoldingsList from './HoldingsList'; import { HoldingsListMovement } from '../InstanceMovement/HoldingMovementList'; @@ -14,6 +12,7 @@ const HoldingsListContainer = ({ instance, isHoldingsMove, tenantId, + pathToAccordionsState, ...rest }) => { const stripes = useStripes(); @@ -35,6 +34,7 @@ const HoldingsListContainer = ({ isViewHoldingsDisabled={!canViewHoldings} isAddItemDisabled={!canCreateItem} isBarcodeAsHotlink={canViewItems} + pathToAccordionsState={pathToAccordionsState} /> ) : ( ) ); @@ -54,6 +55,9 @@ HoldingsListContainer.propTypes = { instance: PropTypes.object.isRequired, isHoldingsMove: PropTypes.bool, tenantId: PropTypes.string, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; +HoldingsListContainer.defaultProps = { pathToAccordionsState: [] }; + export default HoldingsListContainer; diff --git a/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js b/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js index 2e22607b8..fabf873f5 100644 --- a/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js +++ b/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js @@ -1,4 +1,8 @@ -import React, { useContext } from 'react'; +import React, { + useContext, + useEffect, + useRef, +} from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; @@ -13,28 +17,45 @@ import { import { MemberTenantHoldings } from '../MemberTenantHoldings'; import { DataContext } from '../../../contexts'; -import { useSearchForShadowInstanceTenants } from '../../../hooks'; +import { + useHoldingsAccordionState, + useSearchForShadowInstanceTenants, +} from '../../../hooks'; const ConsortialHoldings = ({ instance, userTenantPermissions, }) => { const stripes = useStripes(); + const instanceId = instance?.id; + const prevInstanceId = useRef(instanceId); + const { consortiaTenantsById } = useContext(DataContext); - const { tenants } = useSearchForShadowInstanceTenants({ instanceId: instance?.id }); + const { tenants } = useSearchForShadowInstanceTenants({ instanceId }); const memberTenants = tenants .map(tenant => consortiaTenantsById[tenant.id]) .filter(tenant => !tenant?.isCentral && (tenant?.id !== stripes.okapi.tenant)) .sort((a, b) => a.name.localeCompare(b.name)); + const pathToAccordion = ['consortialHoldings', '_state']; + const [isConsortialAccOpen, setConsortialAccOpen] = useHoldingsAccordionState({ instanceId, pathToAccordion }); + + useEffect(() => { + if (instanceId !== prevInstanceId.current) { + setConsortialAccOpen(false); + prevInstanceId.current = instanceId; + } + }, [instanceId]); + return ( } - closedByDefault + open={isConsortialAccOpen} + onToggle={() => setConsortialAccOpen(prevState => !prevState)} > {!memberTenants.length ? @@ -42,7 +63,7 @@ const ConsortialHoldings = ({ {memberTenants.map(memberTenant => ( { const isInstanceShared = Boolean(isShared || isInstanceShadowCopy(instance?.source)); @@ -205,8 +207,8 @@ const InstanceDetails = forwardRef(({ /> )} - {instance?.shared && ( - diff --git a/src/Instance/InstanceDetails/InstanceDetails.test.js b/src/Instance/InstanceDetails/InstanceDetails.test.js index 86ba79d7e..fcfc915cc 100644 --- a/src/Instance/InstanceDetails/InstanceDetails.test.js +++ b/src/Instance/InstanceDetails/InstanceDetails.test.js @@ -17,9 +17,14 @@ jest.mock('../InstanceDetails/ControllableDetail/ControllableDetail', () => jest jest.mock('../InstanceDetails/SubInstanceGroup/SubInstanceGroup', () => jest.fn().mockReturnValue('SubInstanceGroup')); jest.mock('../InstanceDetails/InstanceAcquisition/InstanceAcquisition', () => jest.fn().mockReturnValue('InstanceAcquisition')); jest.mock('../InstanceDetails/InstanceTitleData/InstanceTitleData', () => jest.fn().mockReturnValue('InstanceTitleData')); +jest.mock('./ConsortialHoldings', () => ({ + ...jest.requireActual('./ConsortialHoldings'), + ConsortialHoldings: () => , +})); const instance = { title: 'Test Title', + source: 'FOLIO', contributors: [], identifiers: [], instanceTypeId: '1234', @@ -30,6 +35,7 @@ const instance = { notes: [], staffSuppress: false, discoverySuppress: false, + shared: false, }; const userTenantPermissions = [{ @@ -53,24 +59,31 @@ const queryClient = new QueryClient(); const actionMenu = jest.fn(); const onClose = jest.fn(); const tagsEnabled = true; + +const renderInstanceDetails = (props) => { + const component = ( + + + + + + + + ); + + return renderWithIntl(component, translationsProperties); +}; + describe('InstanceDetails', () => { it('renders the InstanceDetails component', () => { - renderWithIntl( - - - - , - - - , - translationsProperties - ); + renderInstanceDetails(); + expect(screen.getByText('InstanceTitle')).toBeInTheDocument(); expect(screen.getByText('Add holdings')).toBeInTheDocument(); expect(screen.getByText('Administrative data')).toBeInTheDocument(); @@ -109,22 +122,7 @@ describe('InstanceDetails', () => { ...instance, staffSuppress: true, }; - renderWithIntl( - - - - , - - - , - translationsProperties - ); + renderInstanceDetails({ instance: staffSuppressedInstance }); expect(screen.getByText('Warning: Instance is marked staff suppressed')).toBeInTheDocument(); expect(screen.getByText('Staff suppressed')).toBeInTheDocument(); @@ -135,22 +133,7 @@ describe('InstanceDetails', () => { ...instance, discoverySuppress: true, }; - renderWithIntl( - - - - , - - - , - translationsProperties - ); + renderInstanceDetails({ instance: discoverySuppressedInstance }); expect(screen.getByText('Warning: Instance is marked suppressed from discovery')).toBeInTheDocument(); expect(screen.getByText('Suppressed from discovery')).toBeInTheDocument(); @@ -161,50 +144,21 @@ describe('InstanceDetails', () => { staffSuppress: true, discoverySuppress: true, }; - renderWithIntl( - - - - , - - - , - translationsProperties - ); + renderInstanceDetails({ instance: bothSuppressedInstance }); expect(screen.getByText('Warning: Instance is marked suppressed from discovery and staff suppressed')).toBeInTheDocument(); }); it('expands and collapses the accordion sections', () => { - renderWithIntl( - - - - , - - - , - translationsProperties - ); + renderInstanceDetails(); const expandAllButtons = screen.getByText('Expand all'); const firstAccordionSection = screen.getByRole('button', { name: /Administrative data/i }); const secondAccordionSection = screen.getByRole('button', { name: /Instance notes/i }); const thirdAccordionSection = screen.getByRole('button', { name: /Electronic access/i }); const fourthAccordionSection = screen.getByRole('button', { name: /Classification/i }); - expect(firstAccordionSection.getAttribute('aria-expanded')).toBe('false'); + // Administrative data is open because it has initial data inside it + expect(firstAccordionSection.getAttribute('aria-expanded')).toBe('true'); expect(secondAccordionSection.getAttribute('aria-expanded')).toBe('false'); expect(thirdAccordionSection.getAttribute('aria-expanded')).toBe('false'); expect(fourthAccordionSection.getAttribute('aria-expanded')).toBe('false'); @@ -222,24 +176,39 @@ describe('InstanceDetails', () => { }); it('renders tags button if tagsEnabled is true', () => { - renderWithIntl( - - - - , - - - , - translationsProperties - ); + renderInstanceDetails(); + const button = screen.getAllByRole('button', { id: 'clickable-show-tags' }); fireEvent.click(button[1]); expect(button[1]).toBeEnabled(); }); + + describe('Consortial holdings accordion', () => { + it('should be visible for shared instances', () => { + const sharedInstance = { + ...instance, + shared: true, + }; + renderInstanceDetails({ instance: sharedInstance }); + + expect(screen.getByRole('button', { name: 'Consortial holdings' })).toBeInTheDocument(); + }); + + it('should be visible for shadow instances', () => { + const shadowInstance = { + ...instance, + shared: false, + source: 'CONSORTIUM-FOLIO', + }; + renderInstanceDetails({ instance: shadowInstance }); + + expect(screen.getByRole('button', { name: 'Consortial holdings' })).toBeInTheDocument(); + }); + + it('should not be visible for local instances', () => { + renderInstanceDetails(); + + expect(screen.queryByRole('button', { name: 'Consortial holdings' })).not.toBeInTheDocument(); + }); + }); }); diff --git a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js index 238e0bf05..ea7d5626e 100644 --- a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js +++ b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js @@ -17,6 +17,7 @@ import { MoveItemsContext } from '../../MoveItemsContext'; import { useInstanceHoldingsQuery } from '../../../providers'; import { hasMemberTenantPermission } from '../../../utils'; +import { useHoldingsAccordionState } from '../../../hooks'; import css from './MemberTenantHoldings.css'; @@ -29,7 +30,13 @@ const MemberTenantHoldings = ({ name, id, } = memberTenant; + const instanceId = instance?.id; const stripes = useStripes(); + + const pathToAccordion = ['consortialHoldings', id, '_state']; + const pathToHoldingsAccordion = ['consortialHoldings', id]; + const [isMemberTenantAccOpen, setMemberTenantAccOpen] = useHoldingsAccordionState({ instanceId, pathToAccordion }); + const { holdingsRecords, isLoading } = useInstanceHoldingsQuery(instance?.id, { tenantId: id }); const isUserInCentralTenant = checkIfUserInCentralTenant(stripes); @@ -45,7 +52,8 @@ const MemberTenantHoldings = ({ className={css.memberTenantHoldings} id={`${name}-holdings`} label={name} - closedByDefault + open={isMemberTenantAccOpen} + onToggle={() => setMemberTenantAccOpen(prevValue => !prevValue)} >
{isLoading @@ -61,6 +69,7 @@ const MemberTenantHoldings = ({ isViewHoldingsDisabled={!canViewHoldings} isAddItemDisabled={!canCreateItem} isBarcodeAsHotlink={canViewItems} + pathToAccordionsState={pathToHoldingsAccordion} /> )} diff --git a/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js b/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js index 36aeec7ec..919472900 100644 --- a/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js +++ b/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js @@ -26,6 +26,7 @@ const HoldingsListMovement = ({ isViewHoldingsDisabled, isAddItemDisabled, isBarcodeAsHotlink, + pathToAccordionsState, }) => { const { selectItemsForDrag, @@ -64,6 +65,7 @@ const HoldingsListMovement = ({ isViewHoldingsDisabled={isViewHoldingsDisabled} isAddItemDisabled={isAddItemDisabled} isBarcodeAsHotlink={isBarcodeAsHotlink} + pathToAccordionsState={pathToAccordionsState} /> )) ) : ( @@ -88,10 +90,12 @@ HoldingsListMovement.propTypes = { draggable: PropTypes.bool, droppable: PropTypes.bool, tenantId: PropTypes.string, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; HoldingsListMovement.defaultProps = { holdings: [], + pathToAccordionsState: [], }; export default HoldingsListMovement; diff --git a/src/ViewInstance.js b/src/ViewInstance.js index 0da9d92ce..3aa636308 100644 --- a/src/ViewInstance.js +++ b/src/ViewInstance.js @@ -919,6 +919,7 @@ class ViewInstance extends React.Component { }, ]; const isInstanceLoading = this.state.isLoading || !instance || isCentralTenantPermissionsLoading; + const keyInStorageToHoldingsAccsState = ['holdings']; return ( @@ -947,6 +948,7 @@ class ViewInstance extends React.Component { instance={instance} draggable={this.state.isItemsMovement} tenantId={okapi.tenant} + pathToAccordionsState={keyInStorageToHoldingsAccsState} droppable /> diff --git a/src/hooks/index.js b/src/hooks/index.js index 8d61406b9..51310d19e 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -3,6 +3,7 @@ export { default as useBrowseValidation } from './useBrowseValidation'; export { default as useCallout } from './useCallout'; export { default as useHoldingItemsQuery } from './useHoldingItemsQuery'; export { default as useHoldingMutation } from './useHoldingMutation'; +export { default as useHoldingsAccordionState } from './useHoldingsAccordionState'; export { default as useInstanceMutation } from './useInstanceMutation'; export { default as useHoldingsQueryByHrids } from './useHoldingsQueryByHrids'; export { default as useInventoryBrowse } from './useInventoryBrowse'; diff --git a/src/hooks/useHoldingsAccordionState/index.js b/src/hooks/useHoldingsAccordionState/index.js new file mode 100644 index 000000000..83af4d1a1 --- /dev/null +++ b/src/hooks/useHoldingsAccordionState/index.js @@ -0,0 +1 @@ +export { default } from './useHoldingsAccordionState'; diff --git a/src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.js b/src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.js new file mode 100644 index 000000000..86cad1abb --- /dev/null +++ b/src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.js @@ -0,0 +1,42 @@ +import { + useEffect, + useState, +} from 'react'; +import { + cloneDeep, + get, + set, +} from 'lodash'; + +import { useNamespace } from '@folio/stripes/core'; + +import { + getItem, + setItem, +} from '../../storage'; + +const useHoldingsAccordionState = ({ instanceId, pathToAccordion = [] }) => { + const [namespace] = useNamespace(); + const key = `${namespace}.instanceHoldingsAccordionsState`; + + const instanceHoldingsAccordionsState = getItem(key) ?? {}; + const currentAccState = get(instanceHoldingsAccordionsState, [instanceId, ...pathToAccordion], false); + + const [isOpen, setIsOpen] = useState(currentAccState); + + useEffect(() => { + let newState = { + [instanceId]: { + ...cloneDeep(instanceHoldingsAccordionsState[instanceId]), + } + }; + + newState = set(newState, [instanceId, ...pathToAccordion], isOpen); + + setItem(key, newState); + }, [instanceHoldingsAccordionsState, isOpen, instanceId]); + + return [isOpen, setIsOpen]; +}; + +export default useHoldingsAccordionState; diff --git a/src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.test.js b/src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.test.js new file mode 100644 index 000000000..8e60b2ffa --- /dev/null +++ b/src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.test.js @@ -0,0 +1,43 @@ +import { + renderHook, + waitFor, + act, +} from '@folio/jest-config-stripes/testing-library/react'; + +import '../../../test/jest/__mock__'; + +import useHoldingsAccordionState from './useHoldingsAccordionState'; + +import { getItem } from '../../storage'; + +describe('useHoldingsAccordionState', () => { + it('should save initial holdings state in storage', () => { + const { result } = renderHook(() => useHoldingsAccordionState({ + instanceId: 'instanceId', + pathToAccordion: ['holdings', '_self'], + })); + + const expectedResult = { instanceId: { holdings: { _self: false } } }; + + expect(getItem('@folio/inventory.instanceHoldingsAccordionsState')).toEqual(expectedResult); + expect(result.current[0]).toEqual(false); + }); + + it('should set new state on accordion toggle', () => { + const { result } = renderHook(() => useHoldingsAccordionState({ + instanceId: 'instanceId', + pathToAccordion: ['holdings', '_self'], + })); + + const [isOpen, setOpen] = result.current; + + act(() => { + setOpen(true); + }); + + const expectedResult = { instanceId: { holdings: { _self: true } } }; + + waitFor(() => expect(isOpen).toBeTruthy()); + waitFor(() => expect(getItem('@folio/inventory.instanceHoldingsAccordionsState')).toEqual(expectedResult)); + }); +});