Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIIN-2896 Inventory app: Define and implement shortcut key for editing a quickMARC bib record #2479

Merged
merged 7 commits into from
May 20, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Use consolidated locations endpoint to fetch all locations when in central tenant context. Refs UIIN-2811.
* Change label of eye-readable call number search option in holdings/items. Refs UIIN-2797.
* Jest/RTL: Cover ModalContent components with unit tests. Refs UIIN-2669.
* Inventory app: Define and implement shortcut key for editing a quickMARC bib record. Refs UIIN-2896.

## [11.0.4](https://github.com/folio-org/ui-inventory/tree/v11.0.4) (2024-04-30)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v11.0.3...v11.0.4)
Expand Down
10 changes: 10 additions & 0 deletions src/ViewHoldingsRecord.js
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,16 @@ class ViewHoldingsRecord extends React.Component {
if (stripes.hasPerm('ui-inventory.holdings.edit')) this.onEditHolding();
}),
},
{
name: 'editMARC',
handler: handleKeyCommand(() => {
if (!stripes.hasPerm('ui-quick-marc.quick-marc-editor.all') || !this.isMARCSource()) {
return;
}

this.handleEditInQuickMarc();
}),
},
{
name: 'expandAllSections',
handler: (e) => expandAllSections(e, this.accordionStatusRef),
Expand Down
20 changes: 18 additions & 2 deletions src/ViewHoldingsRecord.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ const spyOncollapseAllSections = jest.spyOn(require('@folio/stripes/components')
const spyOnexpandAllSections = jest.spyOn(require('@folio/stripes/components'), 'expandAllSections');

const mockData = jest.fn().mockResolvedValue({ id: 'testId' });
const mockGoTo = jest.fn();

const defaultProps = {
id: 'id',
goTo: jest.fn(),
goTo: mockGoTo,
holdingsrecordid: 'holdingId',
referenceTables: {
holdingsSources: [{ id: 'sourceId', name: 'MARC' }],
Expand All @@ -57,7 +58,12 @@ const defaultProps = {
resources: {
holdingsRecords: {
records: [
{ sourceId: 'sourceId', temporaryLocationId: 'inactiveLocation' }
{
sourceId: 'sourceId',
temporaryLocationId: 'inactiveLocation',
id: 'holdingId',
_version: 1,
}
],
},
instances1: { records: [{ id: 'instanceId' }], hasLoaded: true },
Expand Down Expand Up @@ -270,4 +276,14 @@ describe('ViewHoldingsRecord actions', () => {
expect(spyOnexpandAllSections).toHaveBeenCalled();
});
});

describe('when using an editMARC shortcut', () => {
it('should redirect to marc edit page', async () => {
await act(async () => { renderViewHoldingsRecord(); });

fireEvent.click(screen.getByRole('button', { name: 'editMARC' }));

expect(mockGoTo).toHaveBeenLastCalledWith(`/inventory/quick-marc/edit-holdings/instanceId/${defaultProps.holdingsrecordid}?%2F=&relatedRecordVersion=1`);
});
});
});
23 changes: 13 additions & 10 deletions src/ViewInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
isMARCSource,
getLinkedAuthorityIds,
setRecordForDeletion,
redirectToMarcEditPage,
} from './utils';
import {
CONSORTIUM_PREFIX,
Expand Down Expand Up @@ -339,21 +340,12 @@ class ViewInstance extends React.Component {
history,
location,
stripes,
isShared,
} = this.props;

const ci = makeConnectedInstance(this.props, stripes.logger);
const instance = ci.instance();

const searchParams = new URLSearchParams(location.search);

searchParams.delete('relatedRecordVersion');
searchParams.append('shared', isShared.toString());

history.push({
pathname: `/inventory/quick-marc/${page}/${instance.id}`,
search: searchParams.toString(),
});
redirectToMarcEditPage(`/inventory/quick-marc/${page}/${instance.id}`, instance, location, history);
};

editInstanceMarc = () => {
Expand Down Expand Up @@ -1042,6 +1034,7 @@ class ViewInstance extends React.Component {
onCopy,
updateLocation,
canUseSingleRecordImport,
selectedInstance,
} = this.props;
const {
linkedAuthoritiesLength,
Expand All @@ -1066,6 +1059,16 @@ class ViewInstance extends React.Component {
if (stripes.hasPerm('ui-inventory.instance.edit')) this.onClickEditInstance();
}),
},
{
name: 'editMARC',
handler: handleKeyCommand(() => {
if (!stripes.hasPerm('ui-quick-marc.quick-marc-editor.all') || !isMARCSource(selectedInstance.source)) {
return;
}

this.editInstanceMarc();
}),
},
{
name: 'duplicateRecord',
handler: handleKeyCommand(() => {
Expand Down
24 changes: 22 additions & 2 deletions src/ViewInstance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ describe('ViewInstance', () => {
renderViewInstance();
const expectedValue = {
pathname: `/inventory/quick-marc/edit-bib/${defaultProp.selectedInstance.id}`,
search: 'filters=test1&query=test2&sort=test3&qindex=test&shared=false',
search: 'filters=test1&query=test2&sort=test3&qindex=test',
};
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
const button = screen.getByRole('button', { name: 'Edit MARC bibliographic record' });
Expand Down Expand Up @@ -929,7 +929,7 @@ describe('ViewInstance', () => {
renderViewInstance({ isShared: false });
const expectedValue = {
pathname: `/inventory/quick-marc/duplicate-bib/${defaultProp.selectedInstance.id}`,
search: 'filters=test1&query=test2&sort=test3&qindex=test&shared=false',
search: 'filters=test1&query=test2&sort=test3&qindex=test',
};
fireEvent.click(screen.getByRole('button', { name: 'Actions' }));
const button = screen.getByRole('button', { name: 'Derive new MARC bibliographic record' });
Expand Down Expand Up @@ -1162,4 +1162,24 @@ describe('ViewInstance', () => {
expect(spyOnexpandAllSections).toBeCalled();
});
});

describe('when using an editMARC shortcut', () => {
it('should redirect to marc edit page', () => {
const selectedInstance = {
...instances[0],
shared: true,
};

StripesConnectedInstance.prototype.instance.mockImplementation(() => selectedInstance);

renderViewInstance({ selectedInstance });

fireEvent.click(screen.getByRole('button', { name: 'editMARC' }));

expect(mockPush).toHaveBeenLastCalledWith({
pathname: `/inventory/quick-marc/edit-bib/${instance.id}`,
search: 'filters=test1&query=test2&sort=test3&qindex=test&shared=true',
});
});
});
});
86 changes: 61 additions & 25 deletions src/components/ViewSource/ViewSource.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import React, {
useState,
useEffect,
useMemo,
useCallback,
} from 'react';
import {
useLocation,
useHistory,
} from 'react-router-dom';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';

import {
Button,
LoadingView,
HasCommand,
checkScope,
} from '@folio/stripes/components';
import { useStripes } from '@folio/stripes/core';
import {
Expand All @@ -18,7 +26,11 @@ import {

import { useGoBack } from '../../common/hooks';

import { isUserInConsortiumMode } from '../../utils';
import {
isUserInConsortiumMode,
handleKeyCommand,
redirectToMarcEditPage,
} from '../../utils';
import MARC_TYPES from './marcTypes';

import styles from './ViewSource.css';
Expand All @@ -33,6 +45,8 @@ const ViewSource = ({
marcType,
}) => {
const stripes = useStripes();
const location = useLocation();
const history = useHistory();
const [isShownPrintPopup, setIsShownPrintPopup] = useState(false);
const openPrintPopup = () => setIsShownPrintPopup(true);
const closePrintPopup = () => setIsShownPrintPopup(false);
Expand All @@ -51,6 +65,22 @@ const ViewSource = ({
const [marc, setMarc] = useState();
const [isMarcLoading, setIsMarcLoading] = useState(true);

const redirectToMARCEdit = useCallback(() => {
const urlId = isHoldingsRecord ? `${instanceId}/${holdingsRecordId}` : instanceId;
const pathname = `/inventory/quick-marc/edit-${isHoldingsRecord ? 'holdings' : 'bib'}/${urlId}`;

redirectToMarcEditPage(pathname, instance, location, history);
}, [isHoldingsRecord]);

const shortcuts = useMemo(() => [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a second argument to useMemo.

{
name: 'editMARC',
handler: handleKeyCommand(() => {
if (stripes.hasPerm('ui-quick-marc.quick-marc-editor.all')) redirectToMARCEdit();
}),
},
], [stripes, redirectToMARCEdit]);

useEffect(() => {
setIsMarcLoading(true);

Expand Down Expand Up @@ -94,32 +124,38 @@ const ViewSource = ({
);

return (
<div className={styles.viewSource}>
<MarcView
paneTitle={paneTitle}
marcTitle={marcTitle}
marc={marc}
onClose={goBack}
lastMenu={
isPrintAvailable &&
<Button
marginBottom0
buttonStyle="primary"
onClick={openPrintPopup}
>
<FormattedMessage id="ui-quick-marc.print" />
</Button>
}
/>
{isPrintAvailable && isShownPrintPopup && (
<PrintPopup
marc={marc}
paneTitle={instance.title}
<HasCommand
commands={shortcuts}
isWithinScope={checkScope}
scope={document.body}
>
<div className={styles.viewSource}>
<MarcView
paneTitle={paneTitle}
marcTitle={marcTitle}
onAfterPrint={closePrintPopup}
marc={marc}
onClose={goBack}
lastMenu={
isPrintAvailable &&
<Button
marginBottom0
buttonStyle="primary"
onClick={openPrintPopup}
>
<FormattedMessage id="ui-quick-marc.print" />
</Button>
}
/>
)}
</div>
{isPrintAvailable && isShownPrintPopup && (
<PrintPopup
marc={marc}
paneTitle={instance.title}
marcTitle={marcTitle}
onAfterPrint={closePrintPopup}
/>
)}
</div>
</HasCommand>
);
};

Expand Down
32 changes: 31 additions & 1 deletion src/components/ViewSource/ViewSource.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import {
BrowserRouter as Router,
useHistory,
} from 'react-router-dom';
import { act } from 'react-dom/test-utils';

import {
Expand All @@ -18,6 +21,12 @@ import { CONSORTIUM_PREFIX } from '../../constants';
import MARC_TYPES from './marcTypes';

jest.mock('../../common/hooks/useGoBack', () => jest.fn());
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: jest.fn().mockReturnValue({
push: jest.fn(),
}),
}));

const mutator = {
marcRecord: {
Expand All @@ -26,6 +35,7 @@ const mutator = {
};

const mockGoBack = jest.fn();
const mockPush = jest.fn();
const mockInstance = {
id: 'instance-id',
title: 'Instance title',
Expand All @@ -49,6 +59,9 @@ const getViewSource = (props = {}) => (

describe('ViewSource', () => {
beforeEach(() => {
useHistory.mockClear().mockReturnValue({
push: mockPush,
});
useGoBack.mockReturnValue(mockGoBack);
});

Expand Down Expand Up @@ -153,4 +166,21 @@ describe('ViewSource', () => {
expect(screen.getByText('Local MARC bibliographic record')).toBeInTheDocument();
});
});

describe('when using an editMARC shortcut', () => {
beforeEach(async () => {
await act(async () => {
await renderWithIntl(getViewSource(), translations);
});
});

it('should redirect to marc edit page', () => {
fireEvent.click(screen.getByRole('button', { name: 'editMARC' }));

expect(mockPush).toHaveBeenLastCalledWith({
pathname: `/inventory/quick-marc/edit-bib/${mockInstance.id}`,
search: '',
});
});
});
});