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; |
