diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-09 18:26:23 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 |
| commit | 15522ec9146f6f1956620355c44dea2a6a75b67c (patch) | |
| tree | 7be0c4ca96cb3e59d2ee989785a6b6a286e6169d /src/components/molecules | |
| parent | 891441a76173c708c6604fa203b175aefa222333 (diff) | |
refactor(components): replace ResponsiveImage with Figure component
The styles applied to ResponsiveImage are related to the figure and
figcaption elements. Those elements could be use with other contents
than images. So I extracted them in a Figure component. The
ResponsiveImage component is no longer useful: the consumer should use
the Image component from `next` and wrap it in a link if needed.
Diffstat (limited to 'src/components/molecules')
| -rw-r--r-- | src/components/molecules/images/index.ts | 1 | ||||
| -rw-r--r-- | src/components/molecules/images/responsive-image.module.scss | 84 | ||||
| -rw-r--r-- | src/components/molecules/images/responsive-image.stories.tsx | 212 | ||||
| -rw-r--r-- | src/components/molecules/images/responsive-image.test.tsx | 19 | ||||
| -rw-r--r-- | src/components/molecules/images/responsive-image.tsx | 91 | ||||
| -rw-r--r-- | src/components/molecules/layout/card.fixture.ts (renamed from src/components/molecules/layout/card.fixture.tsx) | 2 | ||||
| -rw-r--r-- | src/components/molecules/layout/card.module.scss | 1 | ||||
| -rw-r--r-- | src/components/molecules/layout/card.tsx | 11 |
8 files changed, 8 insertions, 413 deletions
diff --git a/src/components/molecules/images/index.ts b/src/components/molecules/images/index.ts index 33ec886..318a6af 100644 --- a/src/components/molecules/images/index.ts +++ b/src/components/molecules/images/index.ts @@ -1,2 +1 @@ export * from './flipping-logo'; -export * from './responsive-image'; diff --git a/src/components/molecules/images/responsive-image.module.scss b/src/components/molecules/images/responsive-image.module.scss deleted file mode 100644 index e4ed4aa..0000000 --- a/src/components/molecules/images/responsive-image.module.scss +++ /dev/null @@ -1,84 +0,0 @@ -@use "../../../styles/abstracts/functions" as fun; - -.caption { - margin: 0; - padding: fun.convert-px(4) var(--spacing-2xs); - background: var(--color-bg-secondary); - border: fun.convert-px(1) solid var(--color-border-light); - font-size: var(--font-size-sm); - font-weight: 500; -} - -.wrapper { - display: flex; - flex-flow: column; - width: fit-content; - margin: 0 auto; - position: relative; - text-align: center; - - &--has-borders { - .caption { - margin-top: fun.convert-px(4); - } - } - - &--has-borders#{&}--has-link { - .link { - padding: fun.convert-px(4); - } - } - - &--has-borders#{&}--no-link { - padding: fun.convert-px(4); - border: fun.convert-px(1) solid var(--color-border); - box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0 - var(--color-shadow); - } -} - -.img { - max-height: 100%; - object-fit: cover; -} - -.link { - display: flex; - flex-flow: column; - background: none; - border: fun.convert-px(1) solid var(--color-border); - box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0 - var(--color-shadow); - text-decoration: none; - - .caption { - color: var(--color-primary-darker); - } - - &:hover, - &:focus { - box-shadow: 0 0 fun.convert-px(2) 0 var(--color-shadow-light), - fun.convert-px(2) fun.convert-px(2) fun.convert-px(4) fun.convert-px(1) - var(--color-shadow-light), - fun.convert-px(4) fun.convert-px(4) fun.convert-px(8) fun.convert-px(2) - var(--color-shadow-light); - transform: scale(var(--scale-up, 1.05)); - } - - &:focus { - .caption { - text-decoration: underline solid var(--color-primary-darker) - fun.convert-px(3); - } - } - - &:active { - box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) - fun.convert-px(1) var(--color-shadow-light); - transform: scale(var(--scale-down, 0.95)); - - .caption { - text-decoration: none; - } - } -} diff --git a/src/components/molecules/images/responsive-image.stories.tsx b/src/components/molecules/images/responsive-image.stories.tsx deleted file mode 100644 index cc6b088..0000000 --- a/src/components/molecules/images/responsive-image.stories.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { ResponsiveImage } from './responsive-image'; - -/** - * ResponsiveImage - Storybook Meta - */ -export default { - title: 'Molecules/Images/ResponsiveImage', - component: ResponsiveImage, - args: { - withBorders: false, - }, - argTypes: { - alt: { - control: { - type: 'text', - }, - description: 'An alternative text.', - type: { - name: 'string', - required: true, - }, - }, - caption: { - control: { - type: 'text', - }, - description: 'A figure caption.', - table: { - category: 'Options', - }, - type: { - name: 'string', - required: false, - }, - }, - className: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the image wrapper.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - height: { - control: { - type: 'number', - }, - description: 'The image height.', - type: { - name: 'string', - required: true, - }, - }, - src: { - control: { - type: 'text', - }, - description: 'The image source.', - type: { - name: 'string', - required: true, - }, - }, - target: { - control: { - type: 'text', - }, - description: 'A link target.', - table: { - category: 'Options', - }, - type: { - name: 'string', - required: false, - }, - }, - width: { - control: { - type: 'number', - }, - description: 'The image width.', - type: { - name: 'string', - required: true, - }, - }, - withBorders: { - control: { - type: 'boolean', - }, - description: 'Add borders around the image.', - table: { - category: 'Styles', - defaultValue: { summary: false }, - }, - type: { - name: 'boolean', - required: false, - }, - }, - }, -} as ComponentMeta<typeof ResponsiveImage>; - -const Template: ComponentStory<typeof ResponsiveImage> = (args) => ( - <ResponsiveImage {...args} /> -); - -/** - * Responsive Image Stories - Default - */ -export const Default = Template.bind({}); -Default.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, -}; - -/** - * Responsive Image Stories - With borders - */ -export const WithBorders = Template.bind({}); -WithBorders.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - withBorders: true, -}; - -/** - * Responsive Image Stories - With link - */ -export const WithLink = Template.bind({}); -WithLink.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - target: '#', -}; - -/** - * Responsive Image Stories - With link and borders - */ -export const WithLinkAndBorders = Template.bind({}); -WithLinkAndBorders.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - target: '#', - withBorders: true, -}; - -/** - * Responsive Image Stories - With caption - */ -export const WithCaption = Template.bind({}); -WithCaption.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - caption: 'Omnis nulla labore', -}; - -/** - * Responsive Image Stories - With caption and borders - */ -export const WithCaptionAndBorders = Template.bind({}); -WithCaptionAndBorders.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - caption: 'Omnis nulla labore', - withBorders: true, -}; - -/** - * Responsive Image Stories - With caption and link - */ -export const WithCaptionAndLink = Template.bind({}); -WithCaptionAndLink.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - caption: 'Omnis nulla labore', - target: '#', -}; - -/** - * Responsive Image Stories - With caption, link and borders - */ -export const WithCaptionLinkAndBorders = Template.bind({}); -WithCaptionLinkAndBorders.args = { - alt: 'An example', - src: 'http://placeimg.com/640/480/transport', - width: 640, - height: 480, - caption: 'Omnis nulla labore', - target: '#', - withBorders: true, -}; diff --git a/src/components/molecules/images/responsive-image.test.tsx b/src/components/molecules/images/responsive-image.test.tsx deleted file mode 100644 index dec36ea..0000000 --- a/src/components/molecules/images/responsive-image.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { ResponsiveImage } from './responsive-image'; - -describe('ResponsiveImage', () => { - it('renders a responsive image', () => { - render( - <ResponsiveImage - src="http://placeimg.com/640/480" - alt="An alternative text" - width={640} - height={480} - /> - ); - expect( - screen.getByRole('img', { name: 'An alternative text' }) - ).toBeInTheDocument(); - }); -}); diff --git a/src/components/molecules/images/responsive-image.tsx b/src/components/molecules/images/responsive-image.tsx deleted file mode 100644 index 85c0c46..0000000 --- a/src/components/molecules/images/responsive-image.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import Image, { type ImageProps } from 'next/image'; -import { FC, ReactNode } from 'react'; -import { Link, type LinkProps } from '../../atoms'; -import styles from './responsive-image.module.scss'; - -export type ResponsiveImageProps = Omit< - ImageProps, - 'alt' | 'width' | 'height' -> & { - /** - * An alternative text. - */ - alt: string; - /** - * A figure caption. - */ - caption?: ReactNode; - /** - * Set additional classnames to the figure wrapper. - */ - className?: string; - /** - * The image height. - */ - height: number | `${number}`; - /** - * A link target. - */ - target?: LinkProps['href']; - /** - * The image width. - */ - width: number | `${number}`; - /** - * Wrap the image with borders. - */ - withBorders?: boolean; -}; - -/** - * ResponsiveImage component - * - * Render a responsive image wrapped in a figure element. - */ -export const ResponsiveImage: FC<ResponsiveImageProps> = ({ - alt, - caption, - className = '', - target, - title, - withBorders, - ...props -}) => { - const bordersModifier = withBorders ? styles['wrapper--has-borders'] : ''; - const linkModifier = target - ? styles['wrapper--has-link'] - : styles['wrapper--no-link']; - const figureClass = `${styles.wrapper} ${bordersModifier} ${linkModifier} ${className}`; - - return ( - <figure aria-label={caption ? undefined : title} className={figureClass}> - {target ? ( - <Link href={target} className={styles.link}> - <Image - {...props} - alt={alt} - className={styles.img} - sizes="100vw" - title={title} - /> - {caption && ( - <figcaption className={styles.caption}>{caption}</figcaption> - )} - </Link> - ) : ( - <> - <Image - {...props} - alt={alt} - className={styles.img} - sizes="100vw" - title={title} - /> - {caption && ( - <figcaption className={styles.caption}>{caption}</figcaption> - )} - </> - )} - </figure> - ); -}; diff --git a/src/components/molecules/layout/card.fixture.tsx b/src/components/molecules/layout/card.fixture.ts index f96cc43..01fe2e9 100644 --- a/src/components/molecules/layout/card.fixture.tsx +++ b/src/components/molecules/layout/card.fixture.ts @@ -1,7 +1,7 @@ export const cover = { alt: 'A picture', height: 480, - src: 'http://placeimg.com/640/480', + src: 'https://picsum.photos/640/480', width: 640, }; diff --git a/src/components/molecules/layout/card.module.scss b/src/components/molecules/layout/card.module.scss index 31f6a4b..7a06508 100644 --- a/src/components/molecules/layout/card.module.scss +++ b/src/components/molecules/layout/card.module.scss @@ -20,6 +20,7 @@ .cover { place-content: center; height: fun.convert-px(150); + object-fit: scale-down; margin: auto; border-bottom: fun.convert-px(1) solid var(--color-border); } diff --git a/src/components/molecules/layout/card.tsx b/src/components/molecules/layout/card.tsx index c9e7a90..c316100 100644 --- a/src/components/molecules/layout/card.tsx +++ b/src/components/molecules/layout/card.tsx @@ -1,7 +1,6 @@ +import NextImage, { type ImageProps as NextImageProps } from 'next/image'; import type { FC } from 'react'; -import type { Image as Img } from '../../../types'; -import { ButtonLink, Heading, type HeadingLevel } from '../../atoms'; -import { ResponsiveImage } from '../images'; +import { ButtonLink, Figure, Heading, type HeadingLevel } from '../../atoms'; import styles from './card.module.scss'; import { Meta, type MetaData } from './meta'; @@ -13,7 +12,7 @@ export type CardProps = { /** * The card cover. */ - cover?: Img; + cover?: Pick<NextImageProps, 'alt' | 'src' | 'title' | 'width' | 'height'>; /** * The card id. */ @@ -63,7 +62,9 @@ export const Card: FC<CardProps> = ({ <article className={styles.article}> <header className={styles.header}> {cover ? ( - <ResponsiveImage {...cover} className={styles.cover} /> + <Figure> + <NextImage {...cover} className={styles.cover} /> + </Figure> ) : null} <Heading className={styles.title} id={headingId} level={titleLevel}> {title} |
