diff options
Diffstat (limited to 'src/components/molecules')
15 files changed, 344 insertions, 65 deletions
| diff --git a/src/components/molecules/forms/labelled-field.module.scss b/src/components/molecules/forms/labelled-field.module.scss new file mode 100644 index 0000000..64ef3d0 --- /dev/null +++ b/src/components/molecules/forms/labelled-field.module.scss @@ -0,0 +1,9 @@ +.label { +  &--left { +    margin-right: var(--spacing-2xs); +  } + +  &--top { +    display: block; +  } +} diff --git a/src/components/molecules/forms/labelled-field.stories.tsx b/src/components/molecules/forms/labelled-field.stories.tsx index eb7f8b5..b77d71e 100644 --- a/src/components/molecules/forms/labelled-field.stories.tsx +++ b/src/components/molecules/forms/labelled-field.stories.tsx @@ -1,4 +1,5 @@  import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { useState } from 'react';  import LabelledFieldComponent from './labelled-field';  export default { @@ -6,6 +7,7 @@ export default {    component: LabelledFieldComponent,    args: {      disabled: false, +    labelPosition: 'top',      required: false,    },    argTypes: { @@ -43,6 +45,21 @@ export default {          required: true,        },      }, +    labelPosition: { +      control: { +        type: 'select', +      }, +      description: 'The label position.', +      options: ['left', 'top'], +      table: { +        category: 'Options', +        defaultValue: { summary: 'top' }, +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      max: {        control: {          type: 'number', @@ -155,7 +172,7 @@ export default {      },      value: {        control: { -        type: 'text', +        type: null,        },        description: 'Field value.',        type: { @@ -166,15 +183,19 @@ export default {    },  } as ComponentMeta<typeof LabelledFieldComponent>; -const Template: ComponentStory<typeof LabelledFieldComponent> = (args) => ( -  <LabelledFieldComponent {...args} /> -); +const Template: ComponentStory<typeof LabelledFieldComponent> = ({ +  value: _value, +  setValue: _setValue, +  ...args +}) => { +  const [value, setValue] = useState<string>(''); + +  return <LabelledFieldComponent value={value} setValue={setValue} {...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.tsx b/src/components/molecules/forms/labelled-field.tsx index 7f81e23..08d0126 100644 --- a/src/components/molecules/forms/labelled-field.tsx +++ b/src/components/molecules/forms/labelled-field.tsx @@ -1,20 +1,46 @@  import Field, { type FieldProps } from '@components/atoms/forms/field';  import Label from '@components/atoms/forms/label'; -import { FC } from 'react'; +import { VFC } from 'react'; +import styles from './labelled-field.module.scss'; -type LabelledFieldProps = FieldProps & { +export type LabelledFieldProps = FieldProps & { +  /** +   * Visually hide the field label. Default: false. +   */ +  hideLabel?: boolean; +  /** +   * The field label. +   */    label: string; +  /** +   * The label position. Default: top. +   */ +  labelPosition?: 'left' | 'top';  }; -const LabelledField: FC<LabelledFieldProps> = ({ +/** + * LabelledField component + * + * Render a field tied to a label. + */ +const LabelledField: VFC<LabelledFieldProps> = ({ +  hideLabel = false,    id,    label, +  labelPosition = 'top',    required,    ...props  }) => { +  const positionModifier = `label--${labelPosition}`; +  const visibilityClass = hideLabel ? 'screen-reader-text' : ''; +    return (      <> -      <Label htmlFor={id} required={required}> +      <Label +        htmlFor={id} +        required={required} +        className={`${visibilityClass} ${styles[positionModifier]}`} +      >          {label}        </Label>        <Field id={id} required={required} {...props} /> diff --git a/src/components/molecules/forms/labelled-select.module.scss b/src/components/molecules/forms/labelled-select.module.scss new file mode 100644 index 0000000..64ef3d0 --- /dev/null +++ b/src/components/molecules/forms/labelled-select.module.scss @@ -0,0 +1,9 @@ +.label { +  &--left { +    margin-right: var(--spacing-2xs); +  } + +  &--top { +    display: block; +  } +} diff --git a/src/components/molecules/forms/labelled-select.stories.tsx b/src/components/molecules/forms/labelled-select.stories.tsx index 0966e13..0c569f5 100644 --- a/src/components/molecules/forms/labelled-select.stories.tsx +++ b/src/components/molecules/forms/labelled-select.stories.tsx @@ -1,4 +1,5 @@  import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { useState } from 'react';  import LabelledSelectComponent from './labelled-select';  const selectOptions = [ @@ -12,6 +13,7 @@ export default {    component: LabelledSelectComponent,    args: {      disabled: false, +    labelPosition: 'top',      required: false,    },    argTypes: { @@ -49,6 +51,48 @@ export default {          required: true,        },      }, +    labelClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the label.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    labelPosition: { +      control: { +        type: 'select', +      }, +      description: 'The label position.', +      options: ['left', 'top'], +      table: { +        category: 'Options', +        defaultValue: { summary: 'top' }, +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    labelSize: { +      control: { +        type: 'select', +      }, +      description: 'The label size.', +      options: ['medium', 'small'], +      table: { +        category: 'Options', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      name: {        control: {          type: 'text', @@ -86,6 +130,19 @@ export default {          required: false,        },      }, +    selectClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the select field.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      setValue: {        control: {          type: null, @@ -101,7 +158,7 @@ export default {      },      value: {        control: { -        type: 'text', +        type: null,        },        description: 'Field value.',        type: { @@ -112,9 +169,21 @@ export default {    },  } as ComponentMeta<typeof LabelledSelectComponent>; -const Template: ComponentStory<typeof LabelledSelectComponent> = (args) => ( -  <LabelledSelectComponent {...args} /> -); +const Template: ComponentStory<typeof LabelledSelectComponent> = ({ +  value, +  setValue: _setValue, +  ...args +}) => { +  const [selected, setSelected] = useState<string>(value); + +  return ( +    <LabelledSelectComponent +      value={selected} +      setValue={setSelected} +      {...args} +    /> +  ); +};  export const LabelledSelect = Template.bind({});  LabelledSelect.args = { @@ -122,6 +191,5 @@ LabelledSelect.args = {    label: 'Labelled select',    name: 'labelled-select-storybook',    options: selectOptions, -  setValue: () => null, -  value: '', +  value: 'option1',  }; diff --git a/src/components/molecules/forms/labelled-select.tsx b/src/components/molecules/forms/labelled-select.tsx index 442e91a..7d4237a 100644 --- a/src/components/molecules/forms/labelled-select.tsx +++ b/src/components/molecules/forms/labelled-select.tsx @@ -1,23 +1,62 @@ -import Label from '@components/atoms/forms/label'; +import Label, { LabelProps } from '@components/atoms/forms/label';  import Select, { type SelectProps } from '@components/atoms/forms/select'; -import { FC } from 'react'; +import { VFC } from 'react'; +import styles from './labelled-select.module.scss'; -type LabelledSelectProps = SelectProps & { +export type LabelledSelectProps = Omit< +  SelectProps, +  'aria-labelledby' | 'className' +> & { +  /** +   * The field label. +   */    label: string; +  /** +   * Set additional classnames to the label. +   */ +  labelClassName?: string; +  /** +   * The label position. Default: top. +   */ +  labelPosition?: 'left' | 'top'; +  /** +   * The label size. +   */ +  labelSize?: LabelProps['size']; +  /** +   * Set additional classnames to the select field. +   */ +  selectClassName?: string;  }; -const LabelledSelect: FC<LabelledSelectProps> = ({ +const LabelledSelect: VFC<LabelledSelectProps> = ({    id,    label, +  labelClassName = '', +  labelPosition = 'top', +  labelSize,    required, +  selectClassName = '',    ...props  }) => { +  const positionModifier = `label--${labelPosition}`; +    return (      <> -      <Label htmlFor={id} required={required}> +      <Label +        htmlFor={id} +        required={required} +        size={labelSize} +        className={`${styles[positionModifier]} ${labelClassName}`} +      >          {label}        </Label> -      <Select id={id} required={required} {...props} /> +      <Select +        id={id} +        required={required} +        {...props} +        className={selectClassName} +      />      </>    );  }; diff --git a/src/components/molecules/forms/motion-toggle.stories.tsx b/src/components/molecules/forms/motion-toggle.stories.tsx index 4fc199a..dc4d2a9 100644 --- a/src/components/molecules/forms/motion-toggle.stories.tsx +++ b/src/components/molecules/forms/motion-toggle.stories.tsx @@ -5,6 +5,18 @@ import MotionToggleComponent from './motion-toggle';  export default {    title: 'Molecules/Forms',    component: MotionToggleComponent, +  argTypes: { +    value: { +      control: { +        type: null, +      }, +      description: 'The reduce motion value.', +      type: { +        name: 'boolean', +        required: true, +      }, +    }, +  },  } as ComponentMeta<typeof MotionToggleComponent>;  const Template: ComponentStory<typeof MotionToggleComponent> = (args) => ( @@ -14,3 +26,6 @@ const Template: ComponentStory<typeof MotionToggleComponent> = (args) => (  );  export const MotionToggle = Template.bind({}); +MotionToggle.args = { +  value: false, +}; diff --git a/src/components/molecules/forms/motion-toggle.tsx b/src/components/molecules/forms/motion-toggle.tsx index 77291ed..d4f7d11 100644 --- a/src/components/molecules/forms/motion-toggle.tsx +++ b/src/components/molecules/forms/motion-toggle.tsx @@ -2,17 +2,17 @@ import Toggle, {    ToggleChoices,    ToggleProps,  } from '@components/atoms/forms/toggle'; -import { FC, useState } from 'react'; +import { useState, VFC } from 'react';  import { useIntl } from 'react-intl'; -export type MotionToggleProps = Pick<ToggleProps, 'value'>; +export type MotionToggleProps = Pick<ToggleProps, 'labelClassName' | 'value'>;  /**   * MotionToggle component   *   * Render a Toggle component to set reduce motion.   */ -const MotionToggle: FC<MotionToggleProps> = ({ value }) => { +const MotionToggle: VFC<MotionToggleProps> = ({ value, ...props }) => {    const intl = useIntl();    const [isDeactivated, setIsDeactivated] = useState<boolean>(value);    const reduceMotionLabel = intl.formatMessage({ @@ -40,9 +40,11 @@ const MotionToggle: FC<MotionToggleProps> = ({ value }) => {        id="reduce-motion-settings"        name="reduce-motion-settings"        label={reduceMotionLabel} +      labelSize="medium"        choices={reduceMotionChoices}        value={isDeactivated}        setValue={setIsDeactivated} +      {...props}      />    );  }; diff --git a/src/components/molecules/forms/prism-theme-toggle.stories.tsx b/src/components/molecules/forms/prism-theme-toggle.stories.tsx index fee54ef..dc9090b 100644 --- a/src/components/molecules/forms/prism-theme-toggle.stories.tsx +++ b/src/components/molecules/forms/prism-theme-toggle.stories.tsx @@ -5,6 +5,18 @@ import PrismThemeToggleComponent from './prism-theme-toggle';  export default {    title: 'Molecules/Forms',    component: PrismThemeToggleComponent, +  argTypes: { +    value: { +      control: { +        type: null, +      }, +      description: 'The prism theme value.', +      type: { +        name: 'boolean', +        required: true, +      }, +    }, +  },  } as ComponentMeta<typeof PrismThemeToggleComponent>;  const Template: ComponentStory<typeof PrismThemeToggleComponent> = (args) => ( @@ -14,3 +26,6 @@ const Template: ComponentStory<typeof PrismThemeToggleComponent> = (args) => (  );  export const PrismThemeToggle = Template.bind({}); +PrismThemeToggle.args = { +  value: false, +}; diff --git a/src/components/molecules/forms/prism-theme-toggle.tsx b/src/components/molecules/forms/prism-theme-toggle.tsx index cedb71a..81a211b 100644 --- a/src/components/molecules/forms/prism-theme-toggle.tsx +++ b/src/components/molecules/forms/prism-theme-toggle.tsx @@ -4,17 +4,20 @@ import Toggle, {  } from '@components/atoms/forms/toggle';  import Moon from '@components/atoms/icons/moon';  import Sun from '@components/atoms/icons/sun'; -import { FC, useState } from 'react'; +import { useState, VFC } from 'react';  import { useIntl } from 'react-intl'; -export type PrismThemeToggleProps = Pick<ToggleProps, 'value'>; +export type PrismThemeToggleProps = Pick< +  ToggleProps, +  'labelClassName' | 'value' +>;  /**   * PrismThemeToggle component   *   * Render a Toggle component to set code blocks theme.   */ -const PrismThemeToggle: FC<PrismThemeToggleProps> = ({ value }) => { +const PrismThemeToggle: VFC<PrismThemeToggleProps> = ({ value, ...props }) => {    const intl = useIntl();    const [isDarkTheme, setIsDarkTheme] = useState<boolean>(value);    const themeLabel = intl.formatMessage({ @@ -42,9 +45,11 @@ const PrismThemeToggle: FC<PrismThemeToggleProps> = ({ value }) => {        id="prism-theme-settings"        name="prism-theme-settings"        label={themeLabel} +      labelSize="medium"        choices={themeChoices}        value={isDarkTheme}        setValue={setIsDarkTheme} +      {...props}      />    );  }; diff --git a/src/components/molecules/forms/select-with-tooltip.module.scss b/src/components/molecules/forms/select-with-tooltip.module.scss index 1f91f74..bfadece 100644 --- a/src/components/molecules/forms/select-with-tooltip.module.scss +++ b/src/components/molecules/forms/select-with-tooltip.module.scss @@ -5,14 +5,9 @@    display: flex;    flex-flow: row wrap;    align-items: center; -  gap: var(--spacing-xs);    position: relative;  } -.label { -  margin-right: auto; -} -  .select {    width: auto; @@ -22,6 +17,8 @@  }  .btn { +  margin-left: var(--spacing-xs); +    &--activated {      background: var(--color-primary); @@ -34,8 +31,7 @@  .tooltip {    position: absolute;    top: calc(100% + var(--spacing-xs)); -  right: 0; -  transform-origin: top right; +  transform-origin: top;    transition: all 0.75s ease-in-out 0s;    &--hidden { diff --git a/src/components/molecules/forms/select-with-tooltip.stories.tsx b/src/components/molecules/forms/select-with-tooltip.stories.tsx index d2d36fa..c63e9b8 100644 --- a/src/components/molecules/forms/select-with-tooltip.stories.tsx +++ b/src/components/molecules/forms/select-with-tooltip.stories.tsx @@ -17,11 +17,25 @@ export default {          required: true,        },      }, -    title: { +    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: 'The tooltip title', +      description: 'Field id.',        type: {          name: 'string',          required: true, @@ -37,28 +51,31 @@ export default {          required: true,        },      }, -    disabled: { +    labelClassName: {        control: { -        type: 'boolean', +        type: 'text',        }, -      description: 'Field state: either enabled or disabled.', +      description: 'Set additional classnames to the label.',        table: { -        category: 'Options', -        defaultValue: { summary: false }, +        category: 'Styles',        },        type: { -        name: 'boolean', +        name: 'string',          required: false,        },      }, -    id: { +    labelSize: {        control: { -        type: 'text', +        type: 'select', +      }, +      description: 'The label size.', +      options: ['medium', 'small'], +      table: { +        category: 'Options',        }, -      description: 'Field id.',        type: {          name: 'string', -        required: true, +        required: false,        },      },      name: { @@ -98,6 +115,19 @@ export default {          required: false,        },      }, +    selectClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the select field.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      setValue: {        control: {          type: null, @@ -111,6 +141,29 @@ export default {          required: true,        },      }, +    title: { +      control: { +        type: 'text', +      }, +      description: 'The tooltip title', +      type: { +        name: 'string', +        required: true, +      }, +    }, +    tooltipClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the tooltip.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      value: {        control: {          type: 'text', diff --git a/src/components/molecules/forms/select-with-tooltip.tsx b/src/components/molecules/forms/select-with-tooltip.tsx index 5e48d62..f537e1e 100644 --- a/src/components/molecules/forms/select-with-tooltip.tsx +++ b/src/components/molecules/forms/select-with-tooltip.tsx @@ -1,19 +1,22 @@ -import Select, { SelectProps } from '@components/atoms/forms/select'; -import { FC, useState } from 'react'; +import { useState, VFC } from 'react';  import HelpButton from '../buttons/help-button'; -import Tooltip, { TooltipProps } from '../modals/tooltip'; +import Tooltip, { type TooltipProps } from '../modals/tooltip'; +import LabelledSelect, { type LabelledSelectProps } from './labelled-select';  import styles from './select-with-tooltip.module.scss'; -export type SelectWithTooltipProps = SelectProps & +export type SelectWithTooltipProps = Omit< +  LabelledSelectProps, +  'labelPosition' +> &    Pick<TooltipProps, 'title' | 'content'> & {      /**       * The select label.       */      label: string;      /** -     * Set additional classes to the tooltip wrapper. +     * Set additional classnames to the tooltip wrapper.       */ -    tooltipClasses?: string; +    tooltipClassName?: string;    };  /** @@ -21,12 +24,11 @@ export type SelectWithTooltipProps = SelectProps &   *   * Render a select with a button to display a tooltip about options.   */ -const SelectWithTooltip: FC<SelectWithTooltipProps> = ({ +const SelectWithTooltip: VFC<SelectWithTooltipProps> = ({    title,    content,    id, -  label, -  tooltipClasses = '', +  tooltipClassName = '',    ...props  }) => {    const [isTooltipOpened, setIsTooltipOpened] = useState<boolean>(false); @@ -37,19 +39,21 @@ const SelectWithTooltip: FC<SelectWithTooltipProps> = ({    return (      <div className={styles.wrapper}> -      <label htmlFor={id} className={styles.label}> -        {label} -      </label> -      <Select id={id} {...props} classes={styles.select} /> +      <LabelledSelect +        labelPosition="left" +        id={id} +        labelClassName={styles.label} +        {...props} +      />        <HelpButton          onClick={() => setIsTooltipOpened(!isTooltipOpened)} -        classes={buttonModifier} +        className={`${styles.btn} ${buttonModifier}`}        />        <Tooltip          title={title}          content={content}          icon="?" -        classes={`${styles.tooltip} ${tooltipModifier} ${tooltipClasses}`} +        className={`${styles.tooltip} ${tooltipModifier} ${tooltipClassName}`}        />      </div>    ); diff --git a/src/components/molecules/forms/theme-toggle.stories.tsx b/src/components/molecules/forms/theme-toggle.stories.tsx index 5970afd..a9bcf73 100644 --- a/src/components/molecules/forms/theme-toggle.stories.tsx +++ b/src/components/molecules/forms/theme-toggle.stories.tsx @@ -5,6 +5,18 @@ import ThemeToggleComponent from './theme-toggle';  export default {    title: 'Molecules/Forms',    component: ThemeToggleComponent, +  argTypes: { +    value: { +      control: { +        type: null, +      }, +      description: 'The theme value.', +      type: { +        name: 'boolean', +        required: true, +      }, +    }, +  },  } as ComponentMeta<typeof ThemeToggleComponent>;  const Template: ComponentStory<typeof ThemeToggleComponent> = (args) => ( @@ -14,3 +26,6 @@ const Template: ComponentStory<typeof ThemeToggleComponent> = (args) => (  );  export const ThemeToggle = Template.bind({}); +ThemeToggle.args = { +  value: false, +}; diff --git a/src/components/molecules/forms/theme-toggle.tsx b/src/components/molecules/forms/theme-toggle.tsx index c927151..6d54591 100644 --- a/src/components/molecules/forms/theme-toggle.tsx +++ b/src/components/molecules/forms/theme-toggle.tsx @@ -4,17 +4,17 @@ import Toggle, {  } from '@components/atoms/forms/toggle';  import Moon from '@components/atoms/icons/moon';  import Sun from '@components/atoms/icons/sun'; -import { FC, useState } from 'react'; +import { useState, VFC } from 'react';  import { useIntl } from 'react-intl'; -export type ThemeToggleProps = Pick<ToggleProps, 'value'>; +export type ThemeToggleProps = Pick<ToggleProps, 'labelClassName' | 'value'>;  /**   * ThemeToggle component   *   * Render a Toggle component to set theme.   */ -const ThemeToggle: FC<ThemeToggleProps> = ({ value }) => { +const ThemeToggle: VFC<ThemeToggleProps> = ({ value, ...props }) => {    const intl = useIntl();    const [isDarkTheme, setIsDarkTheme] = useState<boolean>(value);    const themeLabel = intl.formatMessage({ @@ -42,9 +42,11 @@ const ThemeToggle: FC<ThemeToggleProps> = ({ value }) => {        id="theme-settings"        name="theme-settings"        label={themeLabel} +      labelSize="medium"        choices={themeChoices}        value={isDarkTheme}        setValue={setIsDarkTheme} +      {...props}      />    );  }; | 
