From b7c19d1bd14c5306ee40cc3f62cadf8b12910f8f Mon Sep 17 00:00:00 2001 From: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:11:15 +0300 Subject: [PATCH] UIIN-2453: Instance 3rd pane: Adjust behaviour when returning to instance from holdings/item full screen (#2305) --- CHANGELOG.md | 4 ++ src/Instance/HoldingsList/Holding/Holding.js | 7 +++ .../HoldingsList/Holding/Holding.test.js | 2 + .../HoldingsList/Holding/HoldingAccordion.js | 14 +++++- .../Holding/HoldingAccordion.test.js | 2 + .../HoldingsList/Holding/HoldingContainer.js | 16 +++++++ .../Holding/HoldingContainer.test.js | 1 + src/Instance/HoldingsList/HoldingsList.js | 4 ++ .../HoldingsList/HoldingsListContainer.js | 10 +++-- .../ConsortialHoldings/ConsortialHoldings.js | 31 ++++++++++--- .../MemberTenantHoldings.js | 11 ++++- .../HoldingsListMovement.js | 4 ++ src/ViewInstance.js | 2 + src/hooks/index.js | 1 + src/hooks/useHoldingsAccordionState/index.js | 1 + .../useHoldingsAccordionState.js | 42 ++++++++++++++++++ .../useHoldingsAccordionState.test.js | 43 +++++++++++++++++++ 17 files changed, 184 insertions(+), 11 deletions(-) create mode 100644 src/hooks/useHoldingsAccordionState/index.js create mode 100644 src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.js create mode 100644 src/hooks/useHoldingsAccordionState/useHoldingsAccordionState.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 047214f03..af713044d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change history for ui-inventory +## 10.0.1 IN PROGRESS + +* Instance 3rd pane: Adjust behavior when returning to instance from holdings/item full screen. Refs UIIN-2453. + ## [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/Instance/HoldingsList/Holding/Holding.js b/src/Instance/HoldingsList/Holding/Holding.js index b04382bb0..aea2e7d98 100644 --- a/src/Instance/HoldingsList/Holding/Holding.js +++ b/src/Instance/HoldingsList/Holding/Holding.js @@ -20,6 +20,8 @@ const Holding = ({ isDraggable, isItemsDroppable, tenantId, + instanceId, + pathToAccordionsState, }) => { return (
@@ -49,6 +51,8 @@ const Holding = ({ onViewHolding={onViewHolding} onAddItem={onAddItem} tenantId={tenantId} + 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 8fb96e77b..02b4c2ff4 100644 --- a/src/Instance/HoldingsList/Holding/HoldingAccordion.js +++ b/src/Instance/HoldingsList/Holding/HoldingAccordion.js @@ -13,7 +13,10 @@ import { import { callNumberLabel } from '../../../utils'; import HoldingButtonsGroup from './HoldingButtonsGroup'; import useHoldingItemsQuery from '../../../hooks/useHoldingItemsQuery'; -import { useLocationsQuery } from '../../../hooks'; +import { + useHoldingsAccordionState, + useLocationsQuery, +} from '../../../hooks'; const HoldingAccordion = ({ children, @@ -23,13 +26,16 @@ const HoldingAccordion = ({ onAddItem, withMoveDropdown, tenantId, + 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 }); @@ -113,10 +119,14 @@ 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, + 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 45df04f4c..b8aa058f9 100644 --- a/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js +++ b/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js @@ -37,6 +37,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 93f77f883..9a907bd49 100644 --- a/src/Instance/HoldingsList/Holding/HoldingContainer.js +++ b/src/Instance/HoldingsList/Holding/HoldingContainer.js @@ -36,6 +36,8 @@ const DraggableHolding = ({ onViewHolding, onAddItem, tenantId, + instanceId, + pathToAccordionsState, ...rest }) => { const rowStyles = useMemo(() => ( @@ -68,6 +70,8 @@ const DraggableHolding = ({ onViewHolding={onViewHolding} onAddItem={onAddItem} tenantId={tenantId} + instanceId={instanceId} + pathToAccordionsState={pathToAccordionsState} /> ) } @@ -83,12 +87,16 @@ 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, tenantId: PropTypes.string, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; +DraggableHolding.defaultProps = { pathToAccordionsState: [] }; + const HoldingContainer = ({ location, history, @@ -99,6 +107,7 @@ const HoldingContainer = ({ holdingIndex, draggingHoldingsCount, tenantId, + pathToAccordionsState, ...rest }) => { const onViewHolding = useCallback(() => { @@ -130,6 +139,8 @@ const HoldingContainer = ({ onViewHolding={onViewHolding} onAddItem={onAddItem} tenantId={tenantId} + instanceId={instance?.id} + pathToAccordionsState={pathToAccordionsState} {...rest} /> )} @@ -141,6 +152,8 @@ const HoldingContainer = ({ onViewHolding={onViewHolding} onAddItem={onAddItem} tenantId={tenantId} + instanceId={instance?.id} + pathToAccordionsState={pathToAccordionsState} /> ); }; @@ -157,6 +170,9 @@ HoldingContainer.propTypes = { isDraggable: PropTypes.bool, draggingHoldingsCount: PropTypes.number, tenantId: PropTypes.string, + 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 97c01fff5..b378b7d17 100644 --- a/src/Instance/HoldingsList/HoldingsList.js +++ b/src/Instance/HoldingsList/HoldingsList.js @@ -7,6 +7,7 @@ const HoldingsList = ({ instance, holdings, tenantId, + pathToAccordionsState, draggable, droppable, @@ -19,6 +20,7 @@ const HoldingsList = ({ droppable={droppable} holdings={holdings} tenantId={tenantId} + pathToAccordionsState={pathToAccordionsState} /> )); @@ -26,6 +28,7 @@ HoldingsList.propTypes = { instance: PropTypes.object.isRequired, holdings: PropTypes.arrayOf(PropTypes.object), tenantId: PropTypes.string, + pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), draggable: PropTypes.bool, droppable: PropTypes.bool, @@ -33,6 +36,7 @@ HoldingsList.propTypes = { HoldingsList.defaultProps = { holdings: [], + pathToAccordionsState: [], }; export default HoldingsList; diff --git a/src/Instance/HoldingsList/HoldingsListContainer.js b/src/Instance/HoldingsList/HoldingsListContainer.js index be19ea8ee..343284e36 100644 --- a/src/Instance/HoldingsList/HoldingsListContainer.js +++ b/src/Instance/HoldingsList/HoldingsListContainer.js @@ -1,9 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - Loading, -} from '@folio/stripes/components'; +import { Loading } from '@folio/stripes/components'; import HoldingsList from './HoldingsList'; import { HoldingsListMovement } from '../InstanceMovement/HoldingMovementList'; @@ -13,6 +11,7 @@ const HoldingsListContainer = ({ instance, isHoldingsMove, tenantId, + pathToAccordionsState, ...rest }) => { const { holdingsRecords: holdings, isLoading } = useInstanceHoldingsQuery(instance.id, { tenantId }); @@ -26,6 +25,7 @@ const HoldingsListContainer = ({ holdings={holdings} instance={instance} tenantId={tenantId} + pathToAccordionsState={pathToAccordionsState} /> ) : ( ) ); @@ -42,6 +43,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 d3623b753..aae864206 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,25 +17,42 @@ import { import { MemberTenantHoldings } from '../MemberTenantHoldings'; import { DataContext } from '../../../contexts'; -import { useSearchForShadowInstanceTenants } from '../../../hooks'; +import { + useHoldingsAccordionState, + useSearchForShadowInstanceTenants, +} from '../../../hooks'; const ConsortialHoldings = ({ instance }) => { + const instanceId = instance?.id; + const prevInstanceId = useRef(instanceId); + const stripes = useStripes(); 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 ? @@ -39,7 +60,7 @@ const ConsortialHoldings = ({ instance }) => { {memberTenants.map(memberTenant => ( diff --git a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js index 0ada3eff2..47fc6aa2f 100644 --- a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js +++ b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js @@ -16,6 +16,7 @@ import { InstanceNewHolding } from '../InstanceNewHolding'; import { MoveItemsContext } from '../../MoveItemsContext'; import { useInstanceHoldingsQuery } from '../../../providers'; +import { useHoldingsAccordionState } from '../../../hooks'; import css from './MemberTenantHoldings.css'; @@ -27,7 +28,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); @@ -38,7 +45,8 @@ const MemberTenantHoldings = ({ className={css.memberTenantHoldings} id={`${name}-holdings`} label={name} - closedByDefault + open={isMemberTenantAccOpen} + onToggle={() => setMemberTenantAccOpen(prevValue => !prevValue)} >
{isLoading @@ -51,6 +59,7 @@ const MemberTenantHoldings = ({ tenantId={id} draggable={false} droppable={false} + pathToAccordionsState={pathToHoldingsAccordion} /> )} diff --git a/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js b/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js index 5c0788920..825a8b514 100644 --- a/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js +++ b/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js @@ -23,6 +23,7 @@ const HoldingsListMovement = ({ draggable, droppable, tenantId, + pathToAccordionsState, }) => { const { selectItemsForDrag, @@ -58,6 +59,7 @@ const HoldingsListMovement = ({ holdingIndex={index} draggingHoldingsCount={draggingHoldingsCount} tenantId={tenantId} + pathToAccordionsState={pathToAccordionsState} /> )) ) : ( @@ -80,10 +82,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 67946a013..f67977944 100644 --- a/src/ViewInstance.js +++ b/src/ViewInstance.js @@ -900,6 +900,7 @@ class ViewInstance extends React.Component { }, ]; const isInstanceLoading = this.state.isLoading || !instance || isCentralTenantPermissionsLoading; + const keyInStorageToHoldingsAccsState = ['holdings']; return ( @@ -927,6 +928,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)); + }); +});