aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-11-22 18:12:32 +0100
committerArmand Philippot <git@armandphilippot.com>2023-11-22 19:01:04 +0100
commit329e7c89bac50be9db2c6d2ec6751ab0ffad42ac (patch)
treee389de0a3ccda15fa3fb0dbaace185c905449f7b
parent0ac690339083f01a0b12a74ec117eeccd055e932 (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
-rw-r--r--src/components/mdx.tsx13
-rw-r--r--src/components/molecules/grid/grid-item.tsx15
-rw-r--r--src/components/molecules/grid/grid.module.scss9
-rw-r--r--src/components/molecules/grid/grid.stories.tsx224
-rw-r--r--src/components/molecules/grid/grid.test.tsx73
-rw-r--r--src/components/molecules/grid/grid.tsx23
-rw-r--r--src/components/molecules/grid/index.ts1
-rw-r--r--src/pages/index.tsx42
-rw-r--r--src/pages/projets/index.tsx113
9 files changed, 309 insertions, 204 deletions
diff --git a/src/components/mdx.tsx b/src/components/mdx.tsx
index f11dda5..9f0a4a5 100644
--- a/src/components/mdx.tsx
+++ b/src/components/mdx.tsx
@@ -2,7 +2,7 @@ import type { MDXComponents } from 'mdx/types';
import NextImage from 'next/image';
import type { AnchorHTMLAttributes, ImgHTMLAttributes, ReactNode } from 'react';
import { Figure, Heading, Link, List, ListItem } from './atoms';
-import { Code, Grid } from './molecules';
+import { Code, Grid, GridItem } from './molecules';
const Anchor = ({
children = '',
@@ -45,16 +45,15 @@ const Img = ({
return <img {...props} alt={alt} height={height} src={src} width={width} />;
};
-const Gallery = ({ children }: { children: ReactNode[] }) => (
+const Gallery = ({ children }: { children: ReactNode }) => (
<Grid
// eslint-disable-next-line react/jsx-no-literals
gap="sm"
- items={children.map((child, index) => {
- return { id: `${index}`, item: child };
- })}
// eslint-disable-next-line react/jsx-no-literals
sizeMin="250px"
- />
+ >
+ {children}
+ </Grid>
);
export const mdxComponents: MDXComponents = {
@@ -63,6 +62,8 @@ export const mdxComponents: MDXComponents = {
figure: ({ ref, ...props }) => <Figure {...props} />,
Figure,
Gallery,
+ Grid,
+ GridItem,
h1: ({ ref, ...props }) => <Heading {...props} level={1} />,
h2: ({ ref, ...props }) => <Heading {...props} level={2} />,
h3: ({ ref, ...props }) => <Heading {...props} level={3} />,
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';
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 32c2e7f..b8f754b 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -15,7 +15,6 @@ import {
CardTitle,
getLayout,
Grid,
- type GridItem,
Icon,
List,
ListItem,
@@ -151,12 +150,11 @@ const LibreLinks: FC = () => {
*/
const ShaarliLink: FC = () => {
const intl = useIntl();
- const shaarliUrl = PERSONAL_LINKS.SHAARLI;
return (
<List className={styles.list} hideMarker isInline spacing="sm">
<ListItem>
- <ButtonLink isExternal to={shaarliUrl}>
+ <ButtonLink isExternal to={PERSONAL_LINKS.SHAARLI}>
{intl.formatMessage({
defaultMessage: 'Shaarli',
description: 'HomePage: link to Shaarli',
@@ -202,15 +200,10 @@ const MoreLinks: FC = () => {
);
};
-const StyledGrid = ({ children }: { children: ReactNode[] }) => (
- <Grid
- className={styles.columns}
- gap="sm"
- items={children.map((child, index) => {
- return { id: `${index}`, item: child };
- })}
- sizeMin="250px"
- />
+const StyledGrid = ({ children }: { children: ReactNode }) => (
+ <Grid className={styles.columns} gap="sm" sizeMin="250px">
+ {children}
+ </Grid>
);
/**
@@ -256,9 +249,11 @@ const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
* @returns {JSX.Element} - The cards list.
*/
const getRecentPosts = (): JSX.Element => {
- const posts: GridItem[] = recentPosts.map((post) => {
- return {
- item: (
+ const listClass = `${styles.list} ${styles['list--cards']}`;
+
+ return (
+ <Grid className={listClass} gap="sm" isCentered sizeMax="25ch">
+ {recentPosts.map((post) => (
<Card
cover={
post.cover ? (
@@ -270,6 +265,7 @@ const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
</CardCover>
) : undefined
}
+ key={post.id}
meta={
<CardMeta isCentered>
<MetaItem
@@ -288,20 +284,8 @@ const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
</CardHeader>
<CardFooter />
</Card>
- ),
- id: `${post.id}`,
- };
- });
- const listClass = `${styles.list} ${styles['list--cards']}`;
-
- return (
- <Grid
- className={listClass}
- gap="sm"
- isCentered
- items={posts}
- sizeMax="25ch"
- />
+ ))}
+ </Grid>
);
};
diff --git a/src/pages/projets/index.tsx b/src/pages/projets/index.tsx
index fc6eb5f..00c5a70 100644
--- a/src/pages/projets/index.tsx
+++ b/src/pages/projets/index.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable max-statements */
import type { GetStaticProps } from 'next';
import Head from 'next/head';
import NextImage from 'next/image';
@@ -14,7 +13,6 @@ import {
CardTitle,
getLayout,
Grid,
- type GridItem,
MetaList,
MetaItem,
Page,
@@ -59,59 +57,6 @@ const ProjectsPage: NextPageWithLayout<ProjectsPageProps> = ({ projects }) => {
description: 'Meta: technologies label',
id: 'ADQmDF',
});
-
- const items: GridItem[] = projects.map(
- ({ id, meta: projectMeta, slug, title: projectTitle }) => {
- const { cover, tagline, technologies } = projectMeta;
- const figureLabel = intl.formatMessage(
- {
- defaultMessage: '{title} cover',
- description: 'ProjectsPage: figure (cover) accessible name',
- id: 'FdF33B',
- },
- { title: projectTitle }
- );
-
- return {
- item: (
- <Card
- cover={
- cover ? (
- <CardCover aria-label={figureLabel} hasBorders>
- <NextImage {...cover} />
- </CardCover>
- ) : undefined
- }
- meta={
- technologies ? (
- <MetaList isCentered>
- <MetaItem
- hasBorderedValues
- hasInlinedValues
- isCentered
- label={metaLabel}
- value={technologies.map((techno) => {
- return { id: techno, value: techno };
- })}
- />
- </MetaList>
- ) : undefined
- }
- isCentered
- linkTo={`${ROUTES.PROJECTS}/${slug}`}
- >
- <CardHeader>
- <CardTitle>{projectTitle}</CardTitle>
- </CardHeader>
- <CardBody>{tagline}</CardBody>
- <CardFooter />
- </Card>
- ),
- id: `${id}`,
- };
- }
- );
-
const { asPath } = useRouter();
const webpageSchema = getWebPageSchema({
description: seo.description,
@@ -165,13 +110,57 @@ const ProjectsPage: NextPageWithLayout<ProjectsPageProps> = ({ projects }) => {
intro={<PageContent components={mdxComponents} />}
/>
<PageBody className={styles.body}>
- <Grid
- className={styles.list}
- gap="sm"
- isCentered
- items={items}
- sizeMax="30ch"
- />
+ <Grid className={styles.list} gap="sm" isCentered sizeMax="30ch">
+ {projects.map(
+ ({ id, meta: projectMeta, slug, title: projectTitle }) => {
+ const { cover, tagline, technologies } = projectMeta;
+ const figureLabel = intl.formatMessage(
+ {
+ defaultMessage: '{title} cover',
+ description: 'ProjectsPage: figure (cover) accessible name',
+ id: 'FdF33B',
+ },
+ { title: projectTitle }
+ );
+
+ return (
+ <Card
+ cover={
+ cover ? (
+ <CardCover aria-label={figureLabel} hasBorders>
+ <NextImage {...cover} />
+ </CardCover>
+ ) : undefined
+ }
+ key={id}
+ meta={
+ technologies ? (
+ <MetaList isCentered>
+ <MetaItem
+ hasBorderedValues
+ hasInlinedValues
+ isCentered
+ label={metaLabel}
+ value={technologies.map((techno) => {
+ return { id: techno, value: techno };
+ })}
+ />
+ </MetaList>
+ ) : undefined
+ }
+ isCentered
+ linkTo={`${ROUTES.PROJECTS}/${slug}`}
+ >
+ <CardHeader>
+ <CardTitle>{projectTitle}</CardTitle>
+ </CardHeader>
+ <CardBody>{tagline}</CardBody>
+ <CardFooter />
+ </Card>
+ );
+ }
+ )}
+ </Grid>
</PageBody>
</Page>
);