aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-04-21 17:03:45 +0200
committerArmand Philippot <git@armandphilippot.com>2022-04-21 17:03:45 +0200
commit34502bd004c2522a8f2a217da3adf51586d1dec3 (patch)
tree548ae3e34d6f8e3d2897a126bb8971a7fe2d278b /src/components/molecules
parent2b51960da1bd907f5855869d3eee53565ca7cbfc (diff)
chore: add a PageHeader component
Diffstat (limited to 'src/components/molecules')
-rw-r--r--src/components/molecules/layout/page-header.module.scss63
-rw-r--r--src/components/molecules/layout/page-header.stories.tsx103
-rw-r--r--src/components/molecules/layout/page-header.test.tsx18
-rw-r--r--src/components/molecules/layout/page-header.tsx49
4 files changed, 233 insertions, 0 deletions
diff --git a/src/components/molecules/layout/page-header.module.scss b/src/components/molecules/layout/page-header.module.scss
new file mode 100644
index 0000000..93f7595
--- /dev/null
+++ b/src/components/molecules/layout/page-header.module.scss
@@ -0,0 +1,63 @@
+@use "@styles/abstracts/functions" as fun;
+@use "@styles/abstracts/mixins" as mix;
+@use "@styles/abstracts/placeholders";
+
+.wrapper {
+ @extend %grid;
+
+ &::before,
+ &::after {
+ content: "";
+ width: 100%;
+ height: 100%;
+ background: var(--color-bg-secondary);
+ border-top: fun.convert-px(3) solid var(--color-border-light);
+ border-bottom: fun.convert-px(3) solid var(--color-border-light);
+ }
+
+ &::before {
+ grid-column: 1;
+ justify-self: start;
+ border-right: fun.convert-px(3) solid var(--color-border-light);
+ }
+
+ &::after {
+ grid-column: 3;
+ justify-self: end;
+ border-left: fun.convert-px(3) solid var(--color-border-light);
+ }
+}
+
+.body {
+ grid-column: 2;
+ display: flex;
+ flex-flow: column wrap;
+ row-gap: var(--spacing-sm);
+}
+
+.title {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ position: relative;
+
+ &::before,
+ &::after {
+ content: "";
+ width: 100%;
+ height: fun.convert-px(4);
+ background: radial-gradient(
+ ellipse at center,
+ var(--color-primary-light),
+ var(--color-primary-dark)
+ );
+ }
+}
+
+.meta {
+ @include mix.media("screen") {
+ @include mix.dimensions("xs") {
+ font-size: var(--font-size-sm);
+ }
+ }
+}
diff --git a/src/components/molecules/layout/page-header.stories.tsx b/src/components/molecules/layout/page-header.stories.tsx
new file mode 100644
index 0000000..6054845
--- /dev/null
+++ b/src/components/molecules/layout/page-header.stories.tsx
@@ -0,0 +1,103 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import PageHeader from './page-header';
+
+/**
+ * Page Header - Storybook Meta
+ */
+export default {
+ title: 'Molecules/Layout/PageHeader',
+ component: PageHeader,
+ argTypes: {
+ intro: {
+ control: {
+ type: 'text',
+ },
+ description: 'The page introduction.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ meta: {
+ description: 'The page metadata.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'object',
+ required: false,
+ value: {},
+ },
+ },
+ title: {
+ control: {
+ type: 'text',
+ },
+ description: 'The page title.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
+ },
+} as ComponentMeta<typeof PageHeader>;
+
+const Template: ComponentStory<typeof PageHeader> = (args) => (
+ <PageHeader {...args} />
+);
+
+const meta = {
+ publication: { name: 'Published on:', value: 'April 9th 2022' },
+ categories: {
+ name: 'Categories:',
+ value: [
+ <a key="category1" href="#">
+ Category 1
+ </a>,
+ <a key="category2" href="#">
+ Category 2
+ </a>,
+ ],
+ },
+};
+
+/**
+ * Page Header Stories - Default
+ */
+export const Default = Template.bind({});
+Default.args = {
+ title: 'Excepturi nesciunt illum',
+};
+
+/**
+ * Page Header Stories - With introduction
+ */
+export const WithIntro = Template.bind({});
+WithIntro.args = {
+ intro:
+ 'Minima dolor nihil. Velit atque odit totam enim. Quisquam reprehenderit ut et inventore et nihil libero exercitationem. Cumque similique magni placeat et. Et sed est cumque labore. Et quia similique.',
+ title: 'Excepturi nesciunt illum',
+};
+
+/**
+ * Page Header Stories - With meta
+ */
+export const WithMeta = Template.bind({});
+WithMeta.args = {
+ meta,
+ title: 'Excepturi nesciunt illum',
+};
+
+/**
+ * Page Header Stories - With introduction and meta
+ */
+export const WithIntroAndMeta = Template.bind({});
+WithIntroAndMeta.args = {
+ intro:
+ 'Minima dolor nihil. Velit atque odit totam enim. Quisquam reprehenderit ut et inventore et nihil libero exercitationem. Cumque similique magni placeat et. Et sed est cumque labore. Et quia similique.',
+ meta,
+ title: 'Excepturi nesciunt illum',
+};
diff --git a/src/components/molecules/layout/page-header.test.tsx b/src/components/molecules/layout/page-header.test.tsx
new file mode 100644
index 0000000..329b54c
--- /dev/null
+++ b/src/components/molecules/layout/page-header.test.tsx
@@ -0,0 +1,18 @@
+import { render, screen } from '@test-utils';
+import PageHeader from './page-header';
+
+const title = 'Non nemo amet';
+const intro =
+ 'Suscipit omnis minima doloribus commodi. Laudantium similique ut enim voluptatem soluta maxime autem et.';
+
+describe('PageHeader', () => {
+ it('renders a title', () => {
+ render(<PageHeader title={title} intro={intro} />);
+ expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(title);
+ });
+
+ it('renders an introduction', () => {
+ render(<PageHeader title={title} intro={intro} />);
+ expect(screen.getByText(intro)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx
new file mode 100644
index 0000000..174e246
--- /dev/null
+++ b/src/components/molecules/layout/page-header.tsx
@@ -0,0 +1,49 @@
+import Heading from '@components/atoms/headings/heading';
+import styles from './page-header.module.scss';
+import Meta, { type MetaMap } from './meta';
+import { FC } from 'react';
+
+export type PageHeaderProps = {
+ /**
+ * Set additional classnames to the header element.
+ */
+ className?: string;
+ /**
+ * The page introduction.
+ */
+ intro?: string;
+ /**
+ * The page metadata.
+ */
+ meta?: MetaMap;
+ /**
+ * The page title.
+ */
+ title: string;
+};
+
+/**
+ * PageHeader component
+ *
+ * Render a header element with page title, meta and intro.
+ */
+const PageHeader: FC<PageHeaderProps> = ({
+ className = '',
+ intro,
+ meta,
+ title,
+}) => {
+ return (
+ <header className={`${styles.wrapper} ${className}`}>
+ <div className={styles.body}>
+ <Heading level={1} className={styles.title} withMargin={false}>
+ {title}
+ </Heading>
+ {meta && <Meta data={meta} className={styles.meta} layout="inline" />}
+ {intro && <div>{intro}</div>}
+ </div>
+ </header>
+ );
+};
+
+export default PageHeader;