diff options
Diffstat (limited to 'src/components/molecules/buttons')
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; |
