From 4288459a0d1817385ece01da6b8f2588f692b70a Mon Sep 17 00:00:00 2001 From: Matthew Crouch Date: Mon, 18 Dec 2023 13:53:41 -0500 Subject: [PATCH] MMT-3410: Use widget wrapper for country select, removes test ids --- .../CustomCountrySelectWidget.jsx | 93 ++++++++++++++----- .../CustomCountrySelectWidget.test.js | 32 ++++++- .../CustomFieldTemplate.jsx | 5 +- .../CustomMultiSelectWidget.jsx | 2 +- .../CustomSelectWidget/CustomSelectWidget.jsx | 4 +- .../__tests__/CustomSelectWidget.test.js | 18 ---- .../__tests__/CustomTextWidget.test.js | 18 ---- .../components/DraftPreview/DraftPreview.jsx | 1 - .../src/js/components/GridField/GridField.jsx | 3 +- .../NavigationItemError.scss | 9 -- 10 files changed, 102 insertions(+), 83 deletions(-) diff --git a/static/src/js/components/CustomCountrySelectWidget/CustomCountrySelectWidget.jsx b/static/src/js/components/CustomCountrySelectWidget/CustomCountrySelectWidget.jsx index 2526f80ac..d581d17c8 100644 --- a/static/src/js/components/CustomCountrySelectWidget/CustomCountrySelectWidget.jsx +++ b/static/src/js/components/CustomCountrySelectWidget/CustomCountrySelectWidget.jsx @@ -1,8 +1,15 @@ -import React, { useMemo, useState } from 'react' +import React, { + useEffect, + useMemo, + useRef, + useState +} from 'react' import PropTypes from 'prop-types' import Select from 'react-select' import { startCase } from 'lodash-es' import countryList from 'react-select-country-list' +import CustomWidgetWrapper from '../CustomWidgetWrapper/CustomWidgetWrapper' +import shouldFocusField from '../../utils/shouldFocusField' /** * CustomCountrySelectWidget @@ -21,11 +28,26 @@ import countryList from 'react-select-country-list' const CustomCountrySelectWidget = ({ id, label, + onBlur, onChange, + registry, required, + schema, uiSchema, value }) => { + const selectScrollRef = useRef(null) + const focusRef = useRef(null) + + const { description } = schema + + const { formContext } = registry + + const { + focusField, + setFocusField + } = formContext + // Pull the data from countryList once const countryData = useMemo(() => countryList().getData(), []) @@ -73,6 +95,11 @@ const CustomCountrySelectWidget = ({ }) } + const handleBlur = () => { + setFocusField(null) + onBlur(id) + } + // Uses label for title let title = startCase(label.split(/-/)[0]) @@ -81,31 +108,37 @@ const CustomCountrySelectWidget = ({ title = uiSchema['ui:title'] } + const shouldFocus = shouldFocusField(focusField, id) + + useEffect(() => { + // This useEffect for shouldFocus lets the refs be in place before trying to use them + if (shouldFocus) { + selectScrollRef.current?.scrollIntoView({ behavior: 'smooth' }) + focusRef.current?.focus() + } + }, [shouldFocus]) + return ( -
-
- - {title} - - - - {/* // TODO This should be an icon */} - {required ? '*' : ''} - -
- -
- + ) } @@ -117,8 +150,18 @@ CustomCountrySelectWidget.defaultProps = { CustomCountrySelectWidget.propTypes = { id: PropTypes.string.isRequired, label: PropTypes.string, + onBlur: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired, + registry: PropTypes.shape({ + formContext: PropTypes.shape({ + focusField: PropTypes.string, + setFocusField: PropTypes.func + }).isRequired + }).isRequired, required: PropTypes.bool.isRequired, + schema: PropTypes.shape({ + description: PropTypes.string + }).isRequired, uiSchema: PropTypes.shape({ 'ui:title': PropTypes.string }).isRequired, diff --git a/static/src/js/components/CustomCountrySelectWidget/__tests__/CustomCountrySelectWidget.test.js b/static/src/js/components/CustomCountrySelectWidget/__tests__/CustomCountrySelectWidget.test.js index 2a5738f4f..781e55548 100644 --- a/static/src/js/components/CustomCountrySelectWidget/__tests__/CustomCountrySelectWidget.test.js +++ b/static/src/js/components/CustomCountrySelectWidget/__tests__/CustomCountrySelectWidget.test.js @@ -6,12 +6,22 @@ import CustomCountrySelectWidget from '../CustomCountrySelectWidget' const setup = (overrideProps = {}) => { const props = { - id: 'the-widget', + id: 'mock-id', label: 'MyTestDataLabel', - required: false, + onBlur: jest.fn(), onChange: jest.fn(), - value: 'TZ', + registry: { + formContext: { + focusField: '', + setFocusField: jest.fn() + } + }, + required: false, + schema: { + description: 'Test Description' + }, uiSchema: {}, + value: 'TZ', ...overrideProps } @@ -78,4 +88,20 @@ describe('CustomCountrySelectWidget', () => { expect(props.onChange).toHaveBeenCalledWith('US') }) }) + + describe('when the field should be focused', () => { + test('focuses the field', async () => { + setup({ + registry: { + formContext: { + focusField: 'mock-id' + } + } + }) + + const field = screen.getByRole('combobox') + + expect(field).toHaveFocus() + }) + }) }) diff --git a/static/src/js/components/CustomFieldTemplate/CustomFieldTemplate.jsx b/static/src/js/components/CustomFieldTemplate/CustomFieldTemplate.jsx index cd0203499..2269e832b 100644 --- a/static/src/js/components/CustomFieldTemplate/CustomFieldTemplate.jsx +++ b/static/src/js/components/CustomFieldTemplate/CustomFieldTemplate.jsx @@ -20,10 +20,7 @@ const CustomFieldTemplate = ({ errors, children }) => ( -
+
{children} {errors} {help} diff --git a/static/src/js/components/CustomMultiSelectWidget/CustomMultiSelectWidget.jsx b/static/src/js/components/CustomMultiSelectWidget/CustomMultiSelectWidget.jsx index 93c1e7bb9..6f43ee44b 100644 --- a/static/src/js/components/CustomMultiSelectWidget/CustomMultiSelectWidget.jsx +++ b/static/src/js/components/CustomMultiSelectWidget/CustomMultiSelectWidget.jsx @@ -7,7 +7,7 @@ import CustomWidgetWrapper from '../CustomWidgetWrapper/CustomWidgetWrapper' /** * CustomMultiSelectWidget - * @typedef {Object} CustomArrayFieldTemplate + * @typedef {Object} CustomMultiSelectWidget * @property {String} id The id of the widget. * @property {String} label The label of the widget. * @property {Boolean} onBlur Should blur a field. diff --git a/static/src/js/components/CustomSelectWidget/CustomSelectWidget.jsx b/static/src/js/components/CustomSelectWidget/CustomSelectWidget.jsx index c8f0ab622..b0aa8b91f 100644 --- a/static/src/js/components/CustomSelectWidget/CustomSelectWidget.jsx +++ b/static/src/js/components/CustomSelectWidget/CustomSelectWidget.jsx @@ -15,7 +15,7 @@ import useControlledKeywords from '../../hooks/useControlledKeywords' /** * CustomSelectWidget - * @typedef {Object} CustomArrayFieldTemplate + * @typedef {Object} CustomSelectWidget * @property {Boolean} disable A boolean value to disable the select field. * @property {String} label The label of the widget. * @property {String} id The id of the widget. @@ -32,7 +32,7 @@ import useControlledKeywords from '../../hooks/useControlledKeywords' /** * Renders Custom Select Widget - * @param {CustomArrayFieldTemplate} props + * @param {CustomSelectWidget} props */ const CustomSelectWidget = ({ disabled, diff --git a/static/src/js/components/CustomSelectWidget/__tests__/CustomSelectWidget.test.js b/static/src/js/components/CustomSelectWidget/__tests__/CustomSelectWidget.test.js index 86a5844e9..914548f76 100644 --- a/static/src/js/components/CustomSelectWidget/__tests__/CustomSelectWidget.test.js +++ b/static/src/js/components/CustomSelectWidget/__tests__/CustomSelectWidget.test.js @@ -233,24 +233,6 @@ describe('CustomSelectWidget', () => { }) }) - describe('when the field is focused', () => { - test('shows the field description', async () => { - setup() - - const field = screen.getByRole('combobox') - - await waitFor(async () => { - field.focus() - }) - - expect(field).toHaveFocus() - - expect(CustomWidgetWrapper).toHaveBeenCalledWith(expect.objectContaining({ - description: 'Test Description' - }), {}) - }) - }) - describe('when the field is changed', () => { test('calls onChange', async () => { const { props, user } = setup() diff --git a/static/src/js/components/CustomTextWidget/__tests__/CustomTextWidget.test.js b/static/src/js/components/CustomTextWidget/__tests__/CustomTextWidget.test.js index 3f781f2d1..1bcd8a8ba 100644 --- a/static/src/js/components/CustomTextWidget/__tests__/CustomTextWidget.test.js +++ b/static/src/js/components/CustomTextWidget/__tests__/CustomTextWidget.test.js @@ -112,24 +112,6 @@ describe('CustomTextWidget', () => { }) }) - describe('when the field is focused', () => { - test('shows the field description', async () => { - setup() - - const field = screen.getByRole('textbox') - - await waitFor(async () => { - field.focus() - }) - - expect(field).toHaveFocus() - - expect(CustomWidgetWrapper).toHaveBeenCalledWith(expect.objectContaining({ - description: 'Test Description' - }), {}) - }) - }) - describe('when the field is blurred', () => { test('clears the focusField and calls onBlur', async () => { const { props } = setup() diff --git a/static/src/js/components/DraftPreview/DraftPreview.jsx b/static/src/js/components/DraftPreview/DraftPreview.jsx index 58063aecd..285624ecf 100644 --- a/static/src/js/components/DraftPreview/DraftPreview.jsx +++ b/static/src/js/components/DraftPreview/DraftPreview.jsx @@ -267,7 +267,6 @@ const DraftPreview = () => {