Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* *BREAKING* Migration from mod-config and mod-settings to mod-circulation. Refs UICIRC-1247.
* Update permissions after migration to mod-circulation. Refs UICIRC-1324.
* Fixed an issue where the value containing the previously selected option was not reset. Refs UICIRC-1317.
* Add a new field in "Other settings" to control the display of custom fields in Checkout app. Refs UICIRC-1091.

## [11.0.4](https://github.com/folio-org/ui-circulation/tree/v11.0.4) (2024-12-10)
[Full Changelog](https://github.com/folio-org/ui-circulation/compare/v11.0.3...v11.0.4)
Expand Down
3 changes: 3 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -657,3 +657,6 @@ export const TOKEN_PROP_TYPES = PropTypes.shape({
previewValue: PropTypes.string,
})),
});

export const USERS_MODULE = 'users';
export const CUSTOM_FIELDS_ENTITY_TYPE = 'user';
41 changes: 39 additions & 2 deletions src/settings/CheckoutSettings/CheckoutSettings.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import {
useCallback,
useMemo,
} from 'react';
import {
useIntl,
} from 'react-intl';

import {
TitleManager,
} from '@folio/stripes/core';
import { useCustomFieldsQuery } from '@folio/stripes/smart-components';

import {
CirculationSettingsConfig,
} from '../components';
import CheckoutSettingsForm from './CheckoutSettingsForm';
import {
CONFIG_NAMES,
USERS_MODULE,
CUSTOM_FIELDS_ENTITY_TYPE,
} from '../../constants';

export const DEFAULT_INITIAL_CONFIG = {
Expand All @@ -22,9 +29,10 @@ export const DEFAULT_INITIAL_CONFIG = {
prefPatronIdentifier: '',
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: false,
allowedCustomFieldRefIds: [],
};

export const getInitialValues = (settings) => {
export const getInitialValues = (settings, customFieldsOptions) => {
const config = {
...DEFAULT_INITIAL_CONFIG,
...settings,
Expand All @@ -45,6 +53,10 @@ export const getInitialValues = (settings) => {
}
});

const allowedCustomFieldRefIds = config.allowedCustomFieldRefIds.map(refId => {
return customFieldsOptions.find(customField => customField.value === refId) || { value: refId, label: refId };
});

return {
audioAlertsEnabled: config.audioAlertsEnabled,
audioTheme: config.audioTheme,
Expand All @@ -53,6 +65,7 @@ export const getInitialValues = (settings) => {
identifiers,
useCustomFieldsAsIdentifiers: config.useCustomFieldsAsIdentifiers,
wildcardLookupEnabled: config.wildcardLookupEnabled,
allowedCustomFieldRefIds,
};
};

Expand All @@ -64,6 +77,7 @@ export const normalize = ({
identifiers,
useCustomFieldsAsIdentifiers,
wildcardLookupEnabled,
allowedCustomFieldRefIds,
}) => {
// As in `getInitialValues`, we must assume knowledge of how the IDs and Custom Field IDs
// are rendered in CheckoutSettingsForm. IDs can be toggled on and off by a checkbox,
Expand Down Expand Up @@ -92,6 +106,7 @@ export const normalize = ({
prefPatronIdentifier,
useCustomFieldsAsIdentifiers,
wildcardLookupEnabled,
allowedCustomFieldRefIds: allowedCustomFieldRefIds.map(customField => customField.value),
};
};

Expand All @@ -100,6 +115,26 @@ const CheckoutSettings = () => {
formatMessage,
} = useIntl();

const {
customFields,
isLoadingCustomFields,
} = useCustomFieldsQuery({
moduleName: USERS_MODULE,
entityType: CUSTOM_FIELDS_ENTITY_TYPE,
isVisible: true,
});

const customFieldsOptions = useMemo(() => {
return (customFields || []).map(({ name, refId }) => ({
label: name,
value: refId,
}));
}, [customFields]);

const getOriginalValues = useCallback((settings) => {
return getInitialValues(settings, customFieldsOptions);
}, [customFieldsOptions]);

return (
<TitleManager
page={formatMessage({ id: 'ui-circulation.settings.title.general' })}
Expand All @@ -108,8 +143,10 @@ const CheckoutSettings = () => {
<CirculationSettingsConfig
label={formatMessage({ id: 'ui-circulation.settings.index.otherSettings' })}
configName={CONFIG_NAMES.OTHER_SETTINGS}
getInitialValues={getInitialValues}
getInitialValues={getOriginalValues}
configFormComponent={CheckoutSettingsForm}
customFieldsOptions={customFieldsOptions}
isLoadingCustomFields={isLoadingCustomFields}
onBeforeSave={normalize}
/>
</TitleManager>
Expand Down
62 changes: 60 additions & 2 deletions src/settings/CheckoutSettings/CheckoutSettings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import {
import {
TitleManager,
} from '@folio/stripes/core';
import {
useCustomFieldsQuery,
} from '@folio/stripes/smart-components';

import CheckoutSettings, {
getInitialValues,
Expand Down Expand Up @@ -34,10 +37,29 @@ const labelIds = {
paneTitle: 'ui-circulation.settings.index.otherSettings',
};

const customFieldsOptions = [{
label: 'Custom Field 1',
value: 'refId1'
}, {
label: 'Custom Field 2',
value: 'refId2'
}];

describe('CheckoutSettings', () => {
beforeEach(() => {
jest.clearAllMocks();

useCustomFieldsQuery.mockReturnValue({
customFields: [{
name: 'Custom Field 1',
refId: 'refId1',
}, {
name: 'Custom Field 2',
refId: 'refId2',
}],
isLoadingCustomFields: false,
});

render(<CheckoutSettings />);
});

Expand All @@ -61,8 +83,10 @@ describe('CheckoutSettings', () => {
label: labelIds.paneTitle,
configName: CONFIG_NAMES.OTHER_SETTINGS,
configFormComponent: CheckoutSettingsForm,
getInitialValues,
getInitialValues: expect.any(Function),
onBeforeSave: normalize,
customFieldsOptions,
isLoadingCustomFields: false,
}),
{}
);
Expand All @@ -79,6 +103,7 @@ describe('getInitialValues', () => {
identifiers: { custom: [] },
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: false,
allowedCustomFieldRefIds: [],
});

expect(getInitialValues({})).toEqual({
Expand All @@ -89,6 +114,7 @@ describe('getInitialValues', () => {
identifiers: { custom: [] },
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: false,
allowedCustomFieldRefIds: [],
});
});

Expand All @@ -101,9 +127,10 @@ describe('getInitialValues', () => {
prefPatronIdentifier: 'barcode,customFields.cf1',
useCustomFieldsAsIdentifiers: true,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: ['refId1', 'refId2'],
};

expect(getInitialValues(custom)).toEqual({
expect(getInitialValues(custom, customFieldsOptions)).toEqual({
audioAlertsEnabled: true,
audioTheme: 'modern',
checkoutTimeout: false,
Expand All @@ -114,10 +141,30 @@ describe('getInitialValues', () => {
},
useCustomFieldsAsIdentifiers: true,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: [{
label: 'Custom Field 1',
value: 'refId1',
}, {
label: 'Custom Field 2',
value: 'refId2',
}],
});
});
});

it('should handle missing custom fields in allowedCustomFieldRefIds', () => {
const custom = {
allowedCustomFieldRefIds: ['refId1', 'refId3'],
};

expect(getInitialValues(custom, customFieldsOptions).allowedCustomFieldRefIds).toEqual([{
label: 'Custom Field 1',
value: 'refId1',
}, {
value: 'refId3',
label: 'refId3',
}]);
});
describe('normalize', () => {
it('should normalizes identifiers to prefPatronIdentifier string', () => {
const input = {
Expand All @@ -132,6 +179,10 @@ describe('normalize', () => {
},
useCustomFieldsAsIdentifiers: true,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: [{
label: 'Custom Field 1',
value: 'refId1',
}],
};

expect(normalize(input)).toEqual({
Expand All @@ -142,6 +193,7 @@ describe('normalize', () => {
prefPatronIdentifier: 'barcode,username,customFields.cf1,customFields.cf2',
useCustomFieldsAsIdentifiers: true,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: ['refId1'],
});
});

Expand All @@ -154,6 +206,7 @@ describe('normalize', () => {
identifiers: { custom: [] },
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: false,
allowedCustomFieldRefIds: [],
};

expect(normalize(input)).toEqual({
Expand All @@ -164,6 +217,7 @@ describe('normalize', () => {
prefPatronIdentifier: '',
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: false,
allowedCustomFieldRefIds: [],
});
});

Expand All @@ -179,6 +233,7 @@ describe('normalize', () => {
},
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: [],
};

expect(normalize(input)).toEqual({
Expand All @@ -189,6 +244,7 @@ describe('normalize', () => {
prefPatronIdentifier: 'barcode',
useCustomFieldsAsIdentifiers: false,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: [],
});
});

Expand All @@ -204,6 +260,7 @@ describe('normalize', () => {
},
useCustomFieldsAsIdentifiers: true,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: [],
};

expect(normalize(input)).toEqual({
Expand All @@ -214,6 +271,7 @@ describe('normalize', () => {
prefPatronIdentifier: 'barcode,customFields.cf1,customFields.cf2',
useCustomFieldsAsIdentifiers: true,
wildcardLookupEnabled: true,
allowedCustomFieldRefIds: [],
});
});
});
17 changes: 17 additions & 0 deletions src/settings/CheckoutSettings/CheckoutSettingsForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useIntl,
} from 'react-intl';
import { Field } from 'react-final-form';
import isEqual from 'lodash/isEqual';

import stripesFinalForm from '@folio/stripes/final-form';

Expand Down Expand Up @@ -46,6 +47,7 @@ const CheckoutSettingsForm = ({
label,
pristine,
submitting,
customFieldsOptions,
}) => {
const { formatMessage } = useIntl();

Expand Down Expand Up @@ -191,12 +193,27 @@ const CheckoutSettingsForm = ({
name="wildcardLookupEnabled"
type="checkbox"
/>
<hr />
<Field
name="allowedCustomFieldRefIds"
component={MultiSelection}
dataOptions={customFieldsOptions}
label={formatMessage({ id: 'ui-circulation.settings.checkout.customFieldsAtCheckout' })}
isEqual={isEqual}
itemToString={option => (option ? option.value : '')}
usePortal
emptyMessage={formatMessage({ id: 'ui-circulation.settings.checkout.customFieldsAtCheckout.noCustomFields' })}
/>
</form>
</Pane>
);
};

CheckoutSettingsForm.propTypes = {
customFieldsOptions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
value: PropTypes.string,
})).isRequired,
handleSubmit: PropTypes.func.isRequired,
pristine: PropTypes.bool,
submitting: PropTypes.bool,
Expand Down
19 changes: 19 additions & 0 deletions src/settings/CheckoutSettings/CheckoutSettingsForm.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Field } from 'react-final-form';

import {
render,
screen,
Expand Down Expand Up @@ -33,6 +35,7 @@ describe('CheckoutSettingsForm', () => {
audioTheme: 'ui-circulation.settings.checkout.audioTheme',
wildcardLookup: 'ui-circulation.settings.checkout.wildcardLookup',
otherSettingsFormSubmit: 'ui-circulation.settings.checkout.save',
customFieldsAtCheckout: 'ui-circulation.settings.checkout.customFieldsAtCheckout',
};
const mockedInitialValues = {
checkoutValues: {
Expand Down Expand Up @@ -193,4 +196,20 @@ describe('CheckoutSettingsForm', () => {
expect(screen.getByText(labelIds.otherSettingsFormSubmit)).toBeInTheDocument();
});
});

describe('Custom fields at checkout field', () => {
it('should display a label', () => {
expect(screen.getByText(labelIds.customFieldsAtCheckout)).toBeInTheDocument();
});

describe('when removing first item', () => {
it('should have the itemToString property returning option.value to display the second item as the first one', () => {
const fieldCall = Field.mock.calls.find(call => call[0]?.name === 'allowedCustomFieldRefIds');
const itemToString = fieldCall[0].itemToString;
const option = { value: 'testValue' };

expect(itemToString(option)).toBe('testValue');
});
});
});
});
Loading