aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/figure/figure.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/atoms/figure/figure.tsx')
-rw-r--r--src/components/atoms/figure/figure.tsx72
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);