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/atoms/figure/figure.tsx | |
| 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/atoms/figure/figure.tsx')
| -rw-r--r-- | src/components/atoms/figure/figure.tsx | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/src/components/atoms/figure/figure.tsx b/src/components/atoms/figure/figure.tsx new file mode 100644 index 0000000..4dd5b10 --- /dev/null +++ b/src/components/atoms/figure/figure.tsx @@ -0,0 +1,72 @@ +import { + forwardRef, + type ReactNode, + type ForwardRefRenderFunction, + type HTMLAttributes, + useId, +} from 'react'; +import styles from './figure.module.scss'; + +export type FigureProps = Omit<HTMLAttributes<HTMLElement>, 'children'> & { + /** + * The contents (ie. an image, illustration, diagram, code snippet, etc.). + */ + children: ReactNode; + /** + * A figure caption. + */ + caption?: ReactNode; + /** + * Should we wrap the contents with borders? + */ + hasBorders?: boolean; +}; + +const FigureWithRef: ForwardRefRenderFunction<HTMLElement, FigureProps> = ( + { + 'aria-labelledby': ariaLabelledBy, + caption, + children, + className = '', + hasBorders, + ...props + }, + ref +) => { + const captionId = useId(); + const bordersModifier = hasBorders ? styles['wrapper--has-borders'] : ''; + const figureClass = `${styles.wrapper} ${bordersModifier} ${className}`; + + /** + * We need to ensure that the figcaption is used as an accessible name for the + * figure. In Testing Library, it is not automatically associated, it could + * also be the case in some browsers. However if the consumer provide its own + * `aria-labelled-by` attribute, it should be used instead of the caption (we + * could combine them but we cannot know which order is the more logical). + */ + const figureLabelledBy = + caption && !ariaLabelledBy ? captionId : ariaLabelledBy; + + return ( + <figure + {...props} + aria-labelledby={figureLabelledBy} + className={figureClass} + ref={ref} + > + {children} + {caption ? ( + <figcaption className={styles.caption} id={captionId}> + {caption} + </figcaption> + ) : null} + </figure> + ); +}; + +/** + * Figure component + * + * Render a responsive image wrapped in a figure element. + */ +export const Figure = forwardRef(FigureWithRef); |
