aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/molecules')
-rw-r--r--src/components/molecules/layout/modal.module.scss21
-rw-r--r--src/components/molecules/layout/modal.stories.tsx57
-rw-r--r--src/components/molecules/layout/modal.test.tsx9
-rw-r--r--src/components/molecules/layout/modal.tsx48
4 files changed, 135 insertions, 0 deletions
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;