From 7f47972494d1a9eaa9591d7ff2c588d394019aed Mon Sep 17 00:00:00 2001 From: Lotfi Habbiche Date: Tue, 25 Aug 2026 16:07:16 +0200 Subject: [PATCH 1/3] feat: add component (img, svg, iframe, video, audio) --- src/ContentMedia.tsx | 192 ++++++++++++++++++++++++++++ stories/ContentMedia.stories.tsx | 193 +++++++++++++++++++++++++++++ test/integration/vite/src/Home.tsx | 77 +++++++++++- 3 files changed, 460 insertions(+), 2 deletions(-) create mode 100644 src/ContentMedia.tsx create mode 100644 stories/ContentMedia.stories.tsx diff --git a/src/ContentMedia.tsx b/src/ContentMedia.tsx new file mode 100644 index 000000000..d4d057299 --- /dev/null +++ b/src/ContentMedia.tsx @@ -0,0 +1,192 @@ +"use client"; + +import React, { memo, forwardRef, type CSSProperties, type ReactNode } from "react"; +import { symToStr } from "tsafe/symToStr"; +import { fr } from "./fr"; +import { cx } from "./tools/cx"; +import { getLink, type RegisteredLinkProps } from "./link"; +import { useAnalyticsId } from "./tools/useAnalyticsId"; + +export type ContentMediaProps = { + id?: string; + className?: string; + style?: CSSProperties; + classes?: Partial>; + /** + * aria-label for the `
` element. + * Defaults to the `caption` prop when it is a plain string. + * Required for image and SVG media types when caption is not a string. + */ + label?: string; + /** Caption text shown below the media (description / source). */ + caption?: ReactNode; + /** Text of the optional link inside the figcaption. */ + captionLinkLabel?: ReactNode; + /** Props of the optional link inside the figcaption. */ + captionLinkProps?: RegisteredLinkProps; +} & ContentMediaProps.Media; + +export namespace ContentMediaProps { + /** Image (``) media. */ + export type ImageMedia = { + type: "img"; + /** + * Props forwarded to the `` element. + * `src` and `alt` are required — `alt` can be an empty string when + * the image is purely decorative. + */ + imgProps: React.ImgHTMLAttributes & { + src: string; + /** Empty string is allowed for decorative images. */ + alt: string; + }; + }; + + /** SVG media — pass your `` element as the `svg` prop. */ + export type SvgMedia = { + type: "svg"; + /** + * The `` element to render. + * • Decorative SVGs should carry `aria-hidden="true"`. + * • Meaningful SVGs should carry `role="img"` and an `aria-label`. + */ + svg: ReactNode; + }; + + /** Embedded video via `

` within the `

` within the `

+ +
+ ); + case "svg": + return <>{props.svg}; + case "iframe": + return ( +