diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/atoms/forms/field.stories.tsx | 12 | ||||
| -rw-r--r-- | src/components/atoms/forms/field.test.tsx | 20 | ||||
| -rw-r--r-- | src/components/atoms/forms/field.tsx | 8 | ||||
| -rw-r--r-- | src/components/molecules/forms/labelled-field.stories.tsx | 180 | ||||
| -rw-r--r-- | src/components/molecules/forms/labelled-field.test.tsx | 19 | ||||
| -rw-r--r-- | src/components/molecules/forms/labelled-field.tsx | 25 | 
6 files changed, 250 insertions, 14 deletions
| diff --git a/src/components/atoms/forms/field.stories.tsx b/src/components/atoms/forms/field.stories.tsx index 0406f10..02681e7 100644 --- a/src/components/atoms/forms/field.stories.tsx +++ b/src/components/atoms/forms/field.stories.tsx @@ -31,12 +31,9 @@ export default {          type: 'text',        },        description: 'Field id.', -      table: { -        category: 'Options', -      },        type: {          name: 'string', -        required: false, +        required: true,        },      },      max: { @@ -70,12 +67,9 @@ export default {          type: 'text',        },        description: 'Field name.', -      table: { -        category: 'Options', -      },        type: {          name: 'string', -        required: false, +        required: true,        },      },      placeholder: { @@ -171,6 +165,8 @@ const Template: ComponentStory<typeof FieldComponent> = (args) => (  export const Field = Template.bind({});  Field.args = { +  id: 'field-storybook', +  name: 'field-storybook',    setValue: () => null,    value: '',  }; diff --git a/src/components/atoms/forms/field.test.tsx b/src/components/atoms/forms/field.test.tsx index 5488220..a04a976 100644 --- a/src/components/atoms/forms/field.test.tsx +++ b/src/components/atoms/forms/field.test.tsx @@ -3,12 +3,28 @@ import Field from './field';  describe('Field', () => {    it('renders a text input', () => { -    render(<Field type="text" value="" setValue={() => null} />); +    render( +      <Field +        id="text-field" +        name="text-field" +        type="text" +        value="" +        setValue={() => null} +      /> +    );      expect(screen.getByRole('textbox')).toHaveAttribute('type', 'text');    });    it('renders a search input', () => { -    render(<Field type="search" value="" setValue={() => null} />); +    render( +      <Field +        id="search-field" +        name="search-field" +        type="search" +        value="" +        setValue={() => null} +      /> +    );      expect(screen.getByRole('searchbox')).toHaveAttribute('type', 'search');    });  }); diff --git a/src/components/atoms/forms/field.tsx b/src/components/atoms/forms/field.tsx index 7d1ac93..513d2ba 100644 --- a/src/components/atoms/forms/field.tsx +++ b/src/components/atoms/forms/field.tsx @@ -1,7 +1,7 @@  import { ChangeEvent, FC, SetStateAction } from 'react';  import styles from './forms.module.scss'; -type FieldType = +export type FieldType =    | 'datetime-local'    | 'email'    | 'number' @@ -12,7 +12,7 @@ type FieldType =    | 'time'    | 'url'; -type FieldProps = { +export type FieldProps = {    /**     * Field state. Either enabled (false) or disabled (true).     */ @@ -20,7 +20,7 @@ type FieldProps = {    /**     * Field id attribute.     */ -  id?: string; +  id: string;    /**     * Field maximum value.     */ @@ -32,7 +32,7 @@ type FieldProps = {    /**     * Field name attribute.     */ -  name?: string; +  name: string;    /**     * Placeholder value.     */ diff --git a/src/components/molecules/forms/labelled-field.stories.tsx b/src/components/molecules/forms/labelled-field.stories.tsx new file mode 100644 index 0000000..eb7f8b5 --- /dev/null +++ b/src/components/molecules/forms/labelled-field.stories.tsx @@ -0,0 +1,180 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import LabelledFieldComponent from './labelled-field'; + +export default { +  title: 'Molecules/Forms', +  component: LabelledFieldComponent, +  args: { +    disabled: false, +    required: false, +  }, +  argTypes: { +    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, +      }, +    }, +    label: { +      control: { +        type: 'text', +      }, +      description: 'Field label.', +      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: 'text', +      }, +      description: 'Field value.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +  }, +} as ComponentMeta<typeof LabelledFieldComponent>; + +const Template: ComponentStory<typeof LabelledFieldComponent> = (args) => ( +  <LabelledFieldComponent {...args} /> +); + +export const LabelledField = Template.bind({}); +LabelledField.args = { +  id: 'labelled-field-storybook', +  label: 'Labelled field', +  name: 'labelled-field-storybook', +  setValue: () => null, +  value: '', +}; diff --git a/src/components/molecules/forms/labelled-field.test.tsx b/src/components/molecules/forms/labelled-field.test.tsx new file mode 100644 index 0000000..6fabe19 --- /dev/null +++ b/src/components/molecules/forms/labelled-field.test.tsx @@ -0,0 +1,19 @@ +import { render, screen } from '@test-utils'; +import LabelledField from './labelled-field'; + +describe('LabelledField', () => { +  it('renders a labelled field', () => { +    render( +      <LabelledField +        type="text" +        id="jest-text-field" +        name="jest-text-field" +        label="Jest text field" +        value="test" +        setValue={() => null} +      /> +    ); +    expect(screen.getByLabelText('Jest text field')).toBeInTheDocument(); +    expect(screen.getByRole('textbox')).toHaveValue('test'); +  }); +}); diff --git a/src/components/molecules/forms/labelled-field.tsx b/src/components/molecules/forms/labelled-field.tsx new file mode 100644 index 0000000..7f81e23 --- /dev/null +++ b/src/components/molecules/forms/labelled-field.tsx @@ -0,0 +1,25 @@ +import Field, { type FieldProps } from '@components/atoms/forms/field'; +import Label from '@components/atoms/forms/label'; +import { FC } from 'react'; + +type LabelledFieldProps = FieldProps & { +  label: string; +}; + +const LabelledField: FC<LabelledFieldProps> = ({ +  id, +  label, +  required, +  ...props +}) => { +  return ( +    <> +      <Label htmlFor={id} required={required}> +        {label} +      </Label> +      <Field id={id} required={required} {...props} /> +    </> +  ); +}; + +export default LabelledField; | 
