From 47e12259d512e476326e83929efebf036b57f7c1 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Wed, 6 Apr 2022 18:40:17 +0200 Subject: chore: add a Modal component --- src/components/atoms/headings/heading.stories.tsx | 13 +++++ src/components/atoms/headings/heading.tsx | 6 +-- src/components/atoms/icons/cog.module.scss | 1 - .../atoms/icons/magnifying-glass.module.scss | 1 - src/components/molecules/layout/modal.module.scss | 21 ++++++++ src/components/molecules/layout/modal.stories.tsx | 57 ++++++++++++++++++++++ src/components/molecules/layout/modal.test.tsx | 9 ++++ src/components/molecules/layout/modal.tsx | 48 ++++++++++++++++++ 8 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/components/molecules/layout/modal.module.scss create mode 100644 src/components/molecules/layout/modal.stories.tsx create mode 100644 src/components/molecules/layout/modal.test.tsx create mode 100644 src/components/molecules/layout/modal.tsx (limited to 'src') 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 = ({ 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 ( {children} 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; + +const Template: ComponentStory = (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(); + 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(() => import('@components/atoms/icons/cog')); +const SearchIcon = dynamic( + () => import('@components/atoms/icons/magnifying-glass') +); + +/** + * Modal component + * + * Render a modal component with an optional title and icon. + */ +const Modal: FC = ({ children, icon, title }) => { + const getIcon = (id: Icons) => { + switch (id) { + case 'cogs': + return ; + case 'search': + return ; + default: + return <>; + } + }; + + return ( +
+ {title && ( + + {icon && {getIcon(icon)}} + {title} + + )} + {children} +
+ ); +}; + +export default Modal; -- cgit v1.2.3