From a6ff5eee45215effb3344cb5d631a27a7c0369aa Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 22 Sep 2023 19:34:01 +0200 Subject: refactor(components): rewrite form components --- .../atoms/forms/boolean-field.module.scss | 5 - .../atoms/forms/boolean-field.stories.tsx | 175 -------------- src/components/atoms/forms/boolean-field.test.tsx | 60 ----- src/components/atoms/forms/boolean-field.tsx | 44 ---- src/components/atoms/forms/field.stories.tsx | 257 --------------------- src/components/atoms/forms/field.test.tsx | 30 --- src/components/atoms/forms/field.tsx | 111 --------- .../fields/boolean-field/boolean-field.module.scss | 7 + .../fields/boolean-field/boolean-field.stories.tsx | 175 ++++++++++++++ .../fields/boolean-field/boolean-field.test.tsx | 36 +++ .../forms/fields/boolean-field/boolean-field.tsx | 86 +++++++ .../atoms/forms/fields/boolean-field/index.ts | 1 + .../atoms/forms/fields/checkbox/checkbox.test.tsx | 33 +++ .../atoms/forms/fields/checkbox/checkbox.tsx | 13 ++ .../atoms/forms/fields/checkbox/index.ts | 1 + .../atoms/forms/fields/fields.module.scss | 50 ++++ src/components/atoms/forms/fields/index.ts | 6 + src/components/atoms/forms/fields/input/index.ts | 1 + .../atoms/forms/fields/input/input.stories.tsx | 237 +++++++++++++++++++ .../atoms/forms/fields/input/input.test.tsx | 34 +++ src/components/atoms/forms/fields/input/input.tsx | 72 ++++++ src/components/atoms/forms/fields/radio/index.ts | 1 + .../atoms/forms/fields/radio/radio.test.tsx | 28 +++ src/components/atoms/forms/fields/radio/radio.tsx | 13 ++ src/components/atoms/forms/fields/select/index.ts | 1 + .../atoms/forms/fields/select/select.stories.tsx | 143 ++++++++++++ .../atoms/forms/fields/select/select.test.tsx | 43 ++++ .../atoms/forms/fields/select/select.tsx | 76 ++++++ .../atoms/forms/fields/text-area/index.ts | 1 + .../forms/fields/text-area/text-area.stories.tsx | 136 +++++++++++ .../forms/fields/text-area/text-area.test.tsx | 20 ++ .../atoms/forms/fields/text-area/text-area.tsx | 69 ++++++ .../atoms/forms/fieldset/fieldset.module.scss | 17 ++ .../atoms/forms/fieldset/fieldset.stories.tsx | 63 +++++ .../atoms/forms/fieldset/fieldset.test.tsx | 35 +++ src/components/atoms/forms/fieldset/fieldset.tsx | 68 ++++++ src/components/atoms/forms/fieldset/index.ts | 1 + src/components/atoms/forms/form.test.tsx | 13 -- src/components/atoms/forms/form.tsx | 80 ------- src/components/atoms/forms/form/form.test.tsx | 13 ++ src/components/atoms/forms/form/form.tsx | 28 +++ src/components/atoms/forms/form/index.ts | 1 + src/components/atoms/forms/forms.module.scss | 53 ----- src/components/atoms/forms/index.ts | 6 +- src/components/atoms/forms/label.module.scss | 18 -- src/components/atoms/forms/label.stories.tsx | 104 --------- src/components/atoms/forms/label.test.tsx | 9 - src/components/atoms/forms/label.tsx | 40 ---- src/components/atoms/forms/label/index.ts | 1 + src/components/atoms/forms/label/label.module.scss | 18 ++ src/components/atoms/forms/label/label.stories.tsx | 119 ++++++++++ src/components/atoms/forms/label/label.test.tsx | 9 + src/components/atoms/forms/label/label.tsx | 62 +++++ src/components/atoms/forms/legend/index.ts | 1 + .../atoms/forms/legend/legend.module.scss | 6 + .../atoms/forms/legend/legend.stories.tsx | 27 +++ src/components/atoms/forms/legend/legend.test.tsx | 17 ++ src/components/atoms/forms/legend/legend.tsx | 21 ++ src/components/atoms/forms/select.stories.tsx | 151 ------------ src/components/atoms/forms/select.test.tsx | 30 --- src/components/atoms/forms/select.tsx | 79 ------- src/components/atoms/index.ts | 1 + src/components/atoms/modal/index.ts | 1 + src/components/atoms/modal/modal.module.scss | 66 ++++++ src/components/atoms/modal/modal.stories.tsx | 59 +++++ src/components/atoms/modal/modal.test.tsx | 25 ++ src/components/atoms/modal/modal.tsx | 49 ++++ 67 files changed, 1994 insertions(+), 1262 deletions(-) delete mode 100644 src/components/atoms/forms/boolean-field.module.scss delete mode 100644 src/components/atoms/forms/boolean-field.stories.tsx delete mode 100644 src/components/atoms/forms/boolean-field.test.tsx delete mode 100644 src/components/atoms/forms/boolean-field.tsx delete mode 100644 src/components/atoms/forms/field.stories.tsx delete mode 100644 src/components/atoms/forms/field.test.tsx delete mode 100644 src/components/atoms/forms/field.tsx create mode 100644 src/components/atoms/forms/fields/boolean-field/boolean-field.module.scss create mode 100644 src/components/atoms/forms/fields/boolean-field/boolean-field.stories.tsx create mode 100644 src/components/atoms/forms/fields/boolean-field/boolean-field.test.tsx create mode 100644 src/components/atoms/forms/fields/boolean-field/boolean-field.tsx create mode 100644 src/components/atoms/forms/fields/boolean-field/index.ts create mode 100644 src/components/atoms/forms/fields/checkbox/checkbox.test.tsx create mode 100644 src/components/atoms/forms/fields/checkbox/checkbox.tsx create mode 100644 src/components/atoms/forms/fields/checkbox/index.ts create mode 100644 src/components/atoms/forms/fields/fields.module.scss create mode 100644 src/components/atoms/forms/fields/index.ts create mode 100644 src/components/atoms/forms/fields/input/index.ts create mode 100644 src/components/atoms/forms/fields/input/input.stories.tsx create mode 100644 src/components/atoms/forms/fields/input/input.test.tsx create mode 100644 src/components/atoms/forms/fields/input/input.tsx create mode 100644 src/components/atoms/forms/fields/radio/index.ts create mode 100644 src/components/atoms/forms/fields/radio/radio.test.tsx create mode 100644 src/components/atoms/forms/fields/radio/radio.tsx create mode 100644 src/components/atoms/forms/fields/select/index.ts create mode 100644 src/components/atoms/forms/fields/select/select.stories.tsx create mode 100644 src/components/atoms/forms/fields/select/select.test.tsx create mode 100644 src/components/atoms/forms/fields/select/select.tsx create mode 100644 src/components/atoms/forms/fields/text-area/index.ts create mode 100644 src/components/atoms/forms/fields/text-area/text-area.stories.tsx create mode 100644 src/components/atoms/forms/fields/text-area/text-area.test.tsx create mode 100644 src/components/atoms/forms/fields/text-area/text-area.tsx create mode 100644 src/components/atoms/forms/fieldset/fieldset.module.scss create mode 100644 src/components/atoms/forms/fieldset/fieldset.stories.tsx create mode 100644 src/components/atoms/forms/fieldset/fieldset.test.tsx create mode 100644 src/components/atoms/forms/fieldset/fieldset.tsx create mode 100644 src/components/atoms/forms/fieldset/index.ts delete mode 100644 src/components/atoms/forms/form.test.tsx delete mode 100644 src/components/atoms/forms/form.tsx create mode 100644 src/components/atoms/forms/form/form.test.tsx create mode 100644 src/components/atoms/forms/form/form.tsx create mode 100644 src/components/atoms/forms/form/index.ts delete mode 100644 src/components/atoms/forms/forms.module.scss delete mode 100644 src/components/atoms/forms/label.module.scss delete mode 100644 src/components/atoms/forms/label.stories.tsx delete mode 100644 src/components/atoms/forms/label.test.tsx delete mode 100644 src/components/atoms/forms/label.tsx create mode 100644 src/components/atoms/forms/label/index.ts create mode 100644 src/components/atoms/forms/label/label.module.scss create mode 100644 src/components/atoms/forms/label/label.stories.tsx create mode 100644 src/components/atoms/forms/label/label.test.tsx create mode 100644 src/components/atoms/forms/label/label.tsx create mode 100644 src/components/atoms/forms/legend/index.ts create mode 100644 src/components/atoms/forms/legend/legend.module.scss create mode 100644 src/components/atoms/forms/legend/legend.stories.tsx create mode 100644 src/components/atoms/forms/legend/legend.test.tsx create mode 100644 src/components/atoms/forms/legend/legend.tsx delete mode 100644 src/components/atoms/forms/select.stories.tsx delete mode 100644 src/components/atoms/forms/select.test.tsx delete mode 100644 src/components/atoms/forms/select.tsx create mode 100644 src/components/atoms/modal/index.ts create mode 100644 src/components/atoms/modal/modal.module.scss create mode 100644 src/components/atoms/modal/modal.stories.tsx create mode 100644 src/components/atoms/modal/modal.test.tsx create mode 100644 src/components/atoms/modal/modal.tsx (limited to 'src/components/atoms') diff --git a/src/components/atoms/forms/boolean-field.module.scss b/src/components/atoms/forms/boolean-field.module.scss deleted file mode 100644 index f299ddd..0000000 --- a/src/components/atoms/forms/boolean-field.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -@use "../../../styles/abstracts/mixins" as mix; - -.hidden { - @include mix.visually-hidden; -} diff --git a/src/components/atoms/forms/boolean-field.stories.tsx b/src/components/atoms/forms/boolean-field.stories.tsx deleted file mode 100644 index 3d6f8c3..0000000 --- a/src/components/atoms/forms/boolean-field.stories.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { useState } from 'react'; -import { BooleanField } from './boolean-field'; - -/** - * BooleanField - Storybook Meta - */ -export default { - title: 'Atoms/Forms', - component: BooleanField, - args: { - hidden: false, - }, - argTypes: { - 'aria-labelledby': { - control: { - type: 'text', - }, - description: 'One or more ids that refers to the field name.', - table: { - category: 'Accessibility', - }, - type: { - name: 'string', - required: false, - }, - }, - checked: { - control: { - type: null, - }, - description: 'The field state: true if checked.', - type: { - name: 'boolean', - required: true, - }, - }, - className: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the field.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - hidden: { - control: { - type: 'boolean', - }, - description: 'Define if the field should be visually hidden.', - table: { - category: 'Options', - defaultValue: { summary: false }, - }, - type: { - name: 'boolean', - required: false, - }, - }, - id: { - control: { - type: 'text', - }, - description: 'The field id.', - type: { - name: 'string', - required: true, - }, - }, - name: { - control: { - type: 'text', - }, - description: 'The field name.', - type: { - name: 'string', - required: true, - }, - }, - onChange: { - control: { - type: null, - }, - description: 'A callback function to handle field state change.', - table: { - category: 'Events', - }, - type: { - name: 'function', - required: true, - }, - }, - onClick: { - control: { - type: null, - }, - description: 'A callback function to handle click on field.', - table: { - category: 'Events', - }, - type: { - name: 'function', - required: false, - }, - }, - type: { - control: { - type: 'select', - }, - description: 'The field type. Either checkbox or radio.', - options: ['checkbox', 'radio'], - type: { - name: 'string', - required: true, - }, - }, - value: { - control: { - type: 'text', - }, - description: 'The field value.', - type: { - name: 'string', - required: true, - }, - }, - }, -} as ComponentMeta; - -const Template: ComponentStory = ({ - checked, - onChange: _onChange, - ...args -}) => { - const [isChecked, setIsChecked] = useState(checked); - - return ( - { - setIsChecked(!isChecked); - }} - {...args} - /> - ); -}; - -/** - * Checkbox Story - */ -export const Checkbox = Template.bind({}); -Checkbox.args = { - id: 'checkbox', - checked: false, - name: 'checkbox', - type: 'checkbox', - value: 'checkbox', -}; - -/** - * Radio Story - */ -export const Radio = Template.bind({}); -Radio.args = { - id: 'radio', - checked: false, - name: 'radio', - type: 'radio', - value: 'radio', -}; diff --git a/src/components/atoms/forms/boolean-field.test.tsx b/src/components/atoms/forms/boolean-field.test.tsx deleted file mode 100644 index 503d1ce..0000000 --- a/src/components/atoms/forms/boolean-field.test.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { render, screen } from '../../../../tests/utils'; -import { BooleanField } from './boolean-field'; - -describe('BooleanField', () => { - it('renders an unchecked checkbox', () => { - render( - null} - type="checkbox" - value="checkbox" - /> - ); - expect(screen.getByRole('checkbox')).not.toBeChecked(); - }); - - it('renders a checked checkbox', () => { - render( - null} - type="checkbox" - value="checkbox" - /> - ); - expect(screen.getByRole('checkbox')).toBeChecked(); - }); - - it('renders an unchecked radio', () => { - render( - null} - type="radio" - value="radio" - /> - ); - expect(screen.getByRole('radio')).not.toBeChecked(); - }); - - it('renders a checked radio', () => { - render( - null} - type="radio" - value="radio" - /> - ); - expect(screen.getByRole('radio')).toBeChecked(); - }); -}); diff --git a/src/components/atoms/forms/boolean-field.tsx b/src/components/atoms/forms/boolean-field.tsx deleted file mode 100644 index 8f33a42..0000000 --- a/src/components/atoms/forms/boolean-field.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { FC, InputHTMLAttributes } from 'react'; -import styles from './boolean-field.module.scss'; - -export type BooleanFieldProps = Omit< - InputHTMLAttributes, - 'checked' | 'hidden' | 'name' | 'type' | 'value' -> & { - /** - * True if the field should be checked. - */ - checked: boolean; - /** - * True if the field should be visually hidden. Default: false. - */ - hidden?: boolean; - /** - * Field name attribute. - */ - name: string; - /** - * The input type. - */ - type: 'checkbox' | 'radio'; - /** - * Field name attribute. - */ - value: string; -}; - -/** - * BooleanField component - * - * Render a checkbox or a radio input type. - */ -export const BooleanField: FC = ({ - className = '', - hidden = false, - ...props -}) => { - const modifier = hidden ? 'hidden' : ''; - const inputClass = `${styles[modifier]} ${className}`; - - return ; -}; diff --git a/src/components/atoms/forms/field.stories.tsx b/src/components/atoms/forms/field.stories.tsx deleted file mode 100644 index 27fd3be..0000000 --- a/src/components/atoms/forms/field.stories.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { useState } from 'react'; -import { Field } from './field'; - -/** - * Field - Storybook Meta - */ -export default { - title: 'Atoms/Forms/Fields', - component: Field, - args: { - disabled: false, - required: false, - }, - argTypes: { - 'aria-labelledby': { - control: { - type: 'text', - }, - description: 'One or more ids that refers to the field name.', - table: { - category: 'Accessibility', - }, - type: { - name: 'string', - required: false, - }, - }, - className: { - control: { - type: 'text', - }, - description: 'Add classnames to the field.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - disabled: { - control: { - type: 'boolean', - }, - description: 'Field state: either enabled or disabled.', - table: { - category: 'Options', - defaultValue: { summary: false }, - }, - type: { - name: 'boolean', - required: false, - }, - }, - id: { - control: { - type: 'text', - }, - description: 'Field id.', - type: { - name: 'string', - required: true, - }, - }, - max: { - control: { - type: 'number', - }, - description: 'Maximum value.', - table: { - category: 'Options', - }, - type: { - name: 'number', - required: false, - }, - }, - min: { - control: { - type: 'number', - }, - description: 'Minimum value.', - table: { - category: 'Options', - }, - type: { - name: 'number', - required: false, - }, - }, - name: { - control: { - type: 'text', - }, - description: 'Field name.', - type: { - name: 'string', - required: true, - }, - }, - placeholder: { - control: { - type: 'text', - }, - description: 'A placeholder value.', - table: { - category: 'Options', - }, - type: { - name: 'string', - required: false, - }, - }, - required: { - control: { - type: 'boolean', - }, - description: 'Determine if the field is required.', - table: { - category: 'Options', - defaultValue: { summary: false }, - }, - type: { - name: 'boolean', - required: false, - }, - }, - setValue: { - control: { - type: null, - }, - description: 'Callback function to set field value.', - table: { - category: 'Events', - }, - type: { - name: 'function', - required: true, - }, - }, - step: { - control: { - type: 'number', - }, - description: 'Field incremental values that are valid.', - table: { - category: 'Options', - }, - type: { - name: 'number', - required: false, - }, - }, - type: { - control: { - type: 'select', - }, - description: 'Field type: input type or textarea.', - options: [ - 'datetime-local', - 'email', - 'number', - 'search', - 'tel', - 'text', - 'textarea', - 'time', - 'url', - ], - type: { - name: 'string', - required: true, - }, - }, - value: { - control: { - type: null, - }, - description: 'Field value.', - type: { - name: 'string', - required: true, - }, - }, - }, -} as ComponentMeta; - -const Template: ComponentStory = ({ - value: _value, - setValue: _setValue, - ...args -}) => { - const [value, setValue] = useState(''); - - return ; -}; - -/** - * Field Story - DateTime - */ -export const DateTime = Template.bind({}); -DateTime.args = { - id: 'field-storybook', - name: 'field-storybook', - type: 'datetime-local', -}; - -/** - * Field Story - Email - */ -export const Email = Template.bind({}); -Email.args = { - id: 'field-storybook', - name: 'field-storybook', - type: 'email', -}; - -/** - * Field Story - Text - */ -export const Text = Template.bind({}); -Text.args = { - id: 'field-storybook', - name: 'field-storybook', - type: 'text', -}; - -/** - * Field Story - Number - */ -export const Number = Template.bind({}); -Number.args = { - id: 'field-storybook', - name: 'field-storybook', - type: 'number', -}; - -/** - * Field Story - TextArea - */ -export const TextArea = Template.bind({}); -TextArea.args = { - id: 'field-storybook', - name: 'field-storybook', - type: 'textarea', -}; - -/** - * Field Story - Time - */ -export const Time = Template.bind({}); -Time.args = { - id: 'field-storybook', - name: 'field-storybook', - type: 'time', -}; diff --git a/src/components/atoms/forms/field.test.tsx b/src/components/atoms/forms/field.test.tsx deleted file mode 100644 index 492aa48..0000000 --- a/src/components/atoms/forms/field.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { render, screen } from '../../../../tests/utils'; -import { Field } from './field'; - -describe('Field', () => { - it('renders a text input', () => { - render( - null} - /> - ); - expect(screen.getByRole('textbox')).toHaveAttribute('type', 'text'); - }); - - it('renders a search input', () => { - render( - null} - /> - ); - expect(screen.getByRole('searchbox')).toHaveAttribute('type', 'search'); - }); -}); diff --git a/src/components/atoms/forms/field.tsx b/src/components/atoms/forms/field.tsx deleted file mode 100644 index a4553e3..0000000 --- a/src/components/atoms/forms/field.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { - ChangeEvent, - forwardRef, - ForwardRefRenderFunction, - SetStateAction, -} from 'react'; -import styles from './forms.module.scss'; - -export type FieldType = - | 'datetime-local' - | 'email' - | 'number' - | 'search' - | 'tel' - | 'text' - | 'textarea' - | 'time' - | 'url'; - -export type FieldProps = { - /** - * One or more ids that refers to the field name. - */ - 'aria-labelledby'?: string; - /** - * Add classnames to the field. - */ - className?: string; - /** - * Field state. Either enabled (false) or disabled (true). - */ - disabled?: boolean; - /** - * Field id attribute. - */ - id: string; - /** - * Field maximum value. - */ - max?: number | string; - /** - * Field minimum value. - */ - min?: number | string; - /** - * Field name attribute. - */ - name: string; - /** - * Placeholder value. - */ - placeholder?: string; - /** - * True if the field is required. Default: false. - */ - required?: boolean; - /** - * Callback function to set field value. - */ - setValue: (value: SetStateAction) => void; - /** - * Field incremental values that are valid. - */ - step?: number | string; - /** - * Field type. Default: text. - */ - type: FieldType; - /** - * Field value. - */ - value: string; -}; - -const FieldWithRef: ForwardRefRenderFunction = ( - { className = '', setValue, type, ...props }, - ref -) => { - /** - * Update select value when an option is selected. - * @param e - The option change event. - */ - const updateValue = ( - e: ChangeEvent - ) => { - setValue(e.target.value); - }; - - return type === 'textarea' ? ( -