diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-17 19:46:08 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 |
| commit | c153f93dc8691a71dc76aad3dd618298da9d238a (patch) | |
| tree | 9c116c1472bab5585f98bceee19cfeca5041360d /src/components/organisms | |
| parent | 006b15b467a5cd835a6eab1b49023100bdc8f2e6 (diff) | |
refactor(components): rewrite Card component
* make the component more generic
* merge `<Summary />` and `<Comment />` styles into card component
to avoid repeating the same structure
* remove most of the props to use composition
However the CSS is a bit complex because of the two variants...
Also, the component should be refactored when the CSS pseudo-class
`:has` has enough support: the provider and the `cover` and `meta`
props should be removed.
Diffstat (limited to 'src/components/organisms')
| -rw-r--r-- | src/components/organisms/layout/cards-list.module.scss | 2 | ||||
| -rw-r--r-- | src/components/organisms/layout/cards-list.stories.tsx | 145 | ||||
| -rw-r--r-- | src/components/organisms/layout/cards-list.test.tsx | 93 | ||||
| -rw-r--r-- | src/components/organisms/layout/cards-list.tsx | 25 | ||||
| -rw-r--r-- | src/components/organisms/layout/comment.module.scss | 150 | ||||
| -rw-r--r-- | src/components/organisms/layout/comment.tsx | 121 | ||||
| -rw-r--r-- | src/components/organisms/layout/summary.module.scss | 105 | ||||
| -rw-r--r-- | src/components/organisms/layout/summary.tsx | 92 |
8 files changed, 264 insertions, 469 deletions
diff --git a/src/components/organisms/layout/cards-list.module.scss b/src/components/organisms/layout/cards-list.module.scss index 8b18c08..1665829 100644 --- a/src/components/organisms/layout/cards-list.module.scss +++ b/src/components/organisms/layout/cards-list.module.scss @@ -19,6 +19,6 @@ } } -.card { +.item > * { height: 100%; } diff --git a/src/components/organisms/layout/cards-list.stories.tsx b/src/components/organisms/layout/cards-list.stories.tsx index 03feee7..3f8e72a 100644 --- a/src/components/organisms/layout/cards-list.stories.tsx +++ b/src/components/organisms/layout/cards-list.stories.tsx @@ -1,4 +1,5 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Card, CardBody, CardHeader, CardTitle } from '../../molecules'; import { CardsList as CardsListComponent, type CardsListItem, @@ -10,39 +11,7 @@ import { export default { title: 'Organisms/Layout', component: CardsListComponent, - args: { - coverFit: 'cover', - kind: 'unordered', - }, argTypes: { - className: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the list wrapper.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - coverFit: { - control: { - type: 'select', - }, - description: 'The cover fit.', - options: ['fill', 'contain', 'cover', 'none', 'scale-down'], - table: { - category: 'Options', - defaultValue: { summary: 'cover' }, - }, - type: { - name: 'string', - required: false, - }, - }, items: { description: 'The cards data.', type: { @@ -51,33 +20,20 @@ export default { value: {}, }, }, - kind: { + isOrdered: { control: { - type: 'select', + type: 'boolean', }, - description: 'The list kind.', - options: ['ordered', 'unordered'], + description: 'Should the list be ordered?', table: { category: 'Options', - defaultValue: { summary: 'unordered' }, + defaultValue: { summary: false }, }, type: { - name: 'string', + name: 'boolean', required: false, }, }, - titleLevel: { - control: { - type: 'number', - min: 1, - max: 6, - }, - description: 'The heading level for each card.', - type: { - name: 'number', - required: true, - }, - }, }, } as ComponentMeta<typeof CardsListComponent>; @@ -88,63 +44,49 @@ const Template: ComponentStory<typeof CardsListComponent> = (args) => ( const items: CardsListItem[] = [ { id: 'card-1', - cover: { - alt: 'card 1 picture', - src: 'https://picsum.photos/640/480', - width: 640, - height: 480, - }, - meta: [ - { - id: 'categories', - label: 'Categories', - value: [ - { id: 'velit', value: 'Velit' }, - { id: 'ex', value: 'Ex' }, - { id: 'alias', value: 'Alias' }, - ], - }, - ], - tagline: 'Molestias ut error', - title: 'Et alias omnis', - url: '#', + card: ( + <Card> + <CardHeader> + <CardTitle>Et alias omnis</CardTitle> + </CardHeader> + <CardBody> + Rerum voluptatem sint sint sit dignissimos. Labore totam possimus + tempore atque veniam. Doloremque tenetur quidem beatae veritatis quo. + Quaerat voluptatem deleniti voluptas quia. Qui voluptatem iure iste + expedita et sed beatae. + </CardBody> + </Card> + ), }, { id: 'card-2', - cover: { - alt: 'card 2 picture', - src: 'https://picsum.photos/640/480', - width: 640, - height: 480, - }, - meta: [{ id: 'categories', label: 'Categories', value: 'Voluptas' }], - tagline: 'Quod vel accusamus', - title: 'Laboriosam doloremque mollitia', - url: '#', + card: ( + <Card> + <CardHeader> + <CardTitle>Fugiat magnam nesciunt</CardTitle> + </CardHeader> + <CardBody> + Sit corporis animi ea. Earum asperiores error et. Aliquid quia et + consequatur. Magnam sit ut facere explicabo vel dolorem earum + assumenda. Aspernatur inventore quod libero est. + </CardBody> + </Card> + ), }, { id: 'card-3', - cover: { - alt: 'card 3 picture', - src: 'https://picsum.photos/640/480', - width: 640, - height: 480, - }, - meta: [ - { - id: 'categories', - label: 'Categories', - value: [ - { id: 'quisquam', value: 'Quisquam' }, - { id: 'quia', value: 'Quia' }, - { id: 'sapiente', value: 'Sapiente' }, - { id: 'perspiciatis', value: 'Perspiciatis' }, - ], - }, - ], - tagline: 'Quo error eum', - title: 'Magni rem nulla', - url: '#', + card: ( + <Card> + <CardHeader> + <CardTitle>Asperiores eum quas</CardTitle> + </CardHeader> + <CardBody> + Doloremque ut cupiditate distinctio aperiam. Neque tempora unde + perferendis asperiores. Doloremque velit vel quam. Temporibus itaque + non non exercitationem. + </CardBody> + </Card> + ), }, ]; @@ -154,5 +96,4 @@ const items: CardsListItem[] = [ export const CardsList = Template.bind({}); CardsList.args = { items, - titleLevel: 2, }; diff --git a/src/components/organisms/layout/cards-list.test.tsx b/src/components/organisms/layout/cards-list.test.tsx index c9d6ae7..b04e109 100644 --- a/src/components/organisms/layout/cards-list.test.tsx +++ b/src/components/organisms/layout/cards-list.test.tsx @@ -1,73 +1,60 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen as rtlScreen } from '../../../../tests/utils'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { Card, CardBody, CardHeader, CardTitle } from '../../molecules'; import { CardsList, type CardsListItem } from './cards-list'; const items: CardsListItem[] = [ { id: 'card-1', - cover: { - alt: 'card 1 picture', - src: 'https://picsum.photos/640/480', - width: 640, - height: 480, - }, - meta: [ - { - id: 'categories', - label: 'Categories', - value: [ - { id: 'velit', value: 'Velit' }, - { id: 'ex', value: 'Ex' }, - { id: 'alias', value: 'Alias' }, - ], - }, - ], - tagline: 'Molestias ut error', - title: 'Et alias omnis', - url: '#', + card: ( + <Card> + <CardHeader> + <CardTitle>Et alias omnis</CardTitle> + </CardHeader> + <CardBody> + Rerum voluptatem sint sint sit dignissimos. Labore totam possimus + tempore atque veniam. Doloremque tenetur quidem beatae veritatis quo. + Quaerat voluptatem deleniti voluptas quia. Qui voluptatem iure iste + expedita et sed beatae. + </CardBody> + </Card> + ), }, { id: 'card-2', - cover: { - alt: 'card 2 picture', - src: 'https://picsum.photos/640/480', - width: 640, - height: 480, - }, - meta: [{ id: 'categories', label: 'Categories', value: 'Voluptas' }], - tagline: 'Quod vel accusamus', - title: 'Laboriosam doloremque mollitia', - url: '#', + card: ( + <Card> + <CardHeader> + <CardTitle>Fugiat magnam nesciunt</CardTitle> + </CardHeader> + <CardBody> + Sit corporis animi ea. Earum asperiores error et. Aliquid quia et + consequatur. Magnam sit ut facere explicabo vel dolorem earum + assumenda. Aspernatur inventore quod libero est. + </CardBody> + </Card> + ), }, { id: 'card-3', - cover: { - alt: 'card 3 picture', - src: 'https://picsum.photos/640/480', - width: 640, - height: 480, - }, - meta: [ - { - id: 'categories', - label: 'Categories', - value: [ - { id: 'quisquam', value: 'Quisquam' }, - { id: 'quia', value: 'Quia' }, - { id: 'sapiente', value: 'Sapiente' }, - { id: 'perspiciatis', value: 'Perspiciatis' }, - ], - }, - ], - tagline: 'Quo error eum', - title: 'Magni rem nulla', - url: '#', + card: ( + <Card> + <CardHeader> + <CardTitle>Asperiores eum quas</CardTitle> + </CardHeader> + <CardBody> + Doloremque ut cupiditate distinctio aperiam. Neque tempora unde + perferendis asperiores. Doloremque velit vel quam. Temporibus itaque + non non exercitationem. + </CardBody> + </Card> + ), }, ]; describe('CardsList', () => { it('renders a list of cards', () => { - render(<CardsList items={items} titleLevel={2} />); + render(<CardsList items={items} />); expect(rtlScreen.getAllByRole('heading', { level: 2 })).toHaveLength( items.length ); diff --git a/src/components/organisms/layout/cards-list.tsx b/src/components/organisms/layout/cards-list.tsx index 29add3b..4f920e8 100644 --- a/src/components/organisms/layout/cards-list.tsx +++ b/src/components/organisms/layout/cards-list.tsx @@ -1,16 +1,20 @@ -import type { FC } from 'react'; +import type { FC, ReactElement } from 'react'; import { List, ListItem } from '../../atoms'; -import { Card, type CardProps } from '../../molecules'; +import type { CardProps } from '../../molecules'; import styles from './cards-list.module.scss'; -export type CardsListItem = Omit<CardProps, 'className' | 'titleLevel'> & { +export type CardsListItem = { + /** + * The card. + */ + card: ReactElement<CardProps<string> | CardProps<undefined>>; /** * The card id. */ id: string; }; -export type CardsListProps = Pick<CardProps, 'titleLevel'> & { +export type CardsListProps = { /** * Set additional classnames to the list wrapper. */ @@ -36,7 +40,6 @@ export const CardsList: FC<CardsListProps> = ({ className = '', isOrdered = false, items, - titleLevel, }) => { const kindModifier = `wrapper--${isOrdered ? 'ordered' : 'unordered'}`; @@ -47,15 +50,9 @@ export const CardsList: FC<CardsListProps> = ({ isInline isOrdered={isOrdered} > - {items.map(({ id, ...item }) => ( - <ListItem key={id}> - <Card - {...item} - className={styles.card} - key={id} - id={id} - titleLevel={titleLevel} - /> + {items.map(({ id, card }) => ( + <ListItem className={styles.item} key={id}> + {card} </ListItem> ))} </List> diff --git a/src/components/organisms/layout/comment.module.scss b/src/components/organisms/layout/comment.module.scss index bf8aada..f26b3fe 100644 --- a/src/components/organisms/layout/comment.module.scss +++ b/src/components/organisms/layout/comment.module.scss @@ -2,132 +2,72 @@ @use "../../../styles/abstracts/mixins" as mix; @use "../../../styles/abstracts/placeholders"; -.wrapper { - padding: var(--spacing-md); - background: var(--color-bg); - border: fun.convert-px(1) solid var(--color-border); - box-shadow: - fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light), - fun.convert-px(4) fun.convert-px(4) fun.convert-px(3) fun.convert-px(-2) - var(--color-shadow); - - &--comment { - @include mix.media("screen") { - @include mix.dimensions("sm") { - display: grid; - grid-template-columns: minmax(0, #{fun.convert-px(150)}) minmax(0, 1fr); - column-gap: var(--spacing-lg); - } - } - } - - &--form { - display: flex; - flex-flow: column wrap; - place-content: center; - margin-top: var(--spacing-sm); - } - - .header { - display: flex; - flex-flow: column wrap; - align-items: center; - row-gap: var(--spacing-sm); - - @include mix.media("screen") { - @include mix.dimensions("sm") { - grid-row: 1 / 4; - } - } - } - - .author { - color: var(--color-primary-darker); - font-weight: 600; - text-align: center; - } - - .avatar { - width: fun.convert-px(85); - height: fun.convert-px(85); - position: relative; +.avatar { + img { border-radius: fun.convert-px(3); box-shadow: 0 0 0 fun.convert-px(1) var(--color-shadow-light), fun.convert-px(2) fun.convert-px(2) 0 fun.convert-px(1) var(--color-shadow); - - img { - border-radius: fun.convert-px(3); - } } +} - .date { - margin: var(--spacing-sm) 0; - font-size: var(--font-size-sm); +.author { + color: var(--color-primary-darker); + font-family: var(--font-family-regular); + font-size: var(--font-size-md); + font-weight: 600; + text-align: center; + text-shadow: none; +} - &__item { - justify-content: center; - } +.body { + overflow-wrap: break-word; - @include mix.media("screen") { - @include mix.dimensions("sm") { - margin: 0 0 var(--spacing-sm); + :global { + a { + @extend %link; - &__item { - justify-content: left; - } + &[hreflang], + &.download, + &.external { + @extend %link-with-icon; } - } - } - - .body { - overflow-wrap: break-word; - - :global { - a { - @extend %link; - &[hreflang], - &.download, - &.external { - @extend %link-with-icon; - } - - &[hreflang] { - @extend %link-with-lang; - } + &[hreflang] { + @extend %link-with-lang; + } - &[hreflang]:not(.download, .external) { - --is-icon-hidden: ""; - } + &[hreflang]:not(.download, .external) { + --is-icon-hidden: ""; + } - &.download { - @extend %download-link; - } + &.download { + @extend %download-link; + } - &.external { - @extend %external-link; - } + &.external { + @extend %external-link; + } - &.download, - &.external { - &:not([hreflang]) { - --is-lang-hidden: ""; - } + &.download, + &.external { + &:not([hreflang]) { + --is-lang-hidden: ""; } + } - &.external.download { - @extend %external-download-link; - } + &.external.download { + @extend %external-download-link; } } } +} + +.form { + margin-top: var(--spacing-sm); - .footer { - display: flex; - justify-content: flex-end; - align-items: center; - padding: var(--spacing-md) 0 0; + form > * { + margin-inline: auto; } } diff --git a/src/components/organisms/layout/comment.tsx b/src/components/organisms/layout/comment.tsx index cb2f16f..db7cb3a 100644 --- a/src/components/organisms/layout/comment.tsx +++ b/src/components/organisms/layout/comment.tsx @@ -7,7 +7,16 @@ import type { Comment as CommentSchema, WithContext } from 'schema-dts'; import type { SingleComment } from '../../../types'; import { useSettings } from '../../../utils/hooks'; import { Button, Link, Time } from '../../atoms'; -import { MetaList } from '../../molecules'; +import { + Card, + CardActions, + CardBody, + CardCover, + CardFooter, + CardHeader, + CardMeta, + CardTitle, +} from '../../molecules'; import { CommentForm, type CommentFormProps } from '../forms'; import styles from './comment.module.scss'; @@ -103,9 +112,6 @@ export const UserComment: FC<UserCommentProps> = ({ text: content, }; - const commentWrapperClass = `${styles.wrapper} ${styles['wrapper--comment']}`; - const formWrapperClass = `${styles.wrapper} ${styles['wrapper--form']}`; - return ( <> <Script @@ -113,10 +119,10 @@ export const UserComment: FC<UserCommentProps> = ({ id="schema-comments" type="application/ld+json" /> - <article className={commentWrapperClass} id={`comment-${id}`}> - <header className={styles.header}> - {author.avatar ? ( - <div className={styles.avatar}> + <Card + cover={ + author.avatar ? ( + <CardCover className={styles.avatar}> <NextImage {...props} alt={author.avatar.alt} @@ -124,55 +130,64 @@ export const UserComment: FC<UserCommentProps> = ({ src={author.avatar.src} style={{ objectFit: 'cover' }} /> - </div> - ) : null} - {author.website ? ( - <Link href={author.website} className={styles.author}> - {author.name} - </Link> - ) : ( - <span className={styles.author}>{author.name}</span> - )} - </header> - <MetaList - className={styles.date} - isInline - items={[ - { - id: 'publication-date', - label: intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'Comment: publication date label', - id: 'soj7do', - }), - value: ( - <Link href={`#comment-${id}`}> - <Time date={date} showTime /> - </Link> - ), - }, - ]} - /> - <div + </CardCover> + ) : undefined + } + id={`comment-${id}`} + variant={2} + > + <CardHeader> + <CardTitle className={styles.author} isFake> + {author.website ? ( + <Link href={author.website}>{author.name}</Link> + ) : ( + author.name + )} + </CardTitle> + <CardMeta + hasInlinedItems + items={[ + { + id: 'publication-date', + label: intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Comment: publication date label', + id: 'soj7do', + }), + value: ( + <Link href={`#comment-${id}`}> + <Time date={date} showTime /> + </Link> + ), + }, + ]} + /> + </CardHeader> + <CardBody className={styles.body} dangerouslySetInnerHTML={{ __html: content }} /> - <footer className={styles.footer}> - {canReply ? ( - <Button kind="tertiary" onClick={handleReply}> - {buttonLabel} - </Button> - ) : null} - </footer> - </article> + {canReply ? ( + <CardFooter> + <CardActions> + <Button kind="tertiary" onClick={handleReply}> + {buttonLabel} + </Button> + </CardActions> + </CardFooter> + ) : null} + </Card> {isReplying ? ( - <CommentForm - className={formWrapperClass} - Notice={Notice} - parentId={id} - saveComment={saveComment} - title={formTitle} - /> + <Card className={styles.form} variant={2}> + <CardBody> + <CommentForm + Notice={Notice} + parentId={id} + saveComment={saveComment} + title={formTitle} + /> + </CardBody> + </Card> ) : null} </> ); diff --git a/src/components/organisms/layout/summary.module.scss b/src/components/organisms/layout/summary.module.scss index ffc30ac..6e0af6a 100644 --- a/src/components/organisms/layout/summary.module.scss +++ b/src/components/organisms/layout/summary.module.scss @@ -1,34 +1,6 @@ -@use "../../../styles/abstracts/functions" as fun; -@use "../../../styles/abstracts/mixins" as mix; @use "../../../styles/abstracts/placeholders"; .wrapper { - display: grid; - grid-template-columns: minmax(0, 1fr); - column-gap: var(--spacing-md); - row-gap: var(--spacing-sm); - padding: var(--spacing-2xs) 0 var(--spacing-lg); - - @include mix.media("screen") { - @include mix.dimensions("xs") { - padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-md); - border: fun.convert-px(1) solid var(--color-primary-dark); - border-radius: fun.convert-px(3); - box-shadow: - fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0 - var(--color-shadow), - fun.convert-px(3) fun.convert-px(3) fun.convert-px(3) fun.convert-px(-1) - var(--color-shadow-light), - fun.convert-px(5) fun.convert-px(5) fun.convert-px(7) fun.convert-px(-1) - var(--color-shadow-light); - } - - @include mix.dimensions("sm") { - grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); - grid-template-rows: repeat(3, max-content); - } - } - &:hover { .icon { :global { @@ -38,86 +10,15 @@ } } -.cover { - display: inline-flex; - flex-flow: column nowrap; - justify-content: center; - width: auto; - height: fun.convert-px(100); - max-width: 100%; - border: fun.convert-px(1) solid var(--color-border); - object-fit: scale-down; - - @include mix.media("screen") { - @include mix.dimensions("sm") { - grid-column: 2; - grid-row: 1; - } - } -} - -.header { - @include mix.media("screen") { - @include mix.dimensions("sm") { - grid-column: 1; - grid-row: 1; - align-self: center; - } - } -} - -.body { - @include mix.media("screen") { - @include mix.dimensions("sm") { - grid-column: 1; - grid-row: 2; - } - } -} - -.footer { - @include mix.media("screen") { - @include mix.dimensions("sm") { - grid-column: 2; - grid-row: 2 / 4; - } - } -} - -.link { - display: block; - width: fit-content; -} - .title { - margin: 0; - background: none; - color: inherit; font-size: var(--font-size-2xl); - text-shadow: none; -} - -.read-more { - display: flex; - flex-flow: row nowrap; - column-gap: var(--spacing-xs); - width: max-content; - margin: var(--spacing-sm) 0 0; } -.meta { - flex-flow: row wrap; - font-size: var(--font-size-sm); - - @include mix.media("screen") { - @include mix.dimensions("sm") { - flex-flow: column wrap; - margin-top: 0; - } +.intro { + > *:last-child { + margin-bottom: 0; } -} -.intro { :global { a { @extend %link; diff --git a/src/components/organisms/layout/summary.tsx b/src/components/organisms/layout/summary.tsx index 4fe7632..0c95f90 100644 --- a/src/components/organisms/layout/summary.tsx +++ b/src/components/organisms/layout/summary.tsx @@ -3,16 +3,18 @@ import type { FC, ReactNode } from 'react'; import { useIntl } from 'react-intl'; import type { Article, Meta as MetaType } from '../../../types'; import { useReadingTime } from '../../../utils/hooks'; +import { ButtonLink, type HeadingLevel, Icon, Link, Time } from '../../atoms'; import { - ButtonLink, - Heading, - type HeadingLevel, - Icon, - Link, - Figure, - Time, -} from '../../atoms'; -import { MetaList, type MetaItemData } from '../../molecules'; + Card, + CardActions, + CardBody, + CardCover, + CardFooter, + CardHeader, + CardMeta, + CardTitle, + type MetaItemData, +} from '../../molecules'; import styles from './summary.module.scss'; export type Cover = Pick<NextImageProps, 'alt' | 'src' | 'width' | 'height'>; @@ -56,6 +58,14 @@ export const Summary: FC<SummaryProps> = ({ url, }) => { const intl = useIntl(); + const figureLabel = intl.formatMessage( + { + defaultMessage: '{title} cover', + description: 'Summary: figure (cover) accessible name', + id: 'RNVe1W', + }, + { title } + ); const readMore = intl.formatMessage( { defaultMessage: 'Read more<a11y> about {title}</a11y>', @@ -182,40 +192,44 @@ export const Summary: FC<SummaryProps> = ({ }; return ( - <article className={styles.wrapper}> - {meta.cover ? ( - <Figure> - <NextImage {...meta.cover} className={styles.cover} /> - </Figure> - ) : null} - <header className={styles.header}> - <Link href={url} className={styles.link}> - <Heading level={titleLevel} className={styles.title}> - {title} - </Heading> - </Link> - </header> - <div className={styles.body}> + <Card + className={styles.wrapper} + cover={ + meta.cover ? ( + <CardCover aria-label={figureLabel} hasBorders> + <NextImage {...meta.cover} /> + </CardCover> + ) : undefined + } + meta={<CardMeta items={getMetaItems()} />} + > + <CardHeader> + <CardTitle className={styles.title} level={titleLevel}> + <Link href={url}>{title}</Link> + </CardTitle> + </CardHeader> + <CardBody> <div className={styles.intro} // eslint-disable-next-line react/no-danger dangerouslySetInnerHTML={{ __html: intro }} /> - <ButtonLink className={styles['read-more']} to={url}> - {readMore} - <Icon - aria-hidden={true} - className={styles.icon} - // eslint-disable-next-line react/jsx-no-literals -- Direction allowed - orientation="right" - // eslint-disable-next-line react/jsx-no-literals -- Shape allowed - shape="arrow" - /> - </ButtonLink> - </div> - <footer className={styles.footer}> - <MetaList className={styles.meta} items={getMetaItems()} /> - </footer> - </article> + </CardBody> + <CardFooter> + <CardActions> + <ButtonLink to={url}> + {readMore} + <Icon + aria-hidden={true} + className={styles.icon} + // eslint-disable-next-line react/jsx-no-literals -- Direction allowed + orientation="right" + // eslint-disable-next-line react/jsx-no-literals -- Shape allowed + shape="arrow" + /> + </ButtonLink> + </CardActions> + </CardFooter> + </Card> ); }; |
