diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-22 18:12:32 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-22 19:01:04 +0100 |
| commit | 329e7c89bac50be9db2c6d2ec6751ab0ffad42ac (patch) | |
| tree | e389de0a3ccda15fa3fb0dbaace185c905449f7b /src/components/molecules | |
| parent | 0ac690339083f01a0b12a74ec117eeccd055e932 (diff) | |
refactor(components): replace items prop in Grid with children prop
It is easier to read and to maintain this way. The `items` prop was not
useful since we are not manipulating the items. Changes:
* extract GridItem component from Grid component
* replace `items` prop of type Array<ReactNode> with `children` prop of
type ReactNode
* remove GridItem styles
Diffstat (limited to 'src/components/molecules')
| -rw-r--r-- | src/components/molecules/grid/grid-item.tsx | 15 | ||||
| -rw-r--r-- | src/components/molecules/grid/grid.module.scss | 9 | ||||
| -rw-r--r-- | src/components/molecules/grid/grid.stories.tsx | 224 | ||||
| -rw-r--r-- | src/components/molecules/grid/grid.test.tsx | 73 | ||||
| -rw-r--r-- | src/components/molecules/grid/grid.tsx | 23 | ||||
| -rw-r--r-- | src/components/molecules/grid/index.ts | 1 |
6 files changed, 238 insertions, 107 deletions
diff --git a/src/components/molecules/grid/grid-item.tsx b/src/components/molecules/grid/grid-item.tsx new file mode 100644 index 0000000..0592cec --- /dev/null +++ b/src/components/molecules/grid/grid-item.tsx @@ -0,0 +1,15 @@ +import { type ForwardRefRenderFunction, forwardRef } from 'react'; +import { ListItem, type ListItemProps } from '../../atoms'; + +export type GridItemProps = ListItemProps; + +const GridItemWithRef: ForwardRefRenderFunction< + HTMLLIElement, + GridItemProps +> = ({ children, ...props }, ref) => ( + <ListItem {...props} ref={ref}> + {children} + </ListItem> +); + +export const GridItem = forwardRef(GridItemWithRef); diff --git a/src/components/molecules/grid/grid.module.scss b/src/components/molecules/grid/grid.module.scss index e253a89..f13af30 100644 --- a/src/components/molecules/grid/grid.module.scss +++ b/src/components/molecules/grid/grid.module.scss @@ -30,12 +30,3 @@ ); } } - -.item { - display: flex; - flex-flow: row wrap; - - > * { - flex: 1; - } -} diff --git a/src/components/molecules/grid/grid.stories.tsx b/src/components/molecules/grid/grid.stories.tsx index ce3ee2b..4e12af4 100644 --- a/src/components/molecules/grid/grid.stories.tsx +++ b/src/components/molecules/grid/grid.stories.tsx @@ -1,6 +1,6 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; -import type { FC, ReactNode } from 'react'; import { Grid } from './grid'; +import { GridItem } from './grid-item'; export default { title: 'Molecules/Grid', @@ -19,107 +19,185 @@ export default { const Template: ComponentStory<typeof Grid> = (args) => <Grid {...args} />; -type ItemProps = { - children: ReactNode; -}; - -const Item: FC<ItemProps> = ({ children }) => ( - <div style={{ border: '1px solid #000', padding: '1rem' }}>{children}</div> -); - export const Default = Template.bind({}); Default.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - { id: 'item-5', item: <Item>Item 5</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 5 + </GridItem> + </> + ), }; export const OneColumn = Template.bind({}); OneColumn.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + </> + ), col: 1, gap: 'sm', }; export const TwoColumns = Template.bind({}); TwoColumns.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + </> + ), col: 2, gap: 'sm', }; export const ThreeColumns = Template.bind({}); ThreeColumns.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + </> + ), col: 3, gap: 'sm', }; export const FixedSize = Template.bind({}); FixedSize.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - { id: 'item-5', item: <Item>Item 5</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 5 + </GridItem> + </> + ), size: '300px', gap: 'sm', }; export const MaxSize = Template.bind({}); MaxSize.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - { id: 'item-5', item: <Item>Item 5</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 5 + </GridItem> + </> + ), sizeMax: '300px', gap: 'sm', }; export const MinSize = Template.bind({}); MinSize.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - { id: 'item-5', item: <Item>Item 5</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 5 + </GridItem> + </> + ), sizeMin: '100px', gap: 'sm', }; export const MinAndMaxSize = Template.bind({}); MinAndMaxSize.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - { id: 'item-5', item: <Item>Item 5</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 5 + </GridItem> + </> + ), sizeMax: '300px', sizeMin: '100px', gap: 'sm', @@ -127,13 +205,25 @@ MinAndMaxSize.args = { export const Fill = Template.bind({}); Fill.args = { - items: [ - { id: 'item-1', item: <Item>Item 1</Item> }, - { id: 'item-2', item: <Item>Item 2</Item> }, - { id: 'item-3', item: <Item>Item 3</Item> }, - { id: 'item-4', item: <Item>Item 4</Item> }, - { id: 'item-5', item: <Item>Item 5</Item> }, - ], + children: ( + <> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 1 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 2 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 3 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 4 + </GridItem> + <GridItem style={{ border: '1px solid #000', padding: '1rem' }}> + Item 5 + </GridItem> + </> + ), col: 'auto-fill', sizeMin: '100px', gap: 'sm', diff --git a/src/components/molecules/grid/grid.test.tsx b/src/components/molecules/grid/grid.test.tsx index 212bdc4..e69610d 100644 --- a/src/components/molecules/grid/grid.test.tsx +++ b/src/components/molecules/grid/grid.test.tsx @@ -1,18 +1,25 @@ import { describe, expect, it } from '@jest/globals'; import { render, screen as rtlScreen } from '@testing-library/react'; -import { Grid, type GridItem } from './grid'; - -const items: GridItem[] = [ - { id: 'item-1', item: 'Item 1' }, - { id: 'item-2', item: 'Item 2' }, - { id: 'item-3', item: 'Item 3' }, - { id: 'item-4', item: 'Item 4' }, - { id: 'item-5', item: 'Item 5' }, +import { Grid } from './grid'; +import { GridItem } from './grid-item'; + +const items = [ + { id: 'item-1', contents: 'Item 1' }, + { id: 'item-2', contents: 'Item 2' }, + { id: 'item-3', contents: 'Item 3' }, + { id: 'item-4', contents: 'Item 4' }, + { id: 'item-5', contents: 'Item 5' }, ]; describe('Grid', () => { it('render a list of items as grid', () => { - render(<Grid items={items} />); + render( + <Grid> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getAllByRole('listitem')).toHaveLength(items.length); }); @@ -20,7 +27,13 @@ describe('Grid', () => { it('can render a list of items with fixed size', () => { const size = '200px'; - render(<Grid items={items} size={size} />); + render( + <Grid size={size}> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getByRole('list')).toHaveClass('wrapper--has-fixed-size'); expect(rtlScreen.getByRole('list')).toHaveStyle({ '--size': size }); @@ -29,7 +42,13 @@ describe('Grid', () => { it('can render a list of items with min size', () => { const size = '200px'; - render(<Grid items={items} sizeMin={size} />); + render( + <Grid sizeMin={size}> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getByRole('list')).toHaveClass('wrapper--has-min-size'); expect(rtlScreen.getByRole('list')).toHaveStyle({ '--size-min': size }); @@ -38,7 +57,13 @@ describe('Grid', () => { it('can render a list of items with max size', () => { const size = '200px'; - render(<Grid items={items} sizeMax={size} />); + render( + <Grid sizeMax={size}> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getByRole('list')).toHaveStyle({ '--size-max': size }); }); @@ -46,7 +71,13 @@ describe('Grid', () => { it('can render a list of items with a custom gap', () => { const gap = 'md'; - render(<Grid items={items} gap={gap} />); + render( + <Grid gap={gap}> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getByRole('list')).toHaveStyle({ '--gap': `var(--spacing-${gap})`, @@ -56,13 +87,25 @@ describe('Grid', () => { it('can render a list of items with an explicit number of columns', () => { const col = 4; - render(<Grid col={col} items={items} />); + render( + <Grid col={col}> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getByRole('list')).toHaveStyle(`--col: ${col}`); }); it('can render a centered list of items', () => { - render(<Grid isCentered items={items} />); + render( + <Grid isCentered> + {items.map((item) => ( + <GridItem key={item.id}>{item.contents}</GridItem> + ))} + </Grid> + ); expect(rtlScreen.getByRole('list')).toHaveClass('wrapper--is-centered'); }); diff --git a/src/components/molecules/grid/grid.tsx b/src/components/molecules/grid/grid.tsx index ca920f8..3d0ecf1 100644 --- a/src/components/molecules/grid/grid.tsx +++ b/src/components/molecules/grid/grid.tsx @@ -5,19 +5,18 @@ import { type CSSProperties, } from 'react'; import type { Spacing } from '../../../types'; -import { List, ListItem, type ListProps } from '../../atoms'; +import { List, type ListProps } from '../../atoms'; import styles from './grid.module.scss'; -export type GridItem = { - id: string; - item: ReactNode; -}; - export type GridProps<T extends boolean> = Omit< ListProps<T, false>, 'children' | 'hideMarker' | 'isHierarchical' | 'isInline' | 'spacing' > & { /** + * The grid items. + */ + children: ReactNode; + /** * Control the number of column. * * @default 'auto-fit' @@ -36,10 +35,6 @@ export type GridProps<T extends boolean> = Omit< */ isCentered?: boolean; /** - * The grid items. - */ - items: GridItem[]; - /** * Define a fixed size for each item. * * You should either use `size` or `sizeMax`/`sizeMin` not both. @@ -67,11 +62,11 @@ export type GridProps<T extends boolean> = Omit< const GridWithRef = <T extends boolean>( { + children, className = '', col = 'auto-fit', gap, isCentered = false, - items, size, sizeMax, sizeMin, @@ -104,11 +99,7 @@ const GridWithRef = <T extends boolean>( ref={ref} style={gridStyles} > - {items.map(({ id, item }) => ( - <ListItem className={styles.item} key={id}> - {item} - </ListItem> - ))} + {children} </List> ); }; diff --git a/src/components/molecules/grid/index.ts b/src/components/molecules/grid/index.ts index d24d1bd..d657dcb 100644 --- a/src/components/molecules/grid/index.ts +++ b/src/components/molecules/grid/index.ts @@ -1 +1,2 @@ export * from './grid'; +export * from './grid-item'; |
