diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-09-27 15:40:16 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-10-24 12:25:00 +0200 |
| commit | ba793e043e4d8515b1a9ea490ee2c5f92b1fd6c2 (patch) | |
| tree | f7240a681fb3ee8c886a0c9ec3944082ba2d89bd /src/components/atoms | |
| parent | 388e687857345c85ee550cd5da472675e05a6ff5 (diff) | |
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
Diffstat (limited to 'src/components/atoms')
| -rw-r--r-- | src/components/atoms/layout/section.test.tsx | 18 | ||||
| -rw-r--r-- | src/components/atoms/layout/section.tsx | 54 | ||||
| -rw-r--r-- | src/components/atoms/layout/section/index.ts | 1 | ||||
| -rw-r--r-- | src/components/atoms/layout/section/section.module.scss (renamed from src/components/atoms/layout/section.module.scss) | 12 | ||||
| -rw-r--r-- | src/components/atoms/layout/section/section.stories.tsx (renamed from src/components/atoms/layout/section.stories.tsx) | 71 | ||||
| -rw-r--r-- | src/components/atoms/layout/section/section.test.tsx | 27 | ||||
| -rw-r--r-- | src/components/atoms/layout/section/section.tsx | 50 |
7 files changed, 111 insertions, 122 deletions
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(<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 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<HTMLElement>, - '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<SectionProps> = ({ - 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 ( - <section {...props} className={sectionClass}> - <Heading level={2} className={styles.title}> - {title} - </Heading> - <div className={styles.body}>{content}</div> - </section> - ); -}; 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.module.scss b/src/components/atoms/layout/section/section.module.scss index 8a33848..771b8e3 100644 --- a/src/components/atoms/layout/section.module.scss +++ b/src/components/atoms/layout/section/section.module.scss @@ -1,9 +1,10 @@ -@use "../../../styles/abstracts/functions" as fun; -@use "../../../styles/abstracts/placeholders"; +@use "../../../../styles/abstracts/functions" as fun; +@use "../../../../styles/abstracts/placeholders"; .wrapper { @extend %grid; + row-gap: var(--spacing-sm); padding: var(--spacing-md) 0; &--borders { @@ -17,9 +18,8 @@ &--light { background: var(--color-bg); } -} -.body, -.title { - grid-column: 2; + > * { + grid-column: 2; + } } diff --git a/src/components/atoms/layout/section.stories.tsx b/src/components/atoms/layout/section/section.stories.tsx index 8ab2729..0a3388b 100644 --- a/src/components/atoms/layout/section.stories.tsx +++ b/src/components/atoms/layout/section/section.stories.tsx @@ -1,4 +1,5 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Heading } from '../../headings'; import { Section } from './section'; /** @@ -8,41 +9,29 @@ export default { title: 'Atoms/Layout/Section', component: Section, args: { - variant: 'dark', - withBorder: true, + hasBorder: true, + variant: 'light', }, 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', - }, + children: { description: 'The section content.', type: { - name: 'string', + name: 'function', required: true, }, }, - title: { + hasBorder: { control: { - type: 'text', + type: 'boolean', + }, + description: 'Add a border at the bottom of the section.', + table: { + category: 'Styles', + defaultValue: { summary: false }, }, - description: 'The section title.', type: { - name: 'string', - required: true, + name: 'boolean', + required: false, }, }, variant: { @@ -60,20 +49,6 @@ export default { 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 Section>; @@ -86,8 +61,12 @@ const Template: ComponentStory<typeof Section> = (args) => ( */ export const Light = Template.bind({}); Light.args = { - title: 'A title', - content: 'The content.', + children: ( + <> + <Heading level={2}>A section title</Heading> + <div>The body</div> + </> + ), variant: 'light', }; @@ -96,7 +75,11 @@ Light.args = { */ export const Dark = Template.bind({}); Dark.args = { - title: 'A title', - content: 'The content.', + children: ( + <> + <Heading level={2}>A section title</Heading> + <div>The body</div> + </> + ), 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(<Section>{content}</Section>); + expect(rtlScreen.getByText(content)).toBeInTheDocument(); + }); + + it('renders a section with border', () => { + render(<Section hasBorder>{content}</Section>); + expect(rtlScreen.getByText(content)).toHaveClass('wrapper--borders'); + }); + + it('renders a light section', () => { + render(<Section variant="light">{content}</Section>); + expect(rtlScreen.getByText(content)).toHaveClass('wrapper--light'); + }); + + it('renders a dark section', () => { + render(<Section variant="dark">{content}</Section>); + 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<HTMLAttributes<HTMLElement>, '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<HTMLElement, SectionProps> = ( + { 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 ( + <section {...props} className={sectionClass} ref={ref}> + {children} + </section> + ); +}; + +/** + * Section component + * + * Render a section element with a heading and a body. + */ +export const Section = forwardRef(SectionWithRef); |
