aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/layout
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-04-09 19:40:14 +0200
committerArmand Philippot <git@armandphilippot.com>2022-04-09 19:40:14 +0200
commit355625b5dd1b4e2e52e6e5d0eee1a13d76c09fda (patch)
treec1a4d04e386a04f37d46e912c1547a656fcd03e0 /src/components/atoms/layout
parent7b9b5c2368ecff5b083b171cf38ce914746284ac (diff)
chore: add a Section component
Diffstat (limited to 'src/components/atoms/layout')
-rw-r--r--src/components/atoms/layout/section.module.scss25
-rw-r--r--src/components/atoms/layout/section.stories.tsx85
-rw-r--r--src/components/atoms/layout/section.test.tsx17
-rw-r--r--src/components/atoms/layout/section.tsx57
4 files changed, 184 insertions, 0 deletions
diff --git a/src/components/atoms/layout/section.module.scss b/src/components/atoms/layout/section.module.scss
new file mode 100644
index 0000000..012493a
--- /dev/null
+++ b/src/components/atoms/layout/section.module.scss
@@ -0,0 +1,25 @@
+@use "@styles/abstracts/functions" as fun;
+@use "@styles/abstracts/placeholders";
+
+.wrapper {
+ @extend %grid;
+
+ padding: var(--spacing-md) 0;
+
+ &--borders {
+ border-bottom: fun.convert-px(1) solid var(--color-border);
+ }
+
+ &--dark {
+ background: var(--color-bg-secondary);
+ }
+
+ &--light {
+ background: var(--color-bg);
+ }
+}
+
+.body,
+.title {
+ grid-column: 2;
+}
diff --git a/src/components/atoms/layout/section.stories.tsx b/src/components/atoms/layout/section.stories.tsx
new file mode 100644
index 0000000..abbbeed
--- /dev/null
+++ b/src/components/atoms/layout/section.stories.tsx
@@ -0,0 +1,85 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import SectionComponent from './section';
+
+export default {
+ title: 'Atoms/Layout',
+ component: SectionComponent,
+ args: {
+ variant: 'dark',
+ withBorder: true,
+ },
+ argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the section element.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ content: {
+ control: {
+ type: 'text',
+ },
+ description: 'The section content.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ title: {
+ control: {
+ type: 'text',
+ },
+ description: 'The section title.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ variant: {
+ control: {
+ type: 'select',
+ },
+ description: 'The section variant.',
+ options: ['light', 'dark'],
+ table: {
+ category: 'Styles',
+ defaultValue: { summary: 'dark' },
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ withBorder: {
+ control: {
+ type: 'boolean',
+ },
+ description: 'Add a border at the bottom of the section.',
+ table: {
+ category: 'Styles',
+ defaultValue: { summary: true },
+ },
+ type: {
+ name: 'boolean',
+ required: false,
+ },
+ },
+ },
+} as ComponentMeta<typeof SectionComponent>;
+
+const Template: ComponentStory<typeof SectionComponent> = (args) => (
+ <SectionComponent {...args} />
+);
+
+export const Section = Template.bind({});
+Section.args = {
+ title: 'A title',
+ content: 'The content.',
+};
diff --git a/src/components/atoms/layout/section.test.tsx b/src/components/atoms/layout/section.test.tsx
new file mode 100644
index 0000000..ca5f03a
--- /dev/null
+++ b/src/components/atoms/layout/section.test.tsx
@@ -0,0 +1,17 @@
+import { render, screen } from '@test-utils';
+import Section from './section';
+
+const title = 'Section title';
+const content = 'Section content.';
+
+describe('Section', () => {
+ it('renders a title (h2)', () => {
+ render(<Section title={title} content={content} />);
+ expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent(title);
+ });
+
+ it('renders a content', () => {
+ render(<Section title={title} content={content} />);
+ expect(screen.getByText(content)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/atoms/layout/section.tsx b/src/components/atoms/layout/section.tsx
new file mode 100644
index 0000000..f1bbb34
--- /dev/null
+++ b/src/components/atoms/layout/section.tsx
@@ -0,0 +1,57 @@
+import { ReactNode, VFC } from 'react';
+import Heading from '../headings/heading';
+import styles from './section.module.scss';
+
+export type SectionVariant = 'dark' | 'light';
+
+export type SectionProps = {
+ /**
+ * Set additional classnames to the section element.
+ */
+ className?: string;
+ /**
+ * The section content.
+ */
+ content: ReactNode;
+ /**
+ * The section title.
+ */
+ title: string;
+ /**
+ * The section variant.
+ */
+ variant?: SectionVariant;
+ /**
+ * Add a border at the bottom of the section. Default: true.
+ */
+ withBorder?: boolean;
+};
+
+/**
+ * Section component
+ *
+ * Render a section element.
+ */
+const Section: VFC<SectionProps> = ({
+ className = '',
+ content,
+ title,
+ variant = 'dark',
+ withBorder = true,
+}) => {
+ const borderClass = withBorder ? styles[`wrapper--borders`] : '';
+ const variantClass = styles[`wrapper--${variant}`];
+
+ return (
+ <section
+ className={`${styles.wrapper} ${borderClass} ${variantClass} ${className}`}
+ >
+ <Heading level={2} className={styles.title}>
+ {title}
+ </Heading>
+ <div className={styles.body}>{content}</div>
+ </section>
+ );
+};
+
+export default Section;