Skip to content

Commit

Permalink
UIQM-652 eliminate page refresh when using save and keep ediing [WIP] (
Browse files Browse the repository at this point in the history
…#740)

* UIQM-652 eliminate page refresh when using save and keep ediing

* UIQM-652 added onInputFocus to prop types

* UIQM-652 update tests for updating related record version

* UIQM-652 fix issues
  • Loading branch information
BogdanDenis authored Oct 16, 2024
1 parent f0f44a1 commit cdd7b54
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 90 deletions.
6 changes: 3 additions & 3 deletions src/QuickMarcEditor/QuickMarcEditWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const QuickMarcEditWrapper = ({
const showCallout = useShowCallout();
const location = useLocation();
const [httpError, setHttpError] = useState(null);
const { validationErrorsRef } = useContext(QuickMarcContext);
const { validationErrorsRef, relatedRecordVersion } = useContext(QuickMarcContext);

const { token, locale } = stripes.okapi;
const isRequestToCentralTenantFromMember = applyCentralTenantInHeaders(location, stripes, marcType);
Expand Down Expand Up @@ -203,7 +203,7 @@ const QuickMarcEditWrapper = ({
formValuesToHydrate._actionType = 'edit';
formValuesToHydrate.relatedRecordVersion = marcType === MARC_TYPES.AUTHORITY
? instance._version
: new URLSearchParams(location.search).get('relatedRecordVersion');
: relatedRecordVersion;

const formValuesToSave = hydrateMarcRecord(formValuesToHydrate);

Expand Down Expand Up @@ -241,7 +241,6 @@ const QuickMarcEditWrapper = ({
marcType,
mutator,
linksCount,
location,
prepareForSubmit,
actualizeLinks,
centralTenantId,
Expand All @@ -250,6 +249,7 @@ const QuickMarcEditWrapper = ({
updateMarcRecord,
isRequestToCentralTenantFromMember,
validationErrorsRef,
relatedRecordVersion,
]);

return (
Expand Down
15 changes: 8 additions & 7 deletions src/QuickMarcEditor/QuickMarcEditWrapper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,10 @@ const renderQuickMarcEditWrapper = ({
instance,
mutator,
marcType = MARC_TYPES.BIB,
quickMarcContext,
...props
}) => (render(
<Harness>
<Harness quickMarcContext={quickMarcContext}>
<Form
onSubmit={jest.fn()}
mutators={arrayMutators}
Expand Down Expand Up @@ -442,9 +443,7 @@ describe('Given QuickMarcEditWrapper', () => {
isLoading: false,
});

useLocation.mockReturnValue({
search: 'relatedRecordVersion=1',
});
useLocation.mockReturnValue({});

useValidate.mockReturnValue({
validate: mockValidateFetch,
Expand Down Expand Up @@ -481,13 +480,16 @@ describe('Given QuickMarcEditWrapper', () => {
it('should show on save message and redirect on load page', async () => {
const mockOnSave = jest.fn();

const { getByText } = renderQuickMarcEditWrapper({
const { getByRole } = renderQuickMarcEditWrapper({
instance,
mutator,
onSave: mockOnSave,
quickMarcContext: {
relatedRecordVersion: 1,
},
});

await act(async () => { fireEvent.click(getByText('stripes-acq-components.FormFooter.save')); });
await act(async () => { fireEvent.click(getByRole('button', { name: 'stripes-acq-components.FormFooter.save' })); });

expect(mutator.quickMarcEditInstance.GET).toHaveBeenCalled();
expect(mockUpdateMarcRecord).toHaveBeenCalled();
Expand Down Expand Up @@ -620,7 +622,6 @@ describe('Given QuickMarcEditWrapper', () => {
'parsedRecordDtoId': '1bf159d9-4da8-4c3f-9aac-c83e68356bbf',
'parsedRecordId': '1bf159d9-4da8-4c3f-9aac-c83e68356bbf',
'records': undefined,
'relatedRecordVersion': '1',
'suppressDiscovery': false,
'updateInfo': {
'recordState': 'NEW',
Expand Down
29 changes: 11 additions & 18 deletions src/QuickMarcEditor/QuickMarcEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import React, {
useEffect,
useContext,
} from 'react';
import {
useHistory,
useLocation,
} from 'react-router';
import { useLocation } from 'react-router';
import { FormSpy } from 'react-final-form';
import PropTypes from 'prop-types';
import { FormattedMessage, useIntl } from 'react-intl';
Expand Down Expand Up @@ -119,7 +116,6 @@ const QuickMarcEditor = ({
const stripes = useStripes();
const intl = useIntl();
const formValues = getState().values;
const history = useHistory();
const location = useLocation();
const showCallout = useShowCallout();
const [records, setRecords] = useState([]);
Expand All @@ -131,8 +127,9 @@ const QuickMarcEditor = ({
const [isValidatedCurrentValues, setIsValidatedCurrentValues] = useState(false);
const continueAfterSave = useRef(false);
const formRef = useRef(null);
const lastFocusedInput = useRef(null);
const confirmationChecks = useRef({ ...REQUIRED_CONFIRMATIONS });
const { setValidationErrors } = useContext(QuickMarcContext);
const { setValidationErrors, setRelatedRecordVersion } = useContext(QuickMarcContext);
const { hasErrorIssues, isBackEndValidationMarcType } = useValidation();

const isConsortiaEnv = stripes.hasInterface('consortia');
Expand All @@ -155,16 +152,6 @@ const QuickMarcEditor = ({

const saveFormDisabled = submitting || pristine;

const redirectToVersion = useCallback((updatedVersion) => {
const searchParams = new URLSearchParams(location.search);

searchParams.set('relatedRecordVersion', updatedVersion);

history.replace({
search: searchParams.toString(),
});
}, [history, location.search]);

const handleSubmitResponse = useCallback((updatedRecord) => {
if (!updatedRecord?.version) {
continueAfterSave.current = false;
Expand All @@ -173,13 +160,14 @@ const QuickMarcEditor = ({
}

if (continueAfterSave.current) {
redirectToVersion(updatedRecord.version);
setRelatedRecordVersion(updatedRecord.version);
lastFocusedInput.current?.focus();

return;
}

onSave();
}, [redirectToVersion, onSave]);
}, [setRelatedRecordVersion, onSave]);

const closeModals = () => {
setIsDeleteModalOpened(false);
Expand Down Expand Up @@ -462,6 +450,10 @@ const QuickMarcEditor = ({
}
}, []);

const saveLastFocusedInput = useCallback((e) => {
lastFocusedInput.current = e.target;
}, [lastFocusedInput]);

const shortcuts = useMemo(() => ([{
name: 'save',
shortcut: 'mod+s',
Expand Down Expand Up @@ -568,6 +560,7 @@ const QuickMarcEditor = ({
linksCount={linksCount}
isLoadingLinkSuggestions={isLoadingLinkSuggestions}
onCheckCentralTenantPerm={onCheckCentralTenantPerm}
onInputFocus={saveLastFocusedInput}
/>
</Col>
</Row>
Expand Down
30 changes: 7 additions & 23 deletions src/QuickMarcEditor/QuickMarcEditorContainer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {
import {
useEffect,
useState,
useCallback,
Expand All @@ -9,17 +9,16 @@ import { withRouter } from 'react-router';
import ReactRouterPropTypes from 'react-router-prop-types';
import noop from 'lodash/noop';

import {
stripesConnect,
} from '@folio/stripes/core';

import { stripesConnect } from '@folio/stripes/core';
import { LoadingView } from '@folio/stripes/components';
import {
baseManifest,
useShowCallout,
} from '@folio/stripes-acq-components';
import { getHeaders } from '@folio/stripes-marc-components';

import { useAuthorityLinksCount } from '../queries';
import { QuickMarcProvider } from '../contexts';
import {
EXTERNAL_INSTANCE_APIS,
MARC_RECORD_API,
Expand All @@ -28,7 +27,6 @@ import {
LINKING_RULES_API,
MARC_SPEC_API,
} from '../common/constants';

import {
dehydrateMarcRecordResponse,
getCreateHoldingsMarcRecordResponse,
Expand All @@ -40,8 +38,6 @@ import {
applyCentralTenantInHeaders,
} from './utils';
import { QUICK_MARC_ACTIONS } from './constants';
import { useAuthorityLinksCount } from '../queries';
import { QuickMarcProvider } from '../contexts';

const propTypes = {
action: PropTypes.oneOf(Object.values(QUICK_MARC_ACTIONS)).isRequired,
Expand Down Expand Up @@ -89,17 +85,15 @@ const QuickMarcEditorContainer = ({
const [locations, setLocations] = useState();
const [isLoading, setIsLoading] = useState(true);
const [fixedFieldSpec, setFixedFieldSpec] = useState();
const showCallout = useShowCallout();
const { linksCount } = useAuthorityLinksCount({ id: marcType === MARC_TYPES.AUTHORITY && externalId });

const searchParams = new URLSearchParams(location.search);
const { token, locale } = stripes.okapi;
const centralTenantId = stripes.user.user.consortium?.centralTenantId;

const isRequestToCentralTenantFromMember = applyCentralTenantInHeaders(location, stripes, marcType)
&& action !== QUICK_MARC_ACTIONS.CREATE;

const showCallout = useShowCallout();
const { linksCount } = useAuthorityLinksCount({ id: marcType === MARC_TYPES.AUTHORITY && externalId });

const getCloseEditorParams = useCallback((id) => {
if (marcType === MARC_TYPES.HOLDINGS && action !== QUICK_MARC_ACTIONS.CREATE) {
return `${instanceId}/${externalId}`;
Expand All @@ -125,8 +119,6 @@ const QuickMarcEditorContainer = ({
}, [externalRecordPath, marcType, externalId, instanceId, action]);

const loadData = useCallback(async () => {
setIsLoading(true);

const path = action === QUICK_MARC_ACTIONS.CREATE && marcType === MARC_TYPES.HOLDINGS
? EXTERNAL_INSTANCE_APIS[MARC_TYPES.BIB]
: EXTERNAL_INSTANCE_APIS[marcType];
Expand Down Expand Up @@ -175,14 +167,6 @@ const QuickMarcEditorContainer = ({
linkingRulesResponse,
fixedFieldSpecResponse,
]) => {
if (action !== QUICK_MARC_ACTIONS.CREATE) {
searchParams.set('relatedRecordVersion', instanceResponse._version);

history.replace({
search: searchParams.toString(),
});
}

let dehydratedMarcRecord;

if (action === QUICK_MARC_ACTIONS.CREATE) {
Expand Down Expand Up @@ -231,7 +215,7 @@ const QuickMarcEditorContainer = ({
}

return (
<QuickMarcProvider>
<QuickMarcProvider relatedRecordVersion={instance?._version}>
<Wrapper
instance={instance}
onClose={handleClose}
Expand Down
37 changes: 0 additions & 37 deletions src/QuickMarcEditor/QuickMarcEditorContainer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,43 +226,6 @@ describe('Given Quick Marc Editor Container', () => {
expect(getByText(instance.title)).toBeDefined();
});

describe('when the action is not CREATE', () => {
it('should append the relatedRecordVersion parameter to URL', async () => {
const spyHistory = jest.spyOn(mockHistory, 'replace');

await act(async () => {
await renderQuickMarcEditorContainer({
mutator,
onClose: jest.fn(),
action: QUICK_MARC_ACTIONS.EDIT,
wrapper: QuickMarcEditWrapper,
});
});

expect(spyHistory).toHaveBeenCalledWith({ search: expect.stringContaining('relatedRecordVersion=1') });
});
});

describe('when the action is CREATE', () => {
it('should not append the relatedRecordVersion parameter to URL', async () => {
const history = createMemoryHistory();

history.replace = jest.fn();

await act(async () => {
await renderQuickMarcEditorContainer({
mutator,
onClose: jest.fn(),
action: QUICK_MARC_ACTIONS.CREATE,
wrapper: QuickMarcEditWrapper,
history,
});
});

expect(history.replace).not.toHaveBeenCalled();
});
});

describe('Leader field', () => {
describe('when the action is CREATE a bib record', () => {
let recordLengthField;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const QuickMarcEditorRows = ({
linksCount,
isLoadingLinkSuggestions,
onCheckCentralTenantPerm,
onInputFocus,
}) => {
const location = useLocation();
const stripes = useStripes();
Expand Down Expand Up @@ -266,6 +267,7 @@ const QuickMarcEditorRows = ({
id="quick-marc-editor-rows"
data-testid="quick-marc-editor-rows"
ref={containerRef}
onFocus={onInputFocus}
>
<FieldArray
name="records"
Expand Down Expand Up @@ -660,6 +662,7 @@ QuickMarcEditorRows.propTypes = {
marcType: PropTypes.oneOf(Object.values(MARC_TYPES)).isRequired,
fixedFieldSpec: PropTypes.object.isRequired,
onCheckCentralTenantPerm: PropTypes.func,
onInputFocus: PropTypes.func.isRequired,
};

QuickMarcEditorRows.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ const getComponent = (props) => (
}}
subtype="m"
isLoadingLinkSuggestions={false}
onInputFocus={jest.fn()}
{...props}
/>
)}
Expand Down
7 changes: 7 additions & 0 deletions src/contexts/QuickMarcContext/QuickMarcContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ const propTypes = {
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
relatedRecordVersion: PropTypes.number,
};

const QuickMarcProvider = ({
children,
relatedRecordVersion,
}) => {
const [selectedSourceFile, setSelectedSourceFile] = useState(null);
const [_relatedRecordVersion, setRelatedRecordVersion] = useState(relatedRecordVersion);
const validationErrors = useRef({});

const setValidationErrors = useCallback((newValidationErrors) => {
Expand All @@ -31,11 +34,15 @@ const QuickMarcProvider = ({
setSelectedSourceFile,
validationErrorsRef: validationErrors,
setValidationErrors,
relatedRecordVersion: _relatedRecordVersion,
setRelatedRecordVersion,
}), [
selectedSourceFile,
setSelectedSourceFile,
validationErrors,
setValidationErrors,
_relatedRecordVersion,
setRelatedRecordVersion,
]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const useFocusFirstFieldWithError = () => {
return;
}

document.querySelector(`[data-fieldid="${firstFieldWithErrors.id}"] input:enabled`)?.focus();
const fieldSelector = `[data-fieldid="${firstFieldWithErrors.id}"]`;

document.querySelector(`${fieldSelector} input:enabled, ${fieldSelector} textarea:enabled`)?.focus();
}, [firstFieldWithErrors?.id, validationErrorsRef]);
};
7 changes: 6 additions & 1 deletion test/jest/helpers/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ const defaultHistory = createMemoryHistory();

const queryClient = new QueryClient();

const defaultQuickMarcContextValue = {
validationErrorsRef: { current: {} },
setValidationErrors: jest.fn(),
};

const QuickMarcProviderMock = ({ ctxValue, children }) => (
<QuickMarcContext.Provider value={ctxValue}>
<QuickMarcContext.Provider value={{ ...defaultQuickMarcContextValue, ...ctxValue }}>
{children}
</QuickMarcContext.Provider>
);
Expand Down

0 comments on commit cdd7b54

Please sign in to comment.