diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-04-06 18:40:17 +0200 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-04-06 18:45:08 +0200 | 
| commit | 47e12259d512e476326e83929efebf036b57f7c1 (patch) | |
| tree | ff175aa349caf2a3872f78a239d8f35d72affcc5 | |
| parent | dfd816d1891545aa8ead982b57891858f1c82bb4 (diff) | |
chore: add a Modal component
| -rw-r--r-- | src/components/atoms/headings/heading.stories.tsx | 13 | ||||
| -rw-r--r-- | src/components/atoms/headings/heading.tsx | 6 | ||||
| -rw-r--r-- | src/components/atoms/icons/cog.module.scss | 1 | ||||
| -rw-r--r-- | src/components/atoms/icons/magnifying-glass.module.scss | 1 | ||||
| -rw-r--r-- | src/components/molecules/layout/modal.module.scss | 21 | ||||
| -rw-r--r-- | src/components/molecules/layout/modal.stories.tsx | 57 | ||||
| -rw-r--r-- | src/components/molecules/layout/modal.test.tsx | 9 | ||||
| -rw-r--r-- | src/components/molecules/layout/modal.tsx | 48 | 
8 files changed, 151 insertions, 5 deletions
| diff --git a/src/components/atoms/headings/heading.stories.tsx b/src/components/atoms/headings/heading.stories.tsx index 0b286fe..cea3532 100644 --- a/src/components/atoms/headings/heading.stories.tsx +++ b/src/components/atoms/headings/heading.stories.tsx @@ -9,6 +9,19 @@ export default {      withMargin: true,    },    argTypes: { +    additionalClasses: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classes.', +      table: { +        category: 'Options', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      children: {        description: 'Heading body.',        type: { diff --git a/src/components/atoms/headings/heading.tsx b/src/components/atoms/headings/heading.tsx index 77580cc..136571d 100644 --- a/src/components/atoms/headings/heading.tsx +++ b/src/components/atoms/headings/heading.tsx @@ -1,7 +1,7 @@  import { FC } from 'react';  import styles from './heading.module.scss'; -type HeadingProps = { +export type HeadingProps = {    /**     * Adds additional classes.     */ @@ -33,12 +33,12 @@ const Heading: FC<HeadingProps> = ({    withMargin = true,  }) => {    const TitleTag = isFake ? `p` : (`h${level}` as keyof JSX.IntrinsicElements); -  const variantClass = withMargin ? 'heading--margin' : 'heading--regular';    const levelClass = `heading--${level}`; +  const marginClass = withMargin ? 'heading--margin' : 'heading--regular';    return (      <TitleTag -      className={`${styles.heading} ${styles[variantClass]} ${styles[levelClass]} ${additionalClasses}`} +      className={`${styles.heading} ${styles[levelClass]} ${styles[marginClass]} ${additionalClasses}`}      >        {children}      </TitleTag> diff --git a/src/components/atoms/icons/cog.module.scss b/src/components/atoms/icons/cog.module.scss index 8c48fcc..5201598 100644 --- a/src/components/atoms/icons/cog.module.scss +++ b/src/components/atoms/icons/cog.module.scss @@ -1,7 +1,6 @@  @use "@styles/abstracts/functions" as fun;  .icon { -  display: block;    width: var(--icon-size, #{fun.convert-px(40)});    fill: var(--color-primary-lighter);    stroke: var(--color-primary-darker); diff --git a/src/components/atoms/icons/magnifying-glass.module.scss b/src/components/atoms/icons/magnifying-glass.module.scss index dca76fb..d14bec5 100644 --- a/src/components/atoms/icons/magnifying-glass.module.scss +++ b/src/components/atoms/icons/magnifying-glass.module.scss @@ -1,7 +1,6 @@  @use "@styles/abstracts/functions" as fun;  .icon { -  display: block;    width: var(--icon-size, #{fun.convert-px(40)});  } diff --git a/src/components/molecules/layout/modal.module.scss b/src/components/molecules/layout/modal.module.scss new file mode 100644 index 0000000..2fff562 --- /dev/null +++ b/src/components/molecules/layout/modal.module.scss @@ -0,0 +1,21 @@ +@use "@styles/abstracts/functions" as fun; + +.wrapper { +  padding: var(--spacing-md); +  background: var(--color-bg-secondary); +  border: fun.convert-px(4) solid; +  border-image: radial-gradient( +      ellipse at top, +      var(--color-primary-lighter) 20%, +      var(--color-primary) 100% +    ) +    1; +  box-shadow: fun.convert-px(2) fun.convert-px(-2) fun.convert-px(3) +    fun.convert-px(-1) var(--color-shadow-dark); +} + +.icon { +  --icon-size: #{fun.convert-px(30)}; + +  margin-right: var(--spacing-2xs); +} diff --git a/src/components/molecules/layout/modal.stories.tsx b/src/components/molecules/layout/modal.stories.tsx new file mode 100644 index 0000000..396e89e --- /dev/null +++ b/src/components/molecules/layout/modal.stories.tsx @@ -0,0 +1,57 @@ +import Cog from '@components/atoms/icons/cog'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import ModalComponent from './modal'; + +export default { +  title: 'Molecules/Layout', +  component: ModalComponent, +  argTypes: { +    children: { +      control: { +        type: 'text', +      }, +      description: 'The modal body.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +    icon: { +      control: { +        type: 'select', +      }, +      description: 'The title icon.', +      options: ['', 'cogs', 'search'], +      table: { +        category: 'Options', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    title: { +      control: { +        type: 'text', +      }, +      description: 'The modal title.', +      table: { +        category: 'Options', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +  }, +} as ComponentMeta<typeof ModalComponent>; + +const Template: ComponentStory<typeof ModalComponent> = (args) => ( +  <ModalComponent {...args} /> +); + +export const Modal = Template.bind({}); +Modal.args = { +  children: +    'Inventore natus dignissimos aut illum modi asperiores. Et voluptatibus delectus.', +}; diff --git a/src/components/molecules/layout/modal.test.tsx b/src/components/molecules/layout/modal.test.tsx new file mode 100644 index 0000000..14fb224 --- /dev/null +++ b/src/components/molecules/layout/modal.test.tsx @@ -0,0 +1,9 @@ +import { render, screen } from '@test-utils'; +import Modal from './modal'; + +describe('Modal', () => { +  it('renders a title', () => { +    render(<Modal title="A custom title" />); +    expect(screen.getByText('A custom title')).toBeInTheDocument(); +  }); +}); diff --git a/src/components/molecules/layout/modal.tsx b/src/components/molecules/layout/modal.tsx new file mode 100644 index 0000000..4dc3b0a --- /dev/null +++ b/src/components/molecules/layout/modal.tsx @@ -0,0 +1,48 @@ +import Heading from '@components/atoms/headings/heading'; +import dynamic from 'next/dynamic'; +import { FC, ReactNode } from 'react'; +import styles from './modal.module.scss'; + +export type Icons = 'cogs' | 'search'; + +export type ModalProps = { +  icon?: Icons; +  title?: string; +}; + +const CogIcon = dynamic<ReactNode>(() => import('@components/atoms/icons/cog')); +const SearchIcon = dynamic<ReactNode>( +  () => import('@components/atoms/icons/magnifying-glass') +); + +/** + * Modal component + * + * Render a modal component with an optional title and icon. + */ +const Modal: FC<ModalProps> = ({ children, icon, title }) => { +  const getIcon = (id: Icons) => { +    switch (id) { +      case 'cogs': +        return <CogIcon />; +      case 'search': +        return <SearchIcon />; +      default: +        return <></>; +    } +  }; + +  return ( +    <div className={styles.wrapper}> +      {title && ( +        <Heading isFake={true} level={3}> +          {icon && <span className={styles.icon}>{getIcon(icon)}</span>} +          {title} +        </Heading> +      )} +      {children} +    </div> +  ); +}; + +export default Modal; | 
