From b5a287639d83d43a47b3027f2d0284489f66fd37 Mon Sep 17 00:00:00 2001
From: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com>
Date: Mon, 11 Mar 2024 17:09:58 -0400
Subject: [PATCH] Fix SelectableBox problems (#886)
Due to a bug in the SelectableBox component, selecting values was not possible in different components throughout this MFE.
This fixes the Gallery and the Select Problem Types components by updating the FLCC version and replacing the SelectableBox copy with an import from FLCC.
For a full bug description see #880.
---
package-lock.json | 8 +-
package.json | 2 +-
.../SortAndFilterModal.jsx | 3 +-
src/generic/SelectableBox/FormCheckbox.jsx | 145 ------------
src/generic/SelectableBox/FormCheckboxSet.jsx | 68 ------
.../SelectableBox/FormCheckboxSetContext.jsx | 77 -------
.../SelectableBox/FormControlFeedback.jsx | 56 -----
src/generic/SelectableBox/FormControlSet.jsx | 40 ----
.../SelectableBox/FormGroupContext.jsx | 121 ----------
src/generic/SelectableBox/FormLabel.jsx | 42 ----
src/generic/SelectableBox/FormRadio.jsx | 112 ---------
src/generic/SelectableBox/FormRadioSet.jsx | 68 ------
.../SelectableBox/FormRadioSetContext.jsx | 83 -------
src/generic/SelectableBox/FormText.jsx | 115 ----------
src/generic/SelectableBox/README.md | 215 ------------------
.../SelectableBox/SelectableBoxSet.jsx | 98 --------
src/generic/SelectableBox/_variables.scss | 5 -
src/generic/SelectableBox/constants.js | 17 --
src/generic/SelectableBox/fieldUtils.js | 70 ------
src/generic/SelectableBox/index.jsx | 115 ----------
src/generic/SelectableBox/index.scss | 54 -----
src/generic/SelectableBox/newId.js | 8 -
.../tests/SelectableBox.test.jsx | 124 ----------
.../tests/SelectableBoxSet.test.jsx | 107 ---------
.../__snapshots__/SelectableBox.test.jsx.snap | 22 --
.../SelectableBoxSet.test.jsx.snap | 57 -----
src/generic/SelectableBox/utils.js | 27 ---
27 files changed, 7 insertions(+), 1852 deletions(-)
delete mode 100644 src/generic/SelectableBox/FormCheckbox.jsx
delete mode 100644 src/generic/SelectableBox/FormCheckboxSet.jsx
delete mode 100644 src/generic/SelectableBox/FormCheckboxSetContext.jsx
delete mode 100644 src/generic/SelectableBox/FormControlFeedback.jsx
delete mode 100644 src/generic/SelectableBox/FormControlSet.jsx
delete mode 100644 src/generic/SelectableBox/FormGroupContext.jsx
delete mode 100644 src/generic/SelectableBox/FormLabel.jsx
delete mode 100644 src/generic/SelectableBox/FormRadio.jsx
delete mode 100644 src/generic/SelectableBox/FormRadioSet.jsx
delete mode 100644 src/generic/SelectableBox/FormRadioSetContext.jsx
delete mode 100644 src/generic/SelectableBox/FormText.jsx
delete mode 100644 src/generic/SelectableBox/README.md
delete mode 100644 src/generic/SelectableBox/SelectableBoxSet.jsx
delete mode 100644 src/generic/SelectableBox/_variables.scss
delete mode 100644 src/generic/SelectableBox/constants.js
delete mode 100644 src/generic/SelectableBox/fieldUtils.js
delete mode 100644 src/generic/SelectableBox/index.jsx
delete mode 100644 src/generic/SelectableBox/index.scss
delete mode 100644 src/generic/SelectableBox/newId.js
delete mode 100644 src/generic/SelectableBox/tests/SelectableBox.test.jsx
delete mode 100644 src/generic/SelectableBox/tests/SelectableBoxSet.test.jsx
delete mode 100644 src/generic/SelectableBox/tests/__snapshots__/SelectableBox.test.jsx.snap
delete mode 100644 src/generic/SelectableBox/tests/__snapshots__/SelectableBoxSet.test.jsx.snap
delete mode 100644 src/generic/SelectableBox/utils.js
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`] = `
-
-`;
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;
- }
- }
-};