diff options
Diffstat (limited to 'src/components/molecules')
4 files changed, 203 insertions, 0 deletions
| diff --git a/src/components/molecules/buttons/main-nav-button.module.scss b/src/components/molecules/buttons/main-nav-button.module.scss new file mode 100644 index 0000000..bc1ed19 --- /dev/null +++ b/src/components/molecules/buttons/main-nav-button.module.scss @@ -0,0 +1,37 @@ +@use "@styles/abstracts/functions" as fun; + +.checkbox { +  position: absolute; +  top: calc(#{fun.convert-px(50)} / 2); +  left: calc(#{fun.convert-px(50)} / 2); +  opacity: 0; +  cursor: pointer; +} + +.label { +  display: block; +  cursor: pointer; + +  .icon { +    &__wrapper { +      --icon-size: #{fun.convert-px(50)}; +    } + +    &--active { +      background: transparent; +      border: transparent; + +      &::before { +        top: 0; +        transform-origin: 50% 50%; +        transform: rotate(-45deg); +      } + +      &::after { +        bottom: 0; +        transform-origin: 50% 50%; +        transform: rotate(45deg); +      } +    } +  } +} diff --git a/src/components/molecules/buttons/main-nav-button.stories.tsx b/src/components/molecules/buttons/main-nav-button.stories.tsx new file mode 100644 index 0000000..39e495c --- /dev/null +++ b/src/components/molecules/buttons/main-nav-button.stories.tsx @@ -0,0 +1,80 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { useState } from 'react'; +import { IntlProvider } from 'react-intl'; +import MainNavButtonComponent from './main-nav-button'; + +export default { +  title: 'Molecules/Buttons', +  component: MainNavButtonComponent, +  argTypes: { +    checkboxClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the checkbox.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    isActive: { +      control: { +        type: null, +      }, +      description: 'The button state.', +      type: { +        name: 'boolean', +        required: true, +      }, +    }, +    labelClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the label.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    setIsActive: { +      control: { +        type: null, +      }, +      description: 'A callback function to set the button state.', +      type: { +        name: 'function', +        required: true, +      }, +    }, +  }, +} as ComponentMeta<typeof MainNavButtonComponent>; + +const Template: ComponentStory<typeof MainNavButtonComponent> = ({ +  isActive, +  setIsActive: _setIsActive, +  ...args +}) => { +  const [isChecked, setIsChecked] = useState<boolean>(isActive); + +  return ( +    <IntlProvider locale="en"> +      <MainNavButtonComponent +        isActive={isChecked} +        setIsActive={setIsChecked} +        {...args} +      /> +    </IntlProvider> +  ); +}; + +export const MainNavButton = Template.bind({}); +MainNavButton.args = { +  isActive: false, +}; diff --git a/src/components/molecules/buttons/main-nav-button.test.tsx b/src/components/molecules/buttons/main-nav-button.test.tsx new file mode 100644 index 0000000..e757305 --- /dev/null +++ b/src/components/molecules/buttons/main-nav-button.test.tsx @@ -0,0 +1,11 @@ +import { render, screen } from '@test-utils'; +import MainNavButton from './main-nav-button'; + +describe('MainNavButton', () => { +  it('renders a checkbox', () => { +    render(<MainNavButton isActive={false} setIsActive={() => null} />); +    expect( +      screen.getByRole('checkbox', { name: 'Open menu' }) +    ).toBeInTheDocument(); +  }); +}); diff --git a/src/components/molecules/buttons/main-nav-button.tsx b/src/components/molecules/buttons/main-nav-button.tsx new file mode 100644 index 0000000..59407db --- /dev/null +++ b/src/components/molecules/buttons/main-nav-button.tsx @@ -0,0 +1,75 @@ +import Checkbox, { CheckboxProps } from '@components/atoms/forms/checkbox'; +import Label from '@components/atoms/forms/label'; +import Hamburger from '@components/atoms/icons/hamburger'; +import { VFC } from 'react'; +import { useIntl } from 'react-intl'; +import styles from './main-nav-button.module.scss'; + +export type MainNavButtonProps = { +  /** +   * Set additional classnames to the checkbox. +   */ +  checkboxClassName?: string; +  /** +   * The button state. +   */ +  isActive: CheckboxProps['value']; +  /** +   * Set additional classnames to the label. +   */ +  labelClassName?: string; +  /** +   * A callback function to handle button state. +   */ +  setIsActive: CheckboxProps['setValue']; +}; + +/** + * MainNavButton component + * + * Render a hamburger icon or a close icon depending on state. + */ +const MainNavButton: VFC<MainNavButtonProps> = ({ +  checkboxClassName = '', +  isActive, +  labelClassName = '', +  setIsActive, +}) => { +  const intl = useIntl(); +  const label = isActive +    ? intl.formatMessage({ +        defaultMessage: 'Close menu', +        id: 'wT7YZb', +        description: 'MainNavButton: close menu label', +      }) +    : intl.formatMessage({ +        defaultMessage: 'Open menu', +        id: 'P7j8ZZ', +        description: 'MainNavButton: open menu label', +      }); +  const hamburgerModifier = isActive ? 'icon--active' : ''; + +  return ( +    <> +      <Checkbox +        id="main-nav-button" +        name="main-nav-button" +        value={isActive} +        setValue={setIsActive} +        className={`${styles.checkbox} ${checkboxClassName}`} +      /> +      <Label +        htmlFor="main-nav-button" +        className={`${styles.label} ${labelClassName}`} +        aria-label={label} +      > +        <Hamburger +          className={styles.icon__wrapper} +          iconClassName={styles[hamburgerModifier]} +        /> +      </Label> +    </> +  ); +}; + +export default MainNavButton; | 
