From ba793e043e4d8515b1a9ea490ee2c5f92b1fd6c2 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Wed, 27 Sep 2023 15:40:16 +0200 Subject: refactor(components): rewrite Section component * Make it compliant with ESlint rules * Remove mandatory heading, it now depends on the consumer * Change defaults for hasBorder and variant --- src/components/atoms/layout/section.module.scss | 25 ----- src/components/atoms/layout/section.stories.tsx | 102 --------------------- src/components/atoms/layout/section.test.tsx | 18 ---- src/components/atoms/layout/section.tsx | 54 ----------- src/components/atoms/layout/section/index.ts | 1 + .../atoms/layout/section/section.module.scss | 25 +++++ .../atoms/layout/section/section.stories.tsx | 85 +++++++++++++++++ .../atoms/layout/section/section.test.tsx | 27 ++++++ src/components/atoms/layout/section/section.tsx | 50 ++++++++++ 9 files changed, 188 insertions(+), 199 deletions(-) delete mode 100644 src/components/atoms/layout/section.module.scss delete mode 100644 src/components/atoms/layout/section.stories.tsx delete mode 100644 src/components/atoms/layout/section.test.tsx delete mode 100644 src/components/atoms/layout/section.tsx create mode 100644 src/components/atoms/layout/section/index.ts create mode 100644 src/components/atoms/layout/section/section.module.scss create mode 100644 src/components/atoms/layout/section/section.stories.tsx create mode 100644 src/components/atoms/layout/section/section.test.tsx create mode 100644 src/components/atoms/layout/section/section.tsx (limited to 'src/components/atoms/layout') diff --git a/src/components/atoms/layout/section.module.scss b/src/components/atoms/layout/section.module.scss deleted file mode 100644 index 8a33848..0000000 --- a/src/components/atoms/layout/section.module.scss +++ /dev/null @@ -1,25 +0,0 @@ -@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 deleted file mode 100644 index 8ab2729..0000000 --- a/src/components/atoms/layout/section.stories.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Section } from './section'; - -/** - * Section - Storybook Meta - */ -export default { - title: 'Atoms/Layout/Section', - component: Section, - 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; - -const Template: ComponentStory = (args) => ( -
-); - -/** - * Section Stories - Light - */ -export const Light = Template.bind({}); -Light.args = { - title: 'A title', - content: 'The content.', - variant: 'light', -}; - -/** - * Section Stories - Dark - */ -export const Dark = Template.bind({}); -Dark.args = { - title: 'A title', - content: 'The content.', - variant: 'dark', -}; diff --git a/src/components/atoms/layout/section.test.tsx b/src/components/atoms/layout/section.test.tsx deleted file mode 100644 index 6a2805d..0000000 --- a/src/components/atoms/layout/section.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { Section } from './section'; - -const title = 'Section title'; -const content = 'Section content.'; - -describe('Section', () => { - it('renders a title (h2)', () => { - render(
); - expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent(title); - }); - - it('renders a content', () => { - render(
); - expect(screen.getByText(content)).toBeInTheDocument(); - }); -}); diff --git a/src/components/atoms/layout/section.tsx b/src/components/atoms/layout/section.tsx deleted file mode 100644 index 107e80a..0000000 --- a/src/components/atoms/layout/section.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { FC, HTMLAttributes, ReactNode } from 'react'; -import { Heading } from '../headings'; -import styles from './section.module.scss'; - -export type SectionVariant = 'dark' | 'light'; - -export type SectionProps = Omit< - HTMLAttributes, - 'children' | 'content' -> & { - /** - * The section content. - */ - content: ReactNode | 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. - */ -export const Section: FC = ({ - className = '', - content, - title, - variant = 'dark', - withBorder = true, - ...props -}) => { - const borderClass = withBorder ? styles[`wrapper--borders`] : ''; - const variantClass = styles[`wrapper--${variant}`]; - const sectionClass = `${styles.wrapper} ${borderClass} ${variantClass} ${className}`; - - return ( -
- - {title} - -
{content}
-
- ); -}; diff --git a/src/components/atoms/layout/section/index.ts b/src/components/atoms/layout/section/index.ts new file mode 100644 index 0000000..2786cf0 --- /dev/null +++ b/src/components/atoms/layout/section/index.ts @@ -0,0 +1 @@ +export * from './section'; diff --git a/src/components/atoms/layout/section/section.module.scss b/src/components/atoms/layout/section/section.module.scss new file mode 100644 index 0000000..771b8e3 --- /dev/null +++ b/src/components/atoms/layout/section/section.module.scss @@ -0,0 +1,25 @@ +@use "../../../../styles/abstracts/functions" as fun; +@use "../../../../styles/abstracts/placeholders"; + +.wrapper { + @extend %grid; + + row-gap: var(--spacing-sm); + 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); + } + + > * { + grid-column: 2; + } +} diff --git a/src/components/atoms/layout/section/section.stories.tsx b/src/components/atoms/layout/section/section.stories.tsx new file mode 100644 index 0000000..0a3388b --- /dev/null +++ b/src/components/atoms/layout/section/section.stories.tsx @@ -0,0 +1,85 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Heading } from '../../headings'; +import { Section } from './section'; + +/** + * Section - Storybook Meta + */ +export default { + title: 'Atoms/Layout/Section', + component: Section, + args: { + hasBorder: true, + variant: 'light', + }, + argTypes: { + children: { + description: 'The section content.', + type: { + name: 'function', + required: true, + }, + }, + hasBorder: { + control: { + type: 'boolean', + }, + description: 'Add a border at the bottom of the section.', + table: { + category: 'Styles', + defaultValue: { summary: false }, + }, + type: { + name: 'boolean', + required: false, + }, + }, + variant: { + control: { + type: 'select', + }, + description: 'The section variant.', + options: ['light', 'dark'], + table: { + category: 'Styles', + defaultValue: { summary: 'dark' }, + }, + type: { + name: 'string', + required: false, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( +
+); + +/** + * Section Stories - Light + */ +export const Light = Template.bind({}); +Light.args = { + children: ( + <> + A section title +
The body
+ + ), + variant: 'light', +}; + +/** + * Section Stories - Dark + */ +export const Dark = Template.bind({}); +Dark.args = { + children: ( + <> + A section title +
The body
+ + ), + variant: 'dark', +}; diff --git a/src/components/atoms/layout/section/section.test.tsx b/src/components/atoms/layout/section/section.test.tsx new file mode 100644 index 0000000..85305c0 --- /dev/null +++ b/src/components/atoms/layout/section/section.test.tsx @@ -0,0 +1,27 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { Section } from './section'; + +const content = 'Section content.'; + +describe('Section', () => { + it('renders its body', () => { + render(
{content}
); + expect(rtlScreen.getByText(content)).toBeInTheDocument(); + }); + + it('renders a section with border', () => { + render(
{content}
); + expect(rtlScreen.getByText(content)).toHaveClass('wrapper--borders'); + }); + + it('renders a light section', () => { + render(
{content}
); + expect(rtlScreen.getByText(content)).toHaveClass('wrapper--light'); + }); + + it('renders a dark section', () => { + render(
{content}
); + expect(rtlScreen.getByText(content)).toHaveClass('wrapper--dark'); + }); +}); diff --git a/src/components/atoms/layout/section/section.tsx b/src/components/atoms/layout/section/section.tsx new file mode 100644 index 0000000..63c658a --- /dev/null +++ b/src/components/atoms/layout/section/section.tsx @@ -0,0 +1,50 @@ +import { + forwardRef, + type ForwardRefRenderFunction, + type HTMLAttributes, + type ReactNode, +} from 'react'; +import styles from './section.module.scss'; + +export type SectionVariant = 'dark' | 'light'; + +export type SectionProps = Omit, 'children'> & { + /** + * The section content. + */ + children: ReactNode | ReactNode[]; + /** + * Add a border at the bottom of the section. + * + * @default false + */ + hasBorder?: boolean; + /** + * The section variant. + * + * @default 'light' + */ + variant?: SectionVariant; +}; + +const SectionWithRef: ForwardRefRenderFunction = ( + { children, className = '', hasBorder = false, variant = 'light', ...props }, + ref +) => { + const borderClass = hasBorder ? styles[`wrapper--borders`] : ''; + const variantClass = styles[`wrapper--${variant}`]; + const sectionClass = `${styles.wrapper} ${borderClass} ${variantClass} ${className}`; + + return ( +
+ {children} +
+ ); +}; + +/** + * Section component + * + * Render a section element with a heading and a body. + */ +export const Section = forwardRef(SectionWithRef); -- cgit v1.2.3