diff --git a/CHANGELOG.md b/CHANGELOG.md index 261199c4..aa6e22fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * [UITEN-274](https://folio-org.atlassian.net/browse/UITEN-274) Use Save & close button label stripes-component translation key. * [UITEN-280](https://folio-org.atlassian.net/browse/UITEN-280) Conditionally include SSO Settings based on login-saml interface. +* [UITEN-281](https://folio-org.atlassian.net/browse/UITEN-281) Add Routing service point option to Service point page(ECS only). +* [UITEN-285](https://folio-org.atlassian.net/browse/UITEN-285) Add Confirm Routing service point change modal to edit Service point page(ECS Only). ## [8.1.0](https://github.com/folio-org/ui-tenant-settings/tree/v8.1.0)(2024-03-19) [Full Changelog](https://github.com/folio-org/ui-tenant-settings/compare/v8.0.0...v8.1.0) diff --git a/jest.config.js b/jest.config.js index 98599830..22988ec5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -19,6 +19,7 @@ module.exports = { moduleNameMapper: { '^.+\\.(css)$': 'identity-obj-proxy', '^.+\\.(svg)$': 'identity-obj-proxy', + '^.+\\.(png)$': 'identity-obj-proxy', 'ky': 'ky/umd', }, testMatch: ['**/(lib|src)/**/?(*.)test.{js,jsx}'], diff --git a/src/settings/ServicePoints/ConfirmEcsRequestRoutingChangeModal.js b/src/settings/ServicePoints/ConfirmEcsRequestRoutingChangeModal.js new file mode 100644 index 00000000..2caf0309 --- /dev/null +++ b/src/settings/ServicePoints/ConfirmEcsRequestRoutingChangeModal.js @@ -0,0 +1,54 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import { + Button, + Modal, + ModalFooter, +} from '@folio/stripes/components'; + +const ConfirmEcsRequestRoutingChangeModal = ({ + open, + targetValue, + onConfirm, + onCancel, +}) => { + const footer = ( + + + + + ); + + return ( + } + open={open} + size="small" + footer={footer} + > + + + ); +}; + +ConfirmEcsRequestRoutingChangeModal.propTypes = { + open: PropTypes.bool.isRequired, + targetValue: PropTypes.bool.isRequired, + onConfirm: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +}; + +export default ConfirmEcsRequestRoutingChangeModal; diff --git a/src/settings/ServicePoints/ConfirmEcsRequestRoutingChangeModal.test.js b/src/settings/ServicePoints/ConfirmEcsRequestRoutingChangeModal.test.js new file mode 100644 index 00000000..2f33eff5 --- /dev/null +++ b/src/settings/ServicePoints/ConfirmEcsRequestRoutingChangeModal.test.js @@ -0,0 +1,102 @@ +import { + render, + screen, + fireEvent, +} from '@testing-library/react'; + +import { runAxeTest } from '@folio/stripes-testing'; + +import ConfirmEcsRequestRoutingChangeModal from './ConfirmEcsRequestRoutingChangeModal'; + +const defaultProps = { + open: true, + targetValue: true, + onConfirm: jest.fn(), + onCancel: jest.fn(), +}; +const testIds = { + confirmButton: 'confirmButton', + cancelButton: 'cancelButton', +}; +const messageIds = { + title: 'ui-tenant-settings.settings.confirmEcsRequestRoutingChangeModal.title', + messageNoToYes: 'ui-tenant-settings.settings.confirmEcsRequestRoutingChangeModal.messageNoToYes', + messageYesToNo: 'ui-tenant-settings.settings.confirmEcsRequestRoutingChangeModal.messageYesToNo', + confirmButton: 'ui-tenant-settings.settings.modal.button.confirm', + cancelButton: 'ui-tenant-settings.settings.modal.button.cancel', +}; + +describe('ConfirmEcsRequestRoutingChangeModal', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + beforeEach(() => { + render( + + ); + }); + + it('should render with no axe errors', async () => { + await runAxeTest({ + rootNode: document.body, + }); + }); + + it('should render title', () => { + expect(screen.getByText(messageIds.title)).toBeVisible(); + }); + + it('should render message', () => { + expect(screen.getByText(messageIds.messageNoToYes)).toBeVisible(); + }); + + describe('Alternative props', () => { + const props = { + ...defaultProps, + targetValue: false, + }; + + beforeEach(() => { + render( + + ); + }); + + it('should render message', () => { + expect(screen.getByText(messageIds.messageYesToNo)).toBeVisible(); + }); + }); + + describe('Confirm button', () => { + it('should render confirm button', () => { + expect(screen.getByTestId(testIds.confirmButton)).toBeVisible(); + }); + + it('should render confirm button text', () => { + expect(screen.getByText(messageIds.confirmButton)).toBeVisible(); + }); + + it('should call onConfirm', () => { + fireEvent.click(screen.getByTestId(testIds.confirmButton)); + + expect(defaultProps.onConfirm).toHaveBeenCalled(); + }); + }); + + describe('Cancel button', () => { + it('should render cancel button', () => { + expect(screen.getByTestId(testIds.cancelButton)).toBeVisible(); + }); + + it('should render cancel button text', () => { + expect(screen.getByText(messageIds.cancelButton)).toBeVisible(); + }); + + it('should call onCancel', () => { + fireEvent.click(screen.getByTestId(testIds.cancelButton)); + + expect(defaultProps.onCancel).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.js b/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.js index 258e8698..6c32e96d 100644 --- a/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.js +++ b/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.js @@ -20,13 +20,13 @@ const ConfirmPickupLocationChangeModal = ({ autoFocus onClick={onConfirm} > - + ); diff --git a/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.test.js b/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.test.js index 93865153..5e696d3e 100644 --- a/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.test.js +++ b/src/settings/ServicePoints/ConfirmPickupLocationChangeModal.test.js @@ -22,8 +22,8 @@ const testIds = { const messageIds = { title: 'ui-tenant-settings.settings.confirmPickupLocationChangeModal.title', message: 'ui-tenant-settings.settings.confirmPickupLocationChangeModal.message', - buttonConfirm: 'ui-tenant-settings.settings.confirmPickupLocationChangeModal.button.confirm', - buttonCancel: 'ui-tenant-settings.settings.confirmPickupLocationChangeModal.button.cancel', + buttonConfirm: 'ui-tenant-settings.settings.modal.button.confirm', + buttonCancel: 'ui-tenant-settings.settings.modal.button.cancel', }; describe('ConfirmPickupLocationChangeModal', () => { diff --git a/src/settings/ServicePoints/ServicePointDetail.js b/src/settings/ServicePoints/ServicePointDetail.js index 0443d6d3..0b35ecf7 100644 --- a/src/settings/ServicePoints/ServicePointDetail.js +++ b/src/settings/ServicePoints/ServicePointDetail.js @@ -1,16 +1,18 @@ -import { cloneDeep, keyBy, orderBy } from 'lodash'; import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, FormattedMessage, } from 'react-intl'; +import { cloneDeep, keyBy, orderBy } from 'lodash'; + import { Accordion, Col, ExpandAllButton, KeyValue, Row } from '@folio/stripes/components'; import { ViewMetaData } from '@folio/stripes/smart-components'; - import { TitleManager } from '@folio/stripes/core'; + import LocationList from './LocationList'; import StaffSlipList from './StaffSlipList'; +import { isEcsRequestRoutingVisible, isEcsRequestRoutingAssociatedFieldsVisible } from './utils'; import { intervalPeriods } from '../../constants'; import { closedLibraryDateManagementMapping } from './constants'; @@ -19,6 +21,7 @@ class ServicePointDetail extends React.Component { intl: PropTypes.object, stripes: PropTypes.shape({ connect: PropTypes.func.isRequired, + hasInterface: PropTypes.func.isRequired, }).isRequired, initialValues: PropTypes.object, parentResources: PropTypes.object, @@ -80,7 +83,7 @@ class ServicePointDetail extends React.Component { } render() { - const { initialValues, parentResources } = this.props; + const { initialValues, parentResources, stripes } = this.props; const locations = (parentResources.locations || {}).records || []; const staffSlips = orderBy((parentResources.staffSlips || {}).records || [], 'name'); const servicePoint = initialValues; @@ -133,56 +136,74 @@ class ServicePointDetail extends React.Component { /> - - - } - value={servicePoint.shelvingLagTime} - /> - - - - - }> - { servicePoint.pickupLocation - ? - : - } - - - - { servicePoint.pickupLocation && ( + {isEcsRequestRoutingVisible(stripes) && ( + + + }> + { servicePoint.ecsRequestRouting + ? + : + } + + + + )} + {isEcsRequestRoutingAssociatedFieldsVisible(stripes, servicePoint.ecsRequestRouting) && ( <> - + } - value={`${duration} ${this.intervalPeriodMap[intervalId].label}`} + label={} + value={servicePoint.shelvingLagTime} /> - - }> - + + }> + { servicePoint.pickupLocation + ? + : + } + { servicePoint.pickupLocation && ( + <> + + + } + value={`${duration} ${this.intervalPeriodMap[intervalId].label}`} + /> + + + + + }> + + + + + + ) + } + - ) - } - + )} - + {isEcsRequestRoutingAssociatedFieldsVisible(stripes, servicePoint.ecsRequestRouting) && ( + + )} ); diff --git a/src/settings/ServicePoints/ServicePointForm.js b/src/settings/ServicePoints/ServicePointForm.js index 1e0c0e3a..9ce2af6c 100644 --- a/src/settings/ServicePoints/ServicePointForm.js +++ b/src/settings/ServicePoints/ServicePointForm.js @@ -30,6 +30,7 @@ import { ViewMetaData } from '@folio/stripes/smart-components'; import stripesFinalForm from '@folio/stripes/final-form'; import { useStripes } from '@folio/stripes/core'; +import ConfirmEcsRequestRoutingChangeModal from './ConfirmEcsRequestRoutingChangeModal'; import ConfirmPickupLocationChangeModal from './ConfirmPickupLocationChangeModal'; import Period from '../../components/Period'; import LocationList from './LocationList'; @@ -39,6 +40,8 @@ import { intervalPeriods } from '../../constants'; import { validateServicePointForm, getUniquenessValidation, + isEcsRequestRoutingVisible, + isEcsRequestRoutingAssociatedFieldsVisible, } from './utils'; import { @@ -49,9 +52,13 @@ import { import styles from './ServicePoints.css'; +export const SELECTED_ROUTING_SERVICE_POINT_VALUE = 'true'; export const SELECTED_PICKUP_LOCATION_VALUE = 'true'; export const UNSELECTED_PICKUP_LOCATION_VALUE = 'false'; export const LAYER_EDIT = 'layer=edit'; +export const isConfirmEcsRequestRoutingChangeModalShouldBeVisible = (search, stripes) => ( + search.includes(LAYER_EDIT) && isEcsRequestRoutingVisible(stripes) +); export const isConfirmPickupLocationChangeModalShouldBeVisible = (search, value) => ( search.includes(LAYER_EDIT) && value === UNSELECTED_PICKUP_LOCATION_VALUE ); @@ -73,6 +80,8 @@ const ServicePointForm = ({ generalSection: true, locationSection: true }); + const [ecsRequestRoutingTargetValue, setEcsRequestRoutingTargetValue] = useState(false); + const [isConfirmEcsRequestRoutingChangeModal, setIsConfirmEcsRequestRoutingChangeModal] = useState(false); const [isConfirmPickupLocationChangeModal, setIsConfirmPickupLocationChangeModal] = useState(false); const stripes = useStripes(); const intl = useIntl(); @@ -88,11 +97,11 @@ const ServicePointForm = ({ const selectOptions = [ { - label: intl.formatMessage({ id: 'ui-tenant-settings.settings.servicePoints.pickupLocation.no' }), + label: intl.formatMessage({ id: 'ui-tenant-settings.settings.servicePoints.value.no' }), value: false }, { - label: intl.formatMessage({ id: 'ui-tenant-settings.settings.servicePoints.pickupLocation.yes' }), + label: intl.formatMessage({ id: 'ui-tenant-settings.settings.servicePoints.value.yes' }), value: true } ]; @@ -103,6 +112,12 @@ const ServicePointForm = ({ } )); + const onConfirmConfirmEcsRequestRoutingChangeModal = () => { + form.change('ecsRequestRouting', ecsRequestRoutingTargetValue); + setIsConfirmEcsRequestRoutingChangeModal(false); + }; + const onCancelConfirmEcsRequestRoutingChangeModal = () => setIsConfirmEcsRequestRoutingChangeModal(false); + const onConfirmConfirmPickupLocationChangeModal = () => { form.change('pickupLocation', false); setIsConfirmPickupLocationChangeModal(false); @@ -200,6 +215,17 @@ const ServicePointForm = ({ ))); }; + const handleEcsRequestRoutingChange = (e) => { + const value = e.target.value; + + if (isConfirmEcsRequestRoutingChangeModalShouldBeVisible(search, stripes)) { + setEcsRequestRoutingTargetValue(value === SELECTED_ROUTING_SERVICE_POINT_VALUE); + setIsConfirmEcsRequestRoutingChangeModal(true); + } else { + form.change('ecsRequestRouting', value === SELECTED_ROUTING_SERVICE_POINT_VALUE); + } + }; + const handleChange = (e) => { const value = e.target.value; @@ -290,67 +316,96 @@ const ServicePointForm = ({ /> - - - } - name="shelvingLagTime" - id="input-service-shelvingLagTime" - component={TextField} - fullWidth - disabled={disabled} - /> - - - - - } - name="pickupLocation" - id="input-service-pickupLocation" - component={Select} - dataOptions={selectOptions} - onChange={handleChange} - disabled={disabled} - /> - - - { - formValues.pickupLocation && ( - <> -
- + + } + name="ecsRequestRouting" + id="input-service-ecsRequestRouting" + component={Select} + dataOptions={selectOptions} + onChange={handleEcsRequestRoutingChange} + disabled={disabled} + /> + + + )} + {isEcsRequestRoutingAssociatedFieldsVisible(stripes, formValues.ecsRequestRouting) && ( + <> + + + } + name="shelvingLagTime" + id="input-service-shelvingLagTime" + component={TextField} + fullWidth + disabled={disabled} /> -
-
+ + + + } - name="holdShelfClosedLibraryDateManagement" + data-test-pickup-location + label={} + name="pickupLocation" + id="input-service-pickupLocation" component={Select} - dataOptions={getClosedLibraryDateManagementOptions()} + dataOptions={selectOptions} + onChange={handleChange} + disabled={disabled} /> -
- - ) - } - + + + { + formValues.pickupLocation && ( + <> +
+ +
+
+ } + name="holdShelfClosedLibraryDateManagement" + component={Select} + dataOptions={getClosedLibraryDateManagementOptions()} + /> +
+ + ) + } + + + )} - + {isEcsRequestRoutingAssociatedFieldsVisible(stripes, formValues.ecsRequestRouting) && ( + + )} + {isConfirmEcsRequestRoutingChangeModalShouldBeVisible(search, stripes) && ( + + )} - { userEvent.selectOptions(screen.getByRole('combobox', { name: /settings.servicePoints.pickupLocation/ }), 'true'); - expect(screen.getByRole('option', { name: /settings.servicePoints.pickupLocation.yes/ }).selected).toBe(true); + expect(screen.getAllByRole('option', { name: /settings.servicePoints.value.yes/ })[1].selected).toBe(true); }); describe('when pick location is yes', () => { @@ -97,7 +97,7 @@ describe('ServicePointFormContainer', () => { describe('when hold shelf expiry interval id is short term period Days', () => { beforeEach(() => { - userEvent.selectOptions(screen.getAllByRole('combobox')[1], 'Days'); + userEvent.selectOptions(screen.getAllByRole('combobox')[2], 'Days'); }); it('should render ServicePointFormContainer closed library date management select with changed options ', () => { diff --git a/src/settings/ServicePoints/utils.js b/src/settings/ServicePoints/utils.js index 9df81086..af581fae 100644 --- a/src/settings/ServicePoints/utils.js +++ b/src/settings/ServicePoints/utils.js @@ -53,3 +53,11 @@ export const getUniquenessValidation = (field, mutator) => { }); }; }; + +export const isEcsRequestRoutingVisible = (stripes) => ( + stripes.hasInterface('consortia') && stripes.hasInterface('ecs-tlr') +); + +export const isEcsRequestRoutingAssociatedFieldsVisible = (stripes, ecsRequestRouting) => ( + (isEcsRequestRoutingVisible(stripes) && !ecsRequestRouting) || !isEcsRequestRoutingVisible(stripes) +); diff --git a/src/settings/ServicePoints/utils.test.js b/src/settings/ServicePoints/utils.test.js new file mode 100644 index 00000000..6458327f --- /dev/null +++ b/src/settings/ServicePoints/utils.test.js @@ -0,0 +1,65 @@ +import { + isEcsRequestRoutingVisible, +} from './utils'; + +describe('isEcsRequestRoutingVisible', () => { + it('should return true when both interfaces present', () => { + const stripes = { + hasInterface: (currentInterface) => { + const interfaces = { + consortia: true, + 'ecs-tlr': true, + }; + + return interfaces[currentInterface]; + }, + }; + + expect(isEcsRequestRoutingVisible(stripes)).toBe(true); + }); + + it('should return false when one interfaces absent', () => { + const stripes = { + hasInterface: (currentInterface) => { + const interfaces = { + consortia: true, + 'ecs-tlr': false, + }; + + return interfaces[currentInterface]; + }, + }; + + expect(isEcsRequestRoutingVisible(stripes)).toBe(false); + }); + + it('should return false when one interfaces absent', () => { + const stripes = { + hasInterface: (currentInterface) => { + const interfaces = { + consortia: false, + 'ecs-tlr': true, + }; + + return interfaces[currentInterface]; + }, + }; + + expect(isEcsRequestRoutingVisible(stripes)).toBe(false); + }); + + it('should return false when both interfaces absent', () => { + const stripes = { + hasInterface: (currentInterface) => { + const interfaces = { + consortia: false, + 'ecs-tlr': false, + }; + + return interfaces[currentInterface]; + }, + }; + + expect(isEcsRequestRoutingVisible(stripes)).toBe(false); + }); +}); diff --git a/translations/ui-tenant-settings/en.json b/translations/ui-tenant-settings/en.json index 85f35932..3f9fe78d 100644 --- a/translations/ui-tenant-settings/en.json +++ b/translations/ui-tenant-settings/en.json @@ -138,6 +138,7 @@ "settings.servicePoints.code": "Code", "settings.servicePoints.discoveryDisplayName": "Discovery display name", "settings.servicePoints.description": "Description", + "settings.servicePoints.ecsRequestRouting": "Routing service point", "settings.servicePoints.shelvingLagTime": "Shelving lag time (minutes)", "settings.servicePoints.pickupLocation": "Pickup location", "settings.servicePoints.feeFineOwner": "Fee fine owner", @@ -163,8 +164,8 @@ "settings.servicePoints.validation.numeric": "Please enter a number to continue", "settings.servicePoints.validation.name.unique": "Service point name must be unique", "settings.servicePoints.validation.code.unique": "Code must be unique", - "settings.servicePoints.pickupLocation.yes": "Yes", - "settings.servicePoints.pickupLocation.no": "No", + "settings.servicePoints.value.yes": "Yes", + "settings.servicePoints.value.no": "No", "settings.intervalPeriod.minutes": "Minutes", "settings.intervalPeriod.hours": "Hours", "settings.intervalPeriod.days": "Days", @@ -216,8 +217,11 @@ "permission.settings.servicepoints.view": "Settings (tenant): Can view service points", "permission.settings.bursar-exports": "Settings (tenant): Bursar admin", + "settings.confirmEcsRequestRoutingChangeModal.title": "Confirm Routing service point change", + "settings.confirmEcsRequestRoutingChangeModal.messageYesToNo": "Changing this Routing service point from \"Yes\" to \"No\" will remove it from existing Request policies and affect all Circulation rules using the policies.", + "settings.confirmEcsRequestRoutingChangeModal.messageNoToYes": "Changing Routing service point from \"No\" to \"Yes\" will remove the Service point from being a Pickup location in existing Request policies and affect all Circulation rules using the policies.", "settings.confirmPickupLocationChangeModal.title": "Confirm Pickup location change", "settings.confirmPickupLocationChangeModal.message": "Changing this Pickup location from \"Yes\" to \"No\" will remove it from existing Request policies and affect all Circulation rules using the policies.", - "settings.confirmPickupLocationChangeModal.button.confirm": "Confirm", - "settings.confirmPickupLocationChangeModal.button.cancel": "Back" + "settings.modal.button.confirm": "Confirm", + "settings.modal.button.cancel": "Back" }