Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/UIIN-2453' into UIIN-2453
Browse files Browse the repository at this point in the history
  • Loading branch information
mariia-aloshyna committed Oct 16, 2023
2 parents 89c6c24 + a56930c commit 42b2bad
Show file tree
Hide file tree
Showing 13 changed files with 1,093 additions and 140 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Change history for ui-inventory

## 10.0.0 IN PROGRESS
## 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)

* Added a new option in the "Actions" dropdown within the Inventory app search page for "+New MARC Bib Record". Refs UIIN-2356.
* Change the url for the "New MARC Bib Record" page. Refs UIIN-2380.
* Avoid private paths in stripes-core imports. Refs UIIN-2367.
Expand Down Expand Up @@ -104,7 +110,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.
* Instance 3rd pane: Adjust behavior when returning to instance from holdings/item full screen. Refs UIIN-2453.
* 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)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@
"@folio/stripes-connect": "^9.0.0",
"@folio/stripes-core": "^10.0.0",
"@folio/stripes-smart-components": "^9.0.0",
"@folio/stripes-testing": "^3.1.0",
"@folio/stripes-testing": "^4.6.0",
"@folio/stripes-util": "^6.0.0",
"@formatjs/cli": "^6.1.3",
"core-js": "^3.6.4",
Expand Down
51 changes: 50 additions & 1 deletion src/ViewInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import {
flowRight,
isEmpty,
pick,
} from 'lodash';

import {
Expand Down Expand Up @@ -37,6 +38,7 @@ import {
isMARCSource,
} from './utils';
import {
AUTHORITY_LINKED_FIELDS,
indentifierTypeNames,
INSTANCE_SHARING_STATUSES,
layers,
Expand Down Expand Up @@ -179,6 +181,8 @@ class ViewInstance extends React.Component {
isImportRecordModalOpened: false,
isCopyrightModalOpened: false,
isShareLocalInstanceModalOpen: false,
isUnlinkAuthoritiesModalOpen: false,
linkedAuthoritiesLength: 0,
isNewOrderModalOpen: false,
afterCreate: false,
instancesQuickExportInProgress: false,
Expand Down Expand Up @@ -468,6 +472,7 @@ class ViewInstance extends React.Component {
})
.then(async () => {
this.setState({
isUnlinkAuthoritiesModalOpen: false,
isShareLocalInstanceModalOpen: false,
isLoading: true
});
Expand All @@ -483,14 +488,48 @@ class ViewInstance extends React.Component {
});
})
.catch(() => {
this.setState({ isShareLocalInstanceModalOpen: false });
this.setState({
isUnlinkAuthoritiesModalOpen: false,
isShareLocalInstanceModalOpen: false,
});
this.calloutRef.current?.sendCallout({
type: 'error',
message: <FormattedMessage id="ui-inventory.shareLocalInstance.toast.unsuccessful" values={{ instanceTitle }} />,
});
});
}

checkIfHasLinkedAuthorities = (instance = {}) => {
const linkedAuthorities = pick(this.props.selectedInstance, AUTHORITY_LINKED_FIELDS);

const findLinkedAuthorities = authorities => {
const linkedAuthoritiesLength = AUTHORITY_LINKED_FIELDS.reduce((total, field) => {
const authoritiesAmount = authorities[field].filter(item => item.authorityId).length;
return total + authoritiesAmount;
}, 0);

return {
hasLinkedAuthorities: !!linkedAuthoritiesLength,
linkedAuthoritiesLength,
};
};

const {
hasLinkedAuthorities,
linkedAuthoritiesLength,
} = findLinkedAuthorities(linkedAuthorities);

if (hasLinkedAuthorities) {
this.setState({
linkedAuthoritiesLength,
isShareLocalInstanceModalOpen: false,
isUnlinkAuthoritiesModalOpen: true,
});
} else {
this.handleShareLocalInstance(instance);
}
}

toggleCopyrightModal = () => {
this.setState(prevState => ({ isCopyrightModalOpened: !prevState.isCopyrightModalOpened }));
};
Expand Down Expand Up @@ -826,6 +865,7 @@ class ViewInstance extends React.Component {
isCentralTenantPermissionsLoading,
isShared,
} = this.props;
const { linkedAuthoritiesLength } = this.state;
const ci = makeConnectedInstance(this.props, stripes.logger);
const instance = ci.instance();

Expand Down Expand Up @@ -936,6 +976,15 @@ class ViewInstance extends React.Component {
message={<FormattedMessage id="ui-inventory.shareLocalInstance.modal.message" values={{ instanceTitle: instance?.title }} />}
confirmLabel={<FormattedMessage id="ui-inventory.shareLocalInstance.modal.confirmButton" />}
onCancel={() => this.setState({ isShareLocalInstanceModalOpen: false })}
onConfirm={() => this.checkIfHasLinkedAuthorities(instance)}
/>

<ConfirmationModal
open={this.state.isUnlinkAuthoritiesModalOpen}
heading={<FormattedMessage id="ui-inventory.unlinkLocalMarcAuthorities.modal.header" />}
message={<FormattedMessage id="ui-inventory.unlinkLocalMarcAuthorities.modal.message" values={{ linkedAuthoritiesLength }} />}
confirmLabel={<FormattedMessage id="ui-inventory.unlinkLocalMarcAuthorities.modal.proceed" />}
onCancel={() => this.setState({ isUnlinkAuthoritiesModalOpen: false })}
onConfirm={() => this.handleShareLocalInstance(instance)}
/>

Expand Down
68 changes: 53 additions & 15 deletions src/ViewInstance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -560,27 +560,65 @@ describe('ViewInstance', () => {
});

describe('when clicking Share local instance', () => {
it('should show confirmation modal', () => {
checkIfUserInMemberTenant.mockClear().mockReturnValue(true);

renderViewInstance();
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
fireEvent.click(screen.getByRole('button', { name: 'Share local instance' }));

expect(screen.getByText('Confirmation modal')).toBeInTheDocument();
});

describe('when confirming', () => {
it('should make POST request to share local instance', () => {
defaultProp.mutator.shareInstance.POST.mockResolvedValue({});
describe('and it has no linked MARC Authorities', () => {
it('should show confirmation modal', () => {
checkIfUserInMemberTenant.mockClear().mockReturnValue(true);

renderViewInstance();
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
fireEvent.click(screen.getByRole('button', { name: 'Share local instance' }));
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));

expect(defaultProp.mutator.shareInstance.POST).toHaveBeenCalled();
expect(screen.getByText('Confirmation modal')).toBeInTheDocument();
});
});

describe('when confirming', () => {
describe('and it has linked MARC Authorities', () => {
beforeEach(() => {
defaultProp.mutator.shareInstance.POST.mockResolvedValue({});
checkIfUserInMemberTenant.mockClear().mockReturnValue(true);

renderViewInstance({
selectedInstance: {
...instances[0],
alternativeTitles: [{
alternativeTitleTypeId: 'fe19bae4-da28-472b-be90-d442e2428ead',
alternativeTitle: 'Hashtag youthaction',
authorityId: 'testAuthorityId'
}],
}
});
});

it('should render unlink local MARC Authorities modal', () => {
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
fireEvent.click(screen.getByRole('button', { name: 'Share local instance' }));
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));

expect(screen.getByText('Confirmation modal')).toBeInTheDocument();
});

describe('when proceed sharing', () => {
it('should make POST request to share local instance', () => {
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
fireEvent.click(screen.getByRole('button', { name: 'Share local instance' }));
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));

expect(defaultProp.mutator.shareInstance.POST).toHaveBeenCalled();
});
});

describe('when cancel sharing', () => {
it('should hide confirmation modal', () => {
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
fireEvent.click(screen.getByRole('button', { name: 'Share local instance' }));
fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));

expect(screen.queryByText('Confirmation modal')).not.toBeInTheDocument();
});
});
});
});
});
Expand Down
49 changes: 37 additions & 12 deletions src/common/hooks/useFacets.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]);

Expand All @@ -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]);

Expand Down
Loading

0 comments on commit 42b2bad

Please sign in to comment.