diff --git a/package-lock.json b/package-lock.json index 95a7e0f41..fa514c9f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@edx/frontend-component-footer": "^13.0.2", "@edx/frontend-component-header": "^5.0.2", "@edx/frontend-enterprise-hotjar": "^2.0.0", - "@edx/frontend-lib-content-components": "^2.1.1", + "@edx/frontend-lib-content-components": "^2.1.3", "@edx/frontend-platform": "7.0.1", "@edx/openedx-atlas": "^0.6.0", "@fortawesome/fontawesome-svg-core": "1.2.36", @@ -2632,9 +2632,9 @@ } }, "node_modules/@edx/frontend-lib-content-components": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-content-components/-/frontend-lib-content-components-2.1.1.tgz", - "integrity": "sha512-VCEhFfLkK+o08g7Ts9YgYNb8GHd6d9Mw8lTT1L2OIMsDC2ncFHBUGOfzmoRt+M3cJMb2nAL7Yh4wvNxhgRhhuA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@edx/frontend-lib-content-components/-/frontend-lib-content-components-2.1.3.tgz", + "integrity": "sha512-RDlWyZ57Ecbz1qOZv2aHMSsWbwo2IoK06PHEXsEgZv0f1lmp25BqlDNhhipczcdg0te5K8gVR4qATZ4+EnhKWQ==", "dependencies": { "@codemirror/lang-html": "^6.0.0", "@codemirror/lang-xml": "^6.0.0", diff --git a/package.json b/package.json index aa1dfc997..2c91d5ae8 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@edx/frontend-component-footer": "^13.0.2", "@edx/frontend-component-header": "^5.0.2", "@edx/frontend-enterprise-hotjar": "^2.0.0", - "@edx/frontend-lib-content-components": "^2.1.1", + "@edx/frontend-lib-content-components": "^2.1.3", "@edx/frontend-platform": "7.0.1", "@edx/openedx-atlas": "^0.6.0", "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx index 14ce54f81..fef4b8c01 100644 --- a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx +++ b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx @@ -1,6 +1,8 @@ import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n'; +// SelectableBox in paragon has a bug only visible on stage where you can't change selection. So we override it +import { SelectableBox } from '@edx/frontend-lib-content-components'; import { ActionRow, Button, @@ -9,7 +11,6 @@ import { ModalDialog, useCheckboxSetValues, } from '@openedx/paragon'; -import SelectableBox from '../../../../generic/SelectableBox'; import messages from './messages'; import { getCheckedFilters, getFilterOptions, processFilters } from './utils'; diff --git a/src/generic/SelectableBox/FormCheckbox.jsx b/src/generic/SelectableBox/FormCheckbox.jsx deleted file mode 100644 index d2e1951ba..000000000 --- a/src/generic/SelectableBox/FormCheckbox.jsx +++ /dev/null @@ -1,145 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { useCheckboxSetContext } from './FormCheckboxSetContext'; -import { FormGroupContextProvider, useFormGroupContext } from './FormGroupContext'; -import FormLabel from './FormLabel'; -import FormControlFeedback from './FormControlFeedback'; - -const CheckboxControl = React.forwardRef( - ({ isIndeterminate, ...props }, ref) => { - const { getCheckboxControlProps, hasCheckboxSetProvider } = useCheckboxSetContext(); - const defaultRef = React.useRef(); - const resolvedRef = ref || defaultRef; - const { getControlProps } = useFormGroupContext(); - let checkboxProps = getControlProps({ - ...props, - className: classNames('pgn__form-checkbox-input', props.className), - }); - - if (hasCheckboxSetProvider) { - checkboxProps = getCheckboxControlProps(checkboxProps); - } - - React.useEffect(() => { - // this if(resolvedRef.current) prevents console errors in testing - if (resolvedRef.current) { - resolvedRef.current.indeterminate = isIndeterminate; - } - }, [resolvedRef, isIndeterminate]); - - return ( - - ); - }, -); - -CheckboxControl.propTypes = { - /** Specifies whether the checkbox should be rendered in indeterminate state. */ - isIndeterminate: PropTypes.bool, - /** Specifies class name to append to the base element. */ - className: PropTypes.string, -}; - -CheckboxControl.defaultProps = { - isIndeterminate: false, - className: undefined, -}; - -const FormCheckbox = React.forwardRef(({ - children, - className, - controlClassName, - labelClassName, - description, - isInvalid, - isValid, - controlAs, - floatLabelLeft, - ...props -}, ref) => { - const { hasCheckboxSetProvider } = useCheckboxSetContext(); - const { hasFormGroupProvider, useSetIsControlGroupEffect, getControlProps } = useFormGroupContext(); - useSetIsControlGroupEffect(true); - const shouldActAsGroup = hasFormGroupProvider && !hasCheckboxSetProvider; - const groupProps = shouldActAsGroup ? { - ...getControlProps({}), - role: 'group', - } : {}; - - const control = React.createElement(controlAs, { ...props, className: controlClassName, ref }); - return ( - -
- {control} -
- - {children} - - {description && ( - - {description} - - )} -
-
-
- ); -}); - -FormCheckbox.propTypes = { - /** Specifies id of the FormCheckbox component. */ - id: PropTypes.string, - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** Specifies class name to append to the base element. */ - className: PropTypes.string, - /** Specifies class name for control component. */ - controlClassName: PropTypes.string, - /** Specifies class name for label component. */ - labelClassName: PropTypes.string, - /** Specifies description to show under the checkbox. */ - description: PropTypes.node, - /** Specifies whether to display checkbox in invalid state, this affects styling. */ - isInvalid: PropTypes.bool, - /** Specifies whether to display checkbox in valid state, this affects styling. */ - isValid: PropTypes.bool, - /** Specifies control element. */ - controlAs: PropTypes.elementType, - /** Specifies whether the floating label should be aligned to the left. */ - floatLabelLeft: PropTypes.bool, - /** Specifies whether the `FormCheckbox` is disabled. */ - disabled: PropTypes.bool, -}; - -FormCheckbox.defaultProps = { - id: undefined, - className: undefined, - controlClassName: undefined, - labelClassName: undefined, - description: undefined, - isInvalid: false, - isValid: false, - controlAs: CheckboxControl, - floatLabelLeft: false, - disabled: false, -}; - -export { CheckboxControl }; -export default FormCheckbox; diff --git a/src/generic/SelectableBox/FormCheckboxSet.jsx b/src/generic/SelectableBox/FormCheckboxSet.jsx deleted file mode 100644 index 2d90cada0..000000000 --- a/src/generic/SelectableBox/FormCheckboxSet.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useFormGroupContext } from './FormGroupContext'; -import { FormCheckboxSetContextProvider } from './FormCheckboxSetContext'; -import FormControlSet from './FormControlSet'; - -const FormCheckboxSet = ({ - children, - name, - value, - defaultValue, - isInline, - onChange, - onFocus, - onBlur, - ...props -}) => { - const { getControlProps, useSetIsControlGroupEffect } = useFormGroupContext(); - useSetIsControlGroupEffect(true); - const controlProps = getControlProps(props); - return ( - - - {children} - - - ); -}; - -FormCheckboxSet.propTypes = { - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** Specifies class name to append to the base element. */ - className: PropTypes.string, - /** Specifies name for the component. */ - name: PropTypes.string.isRequired, - /** Specifies values for the checkboxes. */ - value: PropTypes.arrayOf(PropTypes.string), - /** Specifies default values for the checkboxes. */ - defaultValue: PropTypes.arrayOf(PropTypes.string), - /** Specifies whether to display components with inline styling. */ - isInline: PropTypes.bool, - /** Specifies onChange event handler. */ - onChange: PropTypes.func, - /** Specifies onFocus event handler. */ - onFocus: PropTypes.func, - /** Specifies onBlur event handler. */ - onBlur: PropTypes.func, -}; - -FormCheckboxSet.defaultProps = { - className: undefined, - value: undefined, - defaultValue: undefined, - isInline: false, - onChange: undefined, - onFocus: undefined, - onBlur: undefined, -}; - -export default FormCheckboxSet; diff --git a/src/generic/SelectableBox/FormCheckboxSetContext.jsx b/src/generic/SelectableBox/FormCheckboxSetContext.jsx deleted file mode 100644 index 1dd45b524..000000000 --- a/src/generic/SelectableBox/FormCheckboxSetContext.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { useContext } from 'react'; -import PropTypes from 'prop-types'; -import { callAllHandlers } from './fieldUtils'; - -const identityFn = props => props; - -const FormCheckboxSetContext = React.createContext({ - getCheckboxControlProps: identityFn, - hasCheckboxSetProvider: false, -}); - -const useCheckboxSetContext = () => useContext(FormCheckboxSetContext); - -const FormCheckboxSetContextProvider = ({ - children, - name, - onBlur, - onFocus, - onChange, - value, - defaultValue, -}) => { - const isControlled = !defaultValue && Array.isArray(value); - const getCheckboxControlProps = (checkboxProps) => ({ - ...checkboxProps, - name, - /* istanbul ignore next */ - onBlur: checkboxProps.onBlur ? callAllHandlers(onBlur, checkboxProps.onBlur) : onBlur, - /* istanbul ignore next */ - onFocus: checkboxProps.onFocus ? callAllHandlers(onFocus, checkboxProps.onFocus) : onFocus, - /* istanbul ignore next */ - onChange: checkboxProps.onChange ? callAllHandlers(onChange, checkboxProps.onChange) : onChange, - checked: isControlled ? value.includes(checkboxProps.value) : undefined, - defaultChecked: isControlled ? undefined : (defaultValue && defaultValue.includes(checkboxProps.value)), - }); - // eslint-disable-next-line react/jsx-no-constructed-context-values - const contextValue = { - name, - value, - defaultValue, - getCheckboxControlProps, - onBlur, - onFocus, - onChange, - hasCheckboxSetProvider: true, - }; - return ( - - {children} - - ); -}; - -FormCheckboxSetContextProvider.propTypes = { - children: PropTypes.node.isRequired, - name: PropTypes.string, - onBlur: PropTypes.func, - onFocus: PropTypes.func, - onChange: PropTypes.func, - value: PropTypes.arrayOf(PropTypes.string), - defaultValue: PropTypes.arrayOf(PropTypes.string), -}; - -FormCheckboxSetContextProvider.defaultProps = { - onBlur: undefined, - name: undefined, - onFocus: undefined, - onChange: undefined, - value: undefined, - defaultValue: undefined, -}; - -export default FormCheckboxSetContext; -export { - useCheckboxSetContext, - FormCheckboxSetContextProvider, -}; diff --git a/src/generic/SelectableBox/FormControlFeedback.jsx b/src/generic/SelectableBox/FormControlFeedback.jsx deleted file mode 100644 index b4e211218..000000000 --- a/src/generic/SelectableBox/FormControlFeedback.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { useFormGroupContext } from './FormGroupContext'; -import FormText, { resolveTextType } from './FormText'; - -const FormControlFeedback = ({ children, ...props }) => { - const { getDescriptorProps, isInvalid, isValid } = useFormGroupContext(); - const descriptorProps = getDescriptorProps(props); - const className = classNames('pgn__form-control-description', props.className); - const textType = props.type || resolveTextType({ isInvalid, isValid }); - return ( - - {children} - - ); -}; - -const FEEDBACK_TYPES = [ - 'default', - 'valid', - 'invalid', - 'warning', - 'criteria-empty', - 'criteria-valid', - 'criteria-invalid', -]; - -FormControlFeedback.propTypes = { - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** Specifies class name to append to the base element. */ - className: PropTypes.string, - /** Specifies whether to show an icon next to the text. */ - hasIcon: PropTypes.bool, - /** Specifies feedback type, this affects styling. */ - type: PropTypes.oneOf(FEEDBACK_TYPES), - /** Specifies icon to show, will only be shown if `hasIcon` prop is set to `true`. */ - icon: PropTypes.node, - /** Specifies whether to show feedback with muted styling. */ - muted: PropTypes.bool, -}; - -FormControlFeedback.defaultProps = { - hasIcon: true, - type: undefined, - icon: undefined, - className: undefined, - muted: false, -}; - -export default FormControlFeedback; diff --git a/src/generic/SelectableBox/FormControlSet.jsx b/src/generic/SelectableBox/FormControlSet.jsx deleted file mode 100644 index 69052e767..000000000 --- a/src/generic/SelectableBox/FormControlSet.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -const FormControlSet = ({ - as, - className, - isInline, - children, - ...props -}) => React.createElement(as, { - className: classNames( - className, - { - 'pgn__form-control-set': !isInline, - 'pgn__form-control-set-inline': isInline, - }, - ), - ...props, -}, children); - -FormControlSet.propTypes = { - /** Specifies the base element */ - as: PropTypes.elementType, - /** A class name to append to the base element. */ - className: PropTypes.string, - /** Specifies whether the component should be displayed with inline styling. */ - isInline: PropTypes.bool, - /** Specifies contents of the component. */ - children: PropTypes.node, -}; - -FormControlSet.defaultProps = { - as: 'div', - className: undefined, - isInline: false, - children: null, -}; - -export default FormControlSet; diff --git a/src/generic/SelectableBox/FormGroupContext.jsx b/src/generic/SelectableBox/FormGroupContext.jsx deleted file mode 100644 index af38a4f8a..000000000 --- a/src/generic/SelectableBox/FormGroupContext.jsx +++ /dev/null @@ -1,121 +0,0 @@ -import React, { - useState, useEffect, useMemo, useCallback, -} from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import newId from './newId'; -import { useIdList, omitUndefinedProperties } from './fieldUtils'; -import { FORM_CONTROL_SIZES } from './constants'; - -const identityFn = props => props; -const noop = () => {}; - -const FormGroupContext = React.createContext({ - getControlProps: identityFn, - useSetIsControlGroupEffect: noop, - getLabelProps: identityFn, - getDescriptorProps: identityFn, - hasFormGroupProvider: false, -}); - -const useFormGroupContext = () => React.useContext(FormGroupContext); - -const useStateEffect = (initialState) => { - const [state, setState] = useState(initialState); - const useSetStateEffect = (newState) => { - useEffect(() => setState(newState), [newState]); - }; - return [state, useSetStateEffect]; -}; - -const FormGroupContextProvider = ({ - children, - controlId: explicitControlId, - isInvalid, - isValid, - size, -}) => { - const controlId = useMemo(() => explicitControlId || newId('form-field'), [explicitControlId]); - const [describedByIds, registerDescriptorId] = useIdList(controlId); - const [labelledByIds, registerLabelerId] = useIdList(controlId); - const [isControlGroup, useSetIsControlGroupEffect] = useStateEffect(false); - - const getControlProps = useCallback((controlProps) => { - // labelledByIds from the list above should only be added to a control - // if it the control is a group. We prefer adding a condition here because: - // - Hooks cannot be called inside conditionals - // - The getLabelProps function below is forced to generate an id - // whether it is needed or not. - // - This is what allows consumers of Paragon to use - // interchangeably between ControlGroup type controls and regular Controls - const labelledByIdsForControl = isControlGroup ? labelledByIds : undefined; - return omitUndefinedProperties({ - ...controlProps, - 'aria-describedby': classNames(controlProps['aria-describedby'], describedByIds) || undefined, - 'aria-labelledby': classNames(controlProps['aria-labelledby'], labelledByIdsForControl) || undefined, - id: controlId, - }); - }, [ - isControlGroup, - describedByIds, - labelledByIds, - controlId, - ]); - - const getLabelProps = (labelProps) => { - const id = registerLabelerId(labelProps?.id); - if (isControlGroup) { - return { ...labelProps, id }; - } - return { ...labelProps, htmlFor: controlId }; - }; - - const getDescriptorProps = (descriptorProps) => { - const id = registerDescriptorId(descriptorProps?.id); - return { ...descriptorProps, id }; - }; - - // eslint-disable-next-line react/jsx-no-constructed-context-values - const contextValue = { - getControlProps, - getLabelProps, - getDescriptorProps, - useSetIsControlGroupEffect, - isControlGroup, - controlId, - isInvalid, - isValid, - size, - hasFormGroupProvider: true, - }; - - return ( - - {children} - - ); -}; - -FormGroupContextProvider.propTypes = { - children: PropTypes.node.isRequired, - controlId: PropTypes.string, - isInvalid: PropTypes.bool, - isValid: PropTypes.bool, - size: PropTypes.oneOf([ - FORM_CONTROL_SIZES.SMALL, - FORM_CONTROL_SIZES.LARGE, - ]), -}; - -FormGroupContextProvider.defaultProps = { - controlId: undefined, - isInvalid: undefined, - isValid: undefined, - size: undefined, -}; - -export { - FormGroupContext, - FormGroupContextProvider, - useFormGroupContext, -}; diff --git a/src/generic/SelectableBox/FormLabel.jsx b/src/generic/SelectableBox/FormLabel.jsx deleted file mode 100644 index 7f45bb9bc..000000000 --- a/src/generic/SelectableBox/FormLabel.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { useFormGroupContext } from './FormGroupContext'; -import { FORM_CONTROL_SIZES } from './constants'; - -const FormLabel = ({ children, isInline, ...props }) => { - const { size, isControlGroup, getLabelProps } = useFormGroupContext(); - const className = classNames( - 'pgn__form-label', - { - 'pgn__form-label-inline': isInline, - 'pgn__form-label-lg': size === FORM_CONTROL_SIZES.LARGE, - 'pgn__form-label-sm': size === FORM_CONTROL_SIZES.SMALL, - }, - props.className, - ); - const labelProps = getLabelProps({ ...props, className }); - const componentType = isControlGroup ? 'p' : 'label'; - return React.createElement(componentType, labelProps, children); -}; - -const SIZE_CHOICES = ['sm', 'lg']; - -FormLabel.propTypes = { - /** Specifies class name to append to the base element. */ - className: PropTypes.string, - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** Specifies whether the component should be displayed with inline styling. */ - isInline: PropTypes.bool, - /** Specifies size of the component. */ - size: PropTypes.oneOf(SIZE_CHOICES), -}; - -FormLabel.defaultProps = { - isInline: false, - size: undefined, - className: undefined, -}; - -export default FormLabel; diff --git a/src/generic/SelectableBox/FormRadio.jsx b/src/generic/SelectableBox/FormRadio.jsx deleted file mode 100644 index 1609f8266..000000000 --- a/src/generic/SelectableBox/FormRadio.jsx +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { useRadioSetContext } from './FormRadioSetContext'; -import { FormGroupContextProvider, useFormGroupContext } from './FormGroupContext'; -import FormLabel from './FormLabel'; -import FormControlFeedback from './FormControlFeedback'; - -const RadioControl = React.forwardRef((props, ref) => { - const { getControlProps } = useFormGroupContext(); - const { getRadioControlProps, hasRadioSetProvider } = useRadioSetContext(); - let radioProps = getControlProps({ - ...props, - className: classNames('pgn__form-radio-input', props.className), - }); - - if (hasRadioSetProvider) { - radioProps = getRadioControlProps(radioProps); - } - - const onChange = (...args) => { - // eslint-disable-next-line no-console - console.log('RadioControl.onChange called with args: ', args); - if (radioProps.onChange) { - radioProps.onChange(...args); - } - }; - - return ( - - ); -}); - -RadioControl.propTypes = { - className: PropTypes.string, -}; - -RadioControl.defaultProps = { - className: undefined, -}; - -const FormRadio = React.forwardRef(({ - children, - className, - controlClassName, - labelClassName, - description, - isInvalid, - isValid, - ...props -}, ref) => ( - -
- -
- - {children} - - {description && ( - - {description} - - )} -
-
-
-)); - -FormRadio.propTypes = { - /** Specifies id of the FormRadio component. */ - id: PropTypes.string, - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** Specifies class name to append to the base element. */ - className: PropTypes.string, - /** Specifies class name for control component. */ - controlClassName: PropTypes.string, - /** Specifies class name for label component. */ - labelClassName: PropTypes.string, - /** Specifies description to show under the radio's value. */ - description: PropTypes.node, - /** Specifies whether to display component in invalid state, this affects styling. */ - isInvalid: PropTypes.bool, - /** Specifies whether to display component in valid state, this affects styling. */ - isValid: PropTypes.bool, - /** Specifies whether the `FormRadio` is disabled. */ - disabled: PropTypes.bool, -}; - -FormRadio.defaultProps = { - id: undefined, - className: undefined, - controlClassName: undefined, - labelClassName: undefined, - description: undefined, - isInvalid: false, - isValid: false, - disabled: false, -}; - -export { RadioControl }; -export default FormRadio; diff --git a/src/generic/SelectableBox/FormRadioSet.jsx b/src/generic/SelectableBox/FormRadioSet.jsx deleted file mode 100644 index 05524ba20..000000000 --- a/src/generic/SelectableBox/FormRadioSet.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useFormGroupContext } from './FormGroupContext'; -import { FormRadioSetContextProvider } from './FormRadioSetContext'; -import FormControlSet from './FormControlSet'; - -const FormRadioSet = ({ - children, - name, - value, - defaultValue, - isInline, - onChange, - onFocus, - onBlur, - ...props -}) => { - const { getControlProps, useSetIsControlGroupEffect } = useFormGroupContext(); - useSetIsControlGroupEffect(true); - const controlProps = getControlProps(props); - return ( - - - {children} - - - ); -}; - -FormRadioSet.propTypes = { - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** A class name to append to the base element. */ - className: PropTypes.string, - /** Specifies name for the component. */ - name: PropTypes.string.isRequired, - /** Specifies values for the FormRadioSet. */ - value: PropTypes.string, - /** Specifies default values. */ - defaultValue: PropTypes.string, - /** Specifies whether the component should be displayed with inline styling. */ - isInline: PropTypes.bool, - /** Specifies onChange event handler. */ - onChange: PropTypes.func, - /** Specifies onFocus event handler. */ - onFocus: PropTypes.func, - /** Specifies onBlur event handler. */ - onBlur: PropTypes.func, -}; - -FormRadioSet.defaultProps = { - className: undefined, - value: undefined, - defaultValue: undefined, - isInline: false, - onChange: undefined, - onFocus: undefined, - onBlur: undefined, -}; - -export default FormRadioSet; diff --git a/src/generic/SelectableBox/FormRadioSetContext.jsx b/src/generic/SelectableBox/FormRadioSetContext.jsx deleted file mode 100644 index 38bbc3292..000000000 --- a/src/generic/SelectableBox/FormRadioSetContext.jsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useContext } from 'react'; -import PropTypes from 'prop-types'; -import { callAllHandlers } from './fieldUtils'; - -const identityFn = props => props; - -const FormRadioSetContext = React.createContext({ - getRadioControlProps: identityFn, - hasRadioSetProvider: false, -}); - -const useRadioSetContext = () => useContext(FormRadioSetContext); - -const FormRadioSetContextProvider = ({ - children, - name, - onBlur, - onFocus, - onChange, - value, - defaultValue, -}) => { - const handleChange = (...args) => { - // eslint-disable-next-line no-console - console.log('FormRadioSetContextProvider.handleChange called with args: ', args); - onChange(...args); - }; - const isControlled = !defaultValue && value !== undefined; - // eslint-disable-next-line no-console - console.log('FormRadioSetContextProvider | isControlled: ', isControlled); - const getRadioControlProps = (radioProps) => ({ - ...radioProps, - name, - /* istanbul ignore next */ - onBlur: radioProps.onBlur ? callAllHandlers(onBlur, radioProps.onBlur) : onBlur, - /* istanbul ignore next */ - onFocus: radioProps.onFocus ? callAllHandlers(onFocus, radioProps.onFocus) : onFocus, - /* istanbul ignore next */ - onChange: radioProps.onChange ? callAllHandlers(handleChange, radioProps.onChange) : onChange, - checked: isControlled ? value === radioProps.value : undefined, - defaultChecked: isControlled ? undefined : defaultValue === radioProps.value, - }); - // eslint-disable-next-line react/jsx-no-constructed-context-values - const contextValue = { - name, - value, - defaultValue, - getRadioControlProps, - onBlur, - onFocus, - onChange, - hasRadioSetProvider: true, - }; - return ( - - {children} - - ); -}; - -FormRadioSetContextProvider.propTypes = { - children: PropTypes.node.isRequired, - name: PropTypes.string.isRequired, - onBlur: PropTypes.func, - onFocus: PropTypes.func, - onChange: PropTypes.func, - value: PropTypes.string, - defaultValue: PropTypes.string, -}; - -FormRadioSetContextProvider.defaultProps = { - onBlur: undefined, - onFocus: undefined, - onChange: undefined, - value: undefined, - defaultValue: undefined, -}; - -export default FormRadioSetContext; -export { - useRadioSetContext, - FormRadioSetContextProvider, -}; diff --git a/src/generic/SelectableBox/FormText.jsx b/src/generic/SelectableBox/FormText.jsx deleted file mode 100644 index f6469a151..000000000 --- a/src/generic/SelectableBox/FormText.jsx +++ /dev/null @@ -1,115 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -// import Icon from '../Icon'; -// import { -// Check, Close, Cancel, CheckCircle, RadioButtonUnchecked, WarningFilled, -// } from '../../icons'; - -import { FORM_TEXT_TYPES } from './constants'; - -// const FORM_TEXT_ICONS = { -// [FORM_TEXT_TYPES.DEFAULT]: null, -// [FORM_TEXT_TYPES.VALID]: Check, -// [FORM_TEXT_TYPES.INVALID]: Close, -// [FORM_TEXT_TYPES.WARNING]: WarningFilled, -// [FORM_TEXT_TYPES.CRITERIA_EMPTY]: RadioButtonUnchecked, -// [FORM_TEXT_TYPES.CRITERIA_VALID]: CheckCircle, -// [FORM_TEXT_TYPES.CRITERIA_INVALID]: Cancel, -// }; - -const resolveTextType = ({ isInvalid, isValid }) => { - if (isValid) { - return FORM_TEXT_TYPES.VALID; - } - if (isInvalid) { - return FORM_TEXT_TYPES.INVALID; - } - return FORM_TEXT_TYPES.DEFAULT; -}; - -// const FormTextIcon = ({ type, customIcon }) => { -// if (customIcon) { -// return customIcon; -// } - -// const typeIcon = FORM_TEXT_ICONS[type]; -// if (typeIcon) { -// return ; -// } - -// return null; -// }; - -// FormTextIcon.propTypes = { -// type: PropTypes.oneOf(Object.values(FORM_TEXT_TYPES)), -// customIcon: PropTypes.node, -// }; - -// FormTextIcon.defaultProps = { -// type: undefined, -// customIcon: undefined, -// }; - -const FormText = ({ - children, type, icon, muted, hasIcon, ...props -}) => { - const className = classNames( - props.className, - 'pgn__form-text', - `pgn__form-text-${type}`, - { - 'text-muted': muted, - }, - ); - - return ( -
- {/* {hasIcon && } */} -
- {children} -
-
- ); -}; - -const FORM_TEXT_TYPE_CHOICES = [ - 'default', - 'valid', - 'invalid', - 'warning', - 'criteria-empty', - 'criteria-valid', - 'criteria-invalid', -]; - -FormText.propTypes = { - /** Specifies contents of the component. */ - children: PropTypes.node.isRequired, - /** Specifies class name to append to the base element. */ - className: PropTypes.string, - /** Specifies whether to show an icon next to the text. */ - hasIcon: PropTypes.bool, - /** Specifies text type, this affects styling. */ - type: PropTypes.oneOf(FORM_TEXT_TYPE_CHOICES), - /** Specifies icon to show, will only be shown if `hasIcon` prop is set to `true`. */ - icon: PropTypes.node, - /** Specifies whether to show text with muted styling. */ - muted: PropTypes.bool, -}; - -FormText.defaultProps = { - hasIcon: true, - type: 'default', - icon: undefined, - className: undefined, - muted: false, -}; - -export default FormText; -export { - FORM_TEXT_TYPES, - // FORM_TEXT_ICONS, - // FormTextIcon, - resolveTextType, -}; diff --git a/src/generic/SelectableBox/README.md b/src/generic/SelectableBox/README.md deleted file mode 100644 index 4ab1f417d..000000000 --- a/src/generic/SelectableBox/README.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -title: 'SelectableBox' -type: 'component' -components: -- SelectableBox -- SelectableBoxSet -categories: -- Forms -- Content -status: 'New' -designStatus: 'Done' -devStatus: 'In progress' -notes: | ---- - -A box that has selection states. It can be used as an alternative to a radio button or checkbox set. - -The ``SelectableBox`` can contain any kind of content as long as it is not clickable. In other words, there should be no clickable targets distinct from selection. - -## Basic Usage - -As ``Checkbox`` - -```jsx live -() => { - const type = 'checkbox'; - const allCheeseOptions = ['swiss', 'cheddar', 'pepperjack']; - const [checkedCheeses, { add, remove, set, clear }] = useCheckboxSetValues(['swiss']); - - const handleChange = e => { - e.target.checked ? add(e.target.value) : remove(e.target.value); - }; - - const isInvalid = () => checkedCheeses.includes('swiss'); - const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth }); - - return ( -
- - -
-

It is my first SelectableBox

-

Swiss

-
-
- - Cheddar - - -

Pepperjack

-
-
-
- ); -} -``` - -## As Radio - -```jsx live -() => { - const type = 'radio'; - const [value, setValue] = useState('green'); - const handleChange = e => setValue(e.target.value); - const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth }); - - return ( - - -
-

It is Red color

-

Select me

-
-
- -

Green

-

Leaves and grass

-
- -

Blue

-

The sky

-
-
- ); -} -``` -## As Checkbox -As ``Checkbox`` with ``isIndeterminate`` - -```jsx live -() => { - const type = 'checkbox'; - const allCheeseOptions = ['swiss', 'cheddar', 'pepperjack']; - const [checkedCheeses, { add, remove, set, clear }] = useCheckboxSetValues(['swiss']); - - const allChecked = allCheeseOptions.every(value => checkedCheeses.includes(value)); - const someChecked = allCheeseOptions.some(value => checkedCheeses.includes(value)); - const isIndeterminate = someChecked && !allChecked; - const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth }); - - const handleChange = e => { - e.target.checked ? add(e.target.value) : remove(e.target.value); - }; - - const handleCheckAllChange = ({ checked }) => { - checked ? set(allCheeseOptions) : clear(); - }; - - return ( - <> -
- - All the cheese - -
- - -
-

It is my first SelectableBox

-

Swiss

-
-
- - Cheddar - - -

Pepperjack

-
-
- - ); -} -``` - -As ``Checkbox`` with ``ariaLabelledby`` - -```jsx live -() => { - const type = 'checkbox'; - const allCheeseOptions = ['swiss', 'cheddar', 'pepperjack']; - const [checkedCheeses, { add, remove, set, clear }] = useCheckboxSetValues(['swiss']); - - const handleChange = e => { - e.target.checked ? add(e.target.value) : remove(e.target.value); - }; - - const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth }); - - return ( -
-

- Select your favorite cheese -

- - -

- Swiss -

-
- -

- Cheddar -

-
- -

- Pepperjack -

-
-
-
- ); -} -``` diff --git a/src/generic/SelectableBox/SelectableBoxSet.jsx b/src/generic/SelectableBox/SelectableBoxSet.jsx deleted file mode 100644 index a96cccab5..000000000 --- a/src/generic/SelectableBox/SelectableBoxSet.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { getInputType } from './utils'; -// import { requiredWhenNot } from '../utils/propTypes'; - -const INPUT_TYPES = [ - 'radio', - 'checkbox', -]; - -const DEFAULT_COLUMNS_NUMBER = 2; - -const SelectableBoxSet = React.forwardRef(({ - children, - name, - value, - defaultValue, - onChange, - type, - columns, - className, - ariaLabel, - ariaLabelledby, - ...props -}, ref) => { - const inputType = getInputType('SelectableBoxSet', type); - - return React.createElement( - inputType, - { - name, - value, - defaultValue, - onChange, - ref, - className: classNames( - 'pgn__selectable_box-set', - `pgn__selectable_box-set--${columns || DEFAULT_COLUMNS_NUMBER}`, - className, - ), - 'aria-label': ariaLabel, - 'aria-labelledby': ariaLabelledby, - ...props, - }, - children, - ); -}); - -SelectableBoxSet.propTypes = { - /** Specifies a name for the group of `SelectableBox`'es. */ - name: PropTypes.string.isRequired, - /** Content of the `SelectableBoxSet`. */ - children: PropTypes.node, - /** A function that receives event of the clicked `SelectableBox` and can be used to handle the value change. */ - onChange: PropTypes.func, - /** Indicates selected `SelectableBox`'es. */ - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]), - /** Specifies default values for the `SelectableBox`'es. */ - defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - /** Indicates the input type: checkbox or radio. */ - type: PropTypes.oneOf(INPUT_TYPES), - /** - * Specifies number of `SelectableBox`'es in a row. - * - * Class that is responsible for the columns number: `pgn__selectable_box-set--{columns}`. - * Max number of columns: `12`. - */ - columns: PropTypes.number, - /** A class that is be appended to the base element. */ - className: PropTypes.string, - /** - * The ID of the label for the `SelectableBoxSet`. - * - * An accessible label must be provided to the `SelectableBoxSet`. - */ - ariaLabelledby: PropTypes.string, - /** - * A label for the `SelectableBoxSet`. - * - * If not using `ariaLabelledby`, then `ariaLabel` must be provided */ - // eslint-disable-next-line react/forbid-prop-types - ariaLabel: PropTypes.any, // requiredWhenNot(PropTypes.string, 'ariaLabelledby'), -}; - -SelectableBoxSet.defaultProps = { - children: undefined, - onChange: () => {}, - value: undefined, - defaultValue: undefined, - type: 'radio', - columns: DEFAULT_COLUMNS_NUMBER, - className: undefined, - ariaLabelledby: undefined, - ariaLabel: undefined, -}; - -export default SelectableBoxSet; diff --git a/src/generic/SelectableBox/_variables.scss b/src/generic/SelectableBox/_variables.scss deleted file mode 100644 index b7d4e114d..000000000 --- a/src/generic/SelectableBox/_variables.scss +++ /dev/null @@ -1,5 +0,0 @@ -$selectable-box-padding: 1rem !default; -$selectable-box-border-radius: .25rem !default; -$selectable-box-space: .75rem !default; -$min-cols-number: 1 !default; -$max-cols-number: 12 !default; diff --git a/src/generic/SelectableBox/constants.js b/src/generic/SelectableBox/constants.js deleted file mode 100644 index 68abdda93..000000000 --- a/src/generic/SelectableBox/constants.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -const FORM_CONTROL_SIZES = { - SMALL: 'sm', - LARGE: 'lg', -}; - -const FORM_TEXT_TYPES = { - DEFAULT: 'default', - VALID: 'valid', - INVALID: 'invalid', - WARNING: 'warning', - CRITERIA_EMPTY: 'criteria-empty', - CRITERIA_VALID: 'criteria-valid', - CRITERIA_INVALID: 'criteria-invalid', -}; - -export { FORM_CONTROL_SIZES, FORM_TEXT_TYPES }; diff --git a/src/generic/SelectableBox/fieldUtils.js b/src/generic/SelectableBox/fieldUtils.js deleted file mode 100644 index 5c0d980a1..000000000 --- a/src/generic/SelectableBox/fieldUtils.js +++ /dev/null @@ -1,70 +0,0 @@ -import classNames from 'classnames'; -import { useState, useEffect } from 'react'; -import newId from './newId'; - -const omitUndefinedProperties = (obj = {}) => Object.entries(obj) - .reduce((acc, [key, value]) => { - if (value !== undefined) { - acc[key] = value; - } - return acc; - }, {}); - -const callAllHandlers = (...handlers) => { - const unifiedEventHandler = (event) => { - handlers - .filter(handler => typeof handler === 'function') - .forEach(handler => handler(event)); - }; - return unifiedEventHandler; -}; - -const useHasValue = ({ defaultValue, value }) => { - const [hasUncontrolledValue, setHasUncontrolledValue] = useState(!!defaultValue || defaultValue === 0); - const hasValue = !!value || value === 0 || hasUncontrolledValue; - const handleInputEvent = (e) => setHasUncontrolledValue(e.target.value); - return [hasValue, handleInputEvent]; -}; - -const useIdList = (uniqueIdPrefix, initialList) => { - const [idList, setIdList] = useState(initialList || []); - const addId = (idToAdd) => { - setIdList(oldIdList => [...oldIdList, idToAdd]); - return idToAdd; - }; - const getNewId = () => { - const idToAdd = newId(`${uniqueIdPrefix}-`); - return addId(idToAdd); - }; - const removeId = (idToRemove) => { - setIdList(oldIdList => oldIdList.filter(id => id !== idToRemove)); - }; - - const useRegisteredId = (explicitlyRegisteredId) => { - const [registeredId, setRegisteredId] = useState(explicitlyRegisteredId); - useEffect(() => { - if (explicitlyRegisteredId) { - addId(explicitlyRegisteredId); - } else if (!registeredId) { - setRegisteredId(getNewId(uniqueIdPrefix)); - } - return () => removeId(registeredId); - }, [registeredId, explicitlyRegisteredId]); - return registeredId; - }; - - return [idList, useRegisteredId]; -}; - -const mergeAttributeValues = (...values) => { - const mergedValues = classNames(values); - return mergedValues || undefined; -}; - -export { - callAllHandlers, - useHasValue, - mergeAttributeValues, - useIdList, - omitUndefinedProperties, -}; diff --git a/src/generic/SelectableBox/index.jsx b/src/generic/SelectableBox/index.jsx deleted file mode 100644 index d5e1978ff..000000000 --- a/src/generic/SelectableBox/index.jsx +++ /dev/null @@ -1,115 +0,0 @@ -import React, { useRef, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import SelectableBoxSet from './SelectableBoxSet'; -import { useCheckboxSetContext } from './FormCheckboxSetContext'; -import { useRadioSetContext } from './FormRadioSetContext'; -import { getInputType } from './utils'; - -const INPUT_TYPES = [ - 'radio', - 'checkbox', -]; - -const SelectableBox = React.forwardRef(({ - type, - value, - checked, - children, - isIndeterminate, - isInvalid, - onClick, - onFocus, - inputHidden, - className, - ...props -}, ref) => { - const inputType = getInputType('SelectableBox', type); - const { value: radioValue } = useRadioSetContext(); - const { value: checkboxValues = [] } = useCheckboxSetContext(); - - const isChecked = () => { - switch (type) { - case 'radio': - return radioValue === value; - case 'checkbox': - return checkboxValues.includes(value); - default: - return radioValue === value; - } - }; - - const inputRef = useRef(null); - const input = React.createElement(inputType, { - value, - checked, - hidden: inputHidden, - ref: inputRef, - tabIndex: -1, - onChange: () => {}, - ...(type === 'checkbox' ? { ...props, isIndeterminate } : { ...props }), - }, null); - - useEffect(() => { - if (onClick && inputRef.current) { - inputRef.current.onclick = () => onClick(inputRef.current); - } - }, [onClick]); - - return ( -
inputRef.current.click()} - onClick={() => inputRef.current.click()} - onFocus={onFocus} - className={classNames('pgn__selectable_box', className, { - 'pgn__selectable_box-active': isChecked() || checked, - 'pgn__selectable_box-invalid': isInvalid, - })} - tabIndex={0} - ref={ref} - {...props} - > - {input} - {children} -
- ); -}); - -SelectableBox.propTypes = { - /** Content of the `SelectableBox`. */ - children: PropTypes.node.isRequired, - /** A value that is passed to the input tag. */ - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - /** Controls whether `SelectableBox` is checked. */ - checked: PropTypes.bool, - /** Indicates the input type: checkbox or radio. */ - type: PropTypes.oneOf(INPUT_TYPES), - /** Function that is called when the `SelectableBox` is clicked. */ - onClick: PropTypes.func, - /** Function that is called when the `SelectableBox` is focused. */ - onFocus: PropTypes.func, - /** Controls display of the input (checkbox or radio button) on the `SelectableBox`. */ - inputHidden: PropTypes.bool, - /** Indicates a state for the 'checkbox' `type` when `SelectableBox` is neither checked nor unchecked. */ - isIndeterminate: PropTypes.bool, - /** Adds errors styles to the `SelectableBox`. */ - isInvalid: PropTypes.bool, - /** A class that is appended to the base element. */ - className: PropTypes.string, -}; - -SelectableBox.defaultProps = { - value: undefined, - checked: false, - type: 'radio', - onClick: () => {}, - onFocus: () => {}, - inputHidden: true, - isIndeterminate: false, - isInvalid: false, - className: undefined, -}; - -SelectableBox.Set = SelectableBoxSet; -export default SelectableBox; diff --git a/src/generic/SelectableBox/index.scss b/src/generic/SelectableBox/index.scss deleted file mode 100644 index 1038286af..000000000 --- a/src/generic/SelectableBox/index.scss +++ /dev/null @@ -1,54 +0,0 @@ -@import "variables"; - -.pgn__selectable_box-set { - display: grid; - grid-auto-rows: 1fr; - grid-gap: $selectable-box-space; - - @for $i from $min-cols-number through $max-cols-number { - &.pgn__selectable_box-set--#{$i} { - grid-template-columns: repeat(#{$i}, 1fr); - } - } - - & > * + * { - margin: 0; - } -} - -.pgn__selectable_box { - position: relative; - height: 100%; - padding: $selectable-box-padding; - box-shadow: $level-1-box-shadow; - border-radius: $selectable-box-border-radius; - text-align: start; - background: $white; - - &:focus-visible { - outline: 1px solid $primary-700; - } - - .pgn__form-radio, - .pgn__form-checkbox { - position: absolute; - top: $selectable-box-padding; - inset-inline-end: $selectable-box-padding; - - input { - margin-inline-end: 0; - } - } - - * { - pointer-events: none; - } -} - -.pgn__selectable_box-active { - outline: 2px solid $primary-500; -} - -.pgn__selectable_box-invalid { - outline: 2px solid $danger-300; -} diff --git a/src/generic/SelectableBox/newId.js b/src/generic/SelectableBox/newId.js deleted file mode 100644 index 9055c9e31..000000000 --- a/src/generic/SelectableBox/newId.js +++ /dev/null @@ -1,8 +0,0 @@ -let lastId = 0; - -const newId = (prefix = 'id') => { - lastId += 1; - return `${prefix}${lastId}`; -}; - -export default newId; diff --git a/src/generic/SelectableBox/tests/SelectableBox.test.jsx b/src/generic/SelectableBox/tests/SelectableBox.test.jsx deleted file mode 100644 index 226c5a9f8..000000000 --- a/src/generic/SelectableBox/tests/SelectableBox.test.jsx +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import renderer from 'react-test-renderer'; - -import SelectableBox from '..'; - -const checkboxType = 'checkbox'; -const checkboxText = 'SelectableCheckbox'; - -const radioType = 'radio'; -const radioText = 'SelectableRadio'; - -const SelectableCheckbox = (props) => {checkboxText}; - -const SelectableRadio = (props) => {radioText}; - -describe('', () => { - describe('correct rendering', () => { - it('renders without props', () => { - const tree = renderer.create(( - SelectableBox - )).toJSON(); - expect(tree).toMatchSnapshot(); - }); - it('correct render when type prop is changed', () => { - const { rerender } = render(); - const checkboxControl = screen.getByText(radioText); - expect(checkboxControl).toBeTruthy(); - rerender(); - const radioControl = screen.getByText(radioText); - expect(radioControl).toBeTruthy(); - }); - it('renders with radio input type if neither checkbox nor radio is passed', () => { - // Mock the `console.error` is intentional because an invalid `type` prop - // with `wrongType` specified for `ForwardRef` expects one of the ['radio','flag'] parameters. - // eslint-disable-next-line no-console - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - render(); - const selectableBox = screen.getByRole('button'); - expect(selectableBox).toBeTruthy(); - consoleErrorSpy.mockRestore(); - }); - it('renders with checkbox input type', () => { - render(); - const selectableBox = screen.getByRole('button'); - expect(selectableBox).toBeTruthy(); - }); - it('renders with radio input type', () => { - render(); - const selectableBox = screen.getByRole('button'); - expect(selectableBox).toBeTruthy(); - }); - it('renders with correct children', () => { - render(); - const selectableBox = screen.getByText(radioText); - expect(selectableBox).toBeTruthy(); - }); - it('renders with correct class', () => { - const className = 'myClass'; - render(); - const selectableBox = screen.getByRole('button'); - expect(selectableBox.classList.contains(className)).toEqual(true); - }); - it('renders as active when checked is passed', () => { - render(); - const selectableBox = screen.getByRole('button'); - const inputElement = screen.getByRole('radio', { hidden: true }); - expect(selectableBox.classList.contains('pgn__selectable_box-active')).toEqual(true); - expect(inputElement.checked).toEqual(true); - }); - it('renders as invalid when isInvalid is passed', () => { - render(); - const selectableBox = screen.getByRole('button'); - expect(selectableBox.classList.contains('pgn__selectable_box-invalid')).toEqual(true); - }); - it('renders with on click event when onClick is passed', async () => { - const onClickSpy = jest.fn(); - render(); - const selectableBox = screen.getByRole('button'); - await userEvent.click(selectableBox); - expect(onClickSpy).toHaveBeenCalledTimes(1); - }); - it('renders with on key press event when onClick is passed', async () => { - const onClickSpy = jest.fn(); - render(); - const selectableBox = screen.getByRole('button'); - selectableBox.focus(); - await userEvent.keyboard('{enter}'); - expect(onClickSpy).toHaveBeenCalledTimes(1); - }); - it('renders with hidden input when inputHidden is passed', () => { - const { rerender } = render(); - const inputElement = screen.getByRole('checkbox', { hidden: true }); - expect(inputElement.getAttribute('hidden')).toEqual(''); - rerender(); - expect(inputElement.getAttribute('hidden')).toBeNull(); - }); - }); - describe('correct interactions', () => { - it('correct checkbox state change when checked is changed', () => { - const { rerender } = render(); - const checkbox = screen.getByRole('button'); - expect(checkbox.className).not.toContain('pgn__selectable_box-active'); - rerender(); - expect(checkbox.className).toContain('pgn__selectable_box-active'); - }); - it('correct radio state change when checked is changed', () => { - const { rerender } = render(); - const radio = screen.getByRole('button'); - expect(radio.className).toContain('pgn__selectable_box-active'); - rerender(); - expect(radio.className).toContain('pgn__selectable_box-active'); - }); - it('ref is passed to onClick function', () => { - let inputRef; - const onClick = (ref) => { inputRef = ref; }; - render(); - const radio = screen.getByRole('button'); - userEvent.click(radio); - expect(inputRef).not.toBeFalsy(); - }); - }); -}); diff --git a/src/generic/SelectableBox/tests/SelectableBoxSet.test.jsx b/src/generic/SelectableBox/tests/SelectableBoxSet.test.jsx deleted file mode 100644 index cf3db6f3c..000000000 --- a/src/generic/SelectableBox/tests/SelectableBoxSet.test.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; -import userEvent from '@testing-library/user-event'; -import SelectableBox from '..'; - -const boxText = (text) => `SelectableBox${text}`; - -const checkboxType = 'checkbox'; -const checkboxText = (text) => `SelectableCheckbox${text}`; - -const radioType = 'radio'; -const radioText = (text) => `SelectableRadio${text}`; - -const ariaLabel = 'test-default-label'; - -const SelectableBoxSet = (props) => ( - - {boxText(1)} - {boxText(2)} - {boxText(3)} - -); - -const SelectableCheckboxSet = (props) => ( - - {checkboxText(1)} - {checkboxText(2)} - {checkboxText(3)} - -); - -const SelectableRadioSet = (props) => ( - - {radioText(1)} - {radioText(2)} - {radioText(3)} - -); - -describe('', () => { - describe('correct rendering', () => { - it('renders without props', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - it('forwards props', () => { - render(()); - expect(screen.getByTestId('test-radio-set-name')).toBeInTheDocument(); - }); - it('correct render when type prop is changed', () => { - const { rerender } = render(); - expect(screen.getByTestId('radio-set')).toBeInTheDocument(); - rerender(); - expect(screen.getByTestId('radio-set')).toBeInTheDocument(); - rerender(); - expect(screen.getByTestId('checkbox-set')).toBeInTheDocument(); - }); - it('renders with children', () => { - render( - {checkboxText(1)}, - ); - expect(screen.getByText(checkboxText(1))).toBeInTheDocument(); - }); - it('renders with on change event', async () => { - const onChangeSpy = jest.fn(); - render(); - const checkbox = screen.getByRole('button', { name: checkboxText(1) }); - await userEvent.click(checkbox); - expect(onChangeSpy).toHaveBeenCalledTimes(1); - }); - it('renders with checkbox type', () => { - render(); - expect(screen.getByTestId('checkbox-set')).toBeInTheDocument(); - }); - it('renders with radio type if neither checkbox nor radio is passed', () => { - render(); - expect(screen.getByTestId('radio-set')).toBeInTheDocument(); - }); - it('renders with radio type', () => { - render(); - expect(screen.getByTestId('radio-set')).toBeInTheDocument(); - }); - it('renders with correct number of columns', () => { - const columns = 10; - render(); - const selectableBoxSet = screen.getByTestId('selectable-box-set'); - expect(selectableBoxSet).toHaveClass(`pgn__selectable_box-set--${columns}`); - }); - it('renders with an aria-label attribute', () => { - render(()); - expect(screen.getByLabelText('test-radio-set-label')).toBeInTheDocument(); - }); - it('renders with an aria-labelledby attribute', () => { - render(( - <> -

Radio Set Label text

- - - )); - expect(screen.getByLabelText('Radio Set Label text')).toBeInTheDocument(); - }); - }); -}); diff --git a/src/generic/SelectableBox/tests/__snapshots__/SelectableBox.test.jsx.snap b/src/generic/SelectableBox/tests/__snapshots__/SelectableBox.test.jsx.snap deleted file mode 100644 index 96676cabf..000000000 --- a/src/generic/SelectableBox/tests/__snapshots__/SelectableBox.test.jsx.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` correct rendering renders without props 1`] = ` -
- - SelectableBox -
-`; diff --git a/src/generic/SelectableBox/tests/__snapshots__/SelectableBoxSet.test.jsx.snap b/src/generic/SelectableBox/tests/__snapshots__/SelectableBoxSet.test.jsx.snap deleted file mode 100644 index 4c9a0ab8f..000000000 --- a/src/generic/SelectableBox/tests/__snapshots__/SelectableBoxSet.test.jsx.snap +++ /dev/null @@ -1,57 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` correct rendering renders without props 1`] = ` -
-
-
- - SelectableRadio1 -
-
- - SelectableRadio2 -
-
- - SelectableRadio3 -
-
-
-`; diff --git a/src/generic/SelectableBox/utils.js b/src/generic/SelectableBox/utils.js deleted file mode 100644 index 16883a772..000000000 --- a/src/generic/SelectableBox/utils.js +++ /dev/null @@ -1,27 +0,0 @@ -import { CheckboxControl } from './FormCheckbox'; -import { RadioControl } from './FormRadio'; -import FormRadioSet from './FormRadioSet'; -import FormCheckboxSet from './FormCheckboxSet'; - -// eslint-disable-next-line import/prefer-default-export,consistent-return -export const getInputType = (component, type) => { - if (component === 'SelectableBox') { - switch (type) { - case 'radio': - return RadioControl; - case 'checkbox': - return CheckboxControl; - default: - return RadioControl; - } - } else if (component === 'SelectableBoxSet') { - switch (type) { - case 'radio': - return FormRadioSet; - case 'checkbox': - return FormCheckboxSet; - default: - return FormRadioSet; - } - } -};