From 306ca92eb27719e0ffe49ddedcf43cd7adbdaaf2 Mon Sep 17 00:00:00 2001 From: Jonas Carlsen Date: Fri, 19 Dec 2025 11:58:47 +0100 Subject: [PATCH] refactor: support image variants in more places --- .../plugins/embed/KeyFigureEmbedPlugin.tsx | 18 +-- .../ndla-image-search/src/PreviewImage.tsx | 4 +- .../ndla-ui/src/AudioPlayer/AudioPlayer.tsx | 1 + .../src/CampaignBlock/CampaignBlock.tsx | 1 + .../src/Embed/ExternalEmbed.stories.tsx | 33 ++++- packages/ndla-ui/src/Embed/ExternalEmbed.tsx | 7 +- .../ndla-ui/src/Embed/IframeEmbed.stories.tsx | 33 ++++- packages/ndla-ui/src/Embed/IframeEmbed.tsx | 8 +- packages/ndla-ui/src/Embed/KeyFigureEmbed.tsx | 38 ++++++ packages/ndla-ui/src/KeyFigure/KeyFigure.tsx | 74 ++++++----- .../ndla-ui/src/ResourceBox/ResourceBox.tsx | 122 +++++++++--------- packages/ndla-ui/src/index.ts | 3 +- 12 files changed, 212 insertions(+), 130 deletions(-) create mode 100644 packages/ndla-ui/src/Embed/KeyFigureEmbed.tsx diff --git a/packages/article-converter/src/plugins/embed/KeyFigureEmbedPlugin.tsx b/packages/article-converter/src/plugins/embed/KeyFigureEmbedPlugin.tsx index bc54fb1c2d..d68f87cfb6 100644 --- a/packages/article-converter/src/plugins/embed/KeyFigureEmbedPlugin.tsx +++ b/packages/article-converter/src/plugins/embed/KeyFigureEmbedPlugin.tsx @@ -8,25 +8,11 @@ import { attributesToProps } from "html-react-parser"; import { type KeyFigureMetaData } from "@ndla/types-embed"; -import { KeyFigure } from "@ndla/ui"; +import { KeyFigureEmbed } from "@ndla/ui"; import { type PluginType } from "../types"; export const keyFigureEmbedPlugin: PluginType = (element) => { const props = attributesToProps(element.attribs); const data = JSON.parse(props["data-json"] as string) as KeyFigureMetaData; - const { title, subtitle, alt } = data.embedData; - return ( - - ); + return ; }; diff --git a/packages/ndla-image-search/src/PreviewImage.tsx b/packages/ndla-image-search/src/PreviewImage.tsx index 1f954146ba..c24a62e795 100644 --- a/packages/ndla-image-search/src/PreviewImage.tsx +++ b/packages/ndla-image-search/src/PreviewImage.tsx @@ -25,7 +25,6 @@ import { styled } from "@ndla/styled-system/jsx"; import type { ImageMetaInformationV3DTO } from "@ndla/types-backend/image-api"; import { ImageMeta } from "./ImageMeta"; import type { PreviewTranslations } from "./ImageSearch"; -import { getSrcSets } from "./util/imageUtil"; const ImageContainer = styled("div", { base: { @@ -129,10 +128,9 @@ export const PreviewImage = ({ id, image, onSelectImage, showCheckbox, translati diff --git a/packages/ndla-ui/src/AudioPlayer/AudioPlayer.tsx b/packages/ndla-ui/src/AudioPlayer/AudioPlayer.tsx index 9e7e73023c..43b8048387 100644 --- a/packages/ndla-ui/src/AudioPlayer/AudioPlayer.tsx +++ b/packages/ndla-ui/src/AudioPlayer/AudioPlayer.tsx @@ -186,6 +186,7 @@ export const AudioPlayer = ({ src, title, subtitle, speech, description, img, te return ( + {/* TODO: Image variants */} {!!img && ( {img.alt} diff --git a/packages/ndla-ui/src/CampaignBlock/CampaignBlock.tsx b/packages/ndla-ui/src/CampaignBlock/CampaignBlock.tsx index 4392ace5b7..9d9372686c 100644 --- a/packages/ndla-ui/src/CampaignBlock/CampaignBlock.tsx +++ b/packages/ndla-ui/src/CampaignBlock/CampaignBlock.tsx @@ -94,6 +94,7 @@ const Container = styled("div", { }, }); +// TODO: Variants const StyledImg = styled("img", { base: { objectFit: "cover", diff --git a/packages/ndla-ui/src/Embed/ExternalEmbed.stories.tsx b/packages/ndla-ui/src/Embed/ExternalEmbed.stories.tsx index 7f6af6071c..2ed1368daa 100644 --- a/packages/ndla-ui/src/Embed/ExternalEmbed.stories.tsx +++ b/packages/ndla-ui/src/Embed/ExternalEmbed.stories.tsx @@ -113,7 +113,6 @@ const opensInNewMetaData: OembedData = { }, iframeImage: { id: "65086", - inactive: false, metaUrl: "https://api.test.ndla.no/image-api/v3/images/65086", title: { title: "\nSamtale ", @@ -148,21 +147,47 @@ const opensInNewMetaData: OembedData = { language: "nb", }, supportedLanguages: ["nb"], - created: "2022-12-02T14:24:19Z", + created: "2022-12-02T14:24:19.000Z", createdBy: "oltQx44eGQp0DwkiR1NRo5qE", modelRelease: "yes", image: { fileName: "IgOjO6og.jpg", size: 176667, - variants: [], contentType: "image/jpeg", - imageUrl: "https://api.test.ndla.no/image-api/raw/IgOjO6og.jpg", + imageUrl: "https://images.test.ndla.no/IgOjO6og.jpg", dimensions: { width: 1920, height: 804, }, + variants: [ + { + size: "icon", + variantUrl: "https://images.test.ndla.no/IgOjO6og/icon.webp", + }, + { + size: "xsmall", + variantUrl: "https://images.test.ndla.no/IgOjO6og/xsmall.webp", + }, + { + size: "small", + variantUrl: "https://images.test.ndla.no/IgOjO6og/small.webp", + }, + { + size: "medium", + variantUrl: "https://images.test.ndla.no/IgOjO6og/medium.webp", + }, + { + size: "large", + variantUrl: "https://images.test.ndla.no/IgOjO6og/large.webp", + }, + { + size: "xlarge", + variantUrl: "https://images.test.ndla.no/IgOjO6og/xlarge.webp", + }, + ], language: "nb", }, + inactive: false, }, }; diff --git a/packages/ndla-ui/src/Embed/ExternalEmbed.tsx b/packages/ndla-ui/src/Embed/ExternalEmbed.tsx index 19d13b5128..ada16bc29d 100644 --- a/packages/ndla-ui/src/Embed/ExternalEmbed.tsx +++ b/packages/ndla-ui/src/Embed/ExternalEmbed.tsx @@ -48,14 +48,11 @@ export const ExternalEmbed = ({ embed }: Props) => { const { embedData, data } = embed; if (embedData.type === "fullscreen") { - const image = { - src: data.iframeImage?.image.imageUrl, - alt: embedData.alt !== undefined ? embedData.alt : (data.iframeImage?.alttext?.alttext ?? ""), - }; return (
{ return ; } - const { embedData, data } = embed; + const { embedData } = embed; if (embedData.type === "fullscreen") { - const iframeImage = embed.status === "success" ? data.iframeImage : undefined; - const alt = embedData.alt !== undefined ? embedData.alt : iframeImage?.alttext.alttext; - const image = { src: iframeImage?.image.imageUrl, alt: alt ?? "" }; return ( { + const image = embed.status === "success" && embed.data.metaImage ? embed.data.metaImage : undefined; + return ( + + {!!image && ( + + )} + +
{parse(embed.embedData.title)}
+
+ +
{parse(embed.embedData.subtitle)}
+
+
+ ); +}; diff --git a/packages/ndla-ui/src/KeyFigure/KeyFigure.tsx b/packages/ndla-ui/src/KeyFigure/KeyFigure.tsx index ea4afc0c45..7a5ebef61f 100644 --- a/packages/ndla-ui/src/KeyFigure/KeyFigure.tsx +++ b/packages/ndla-ui/src/KeyFigure/KeyFigure.tsx @@ -7,43 +7,49 @@ */ import parse from "html-react-parser"; -import { styled } from "@ndla/styled-system/jsx"; +import { ark } from "@ark-ui/react"; +import { Image } from "@ndla/primitives"; +import { sva } from "@ndla/styled-system/css"; +import { createStyleContext } from "@ndla/styled-system/jsx"; -const ContentWrapper = styled("div", { +const keyfigureRecipe = sva({ + slots: ["root", "image", "title", "subtitle"], base: { - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - gap: "xsmall", - - "&:not(:has(> img))": { - paddingBlock: "xxlarge", + root: { + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + gap: "xsmall", + "&:not(:has(> img))": { + paddingBlock: "xxlarge", + }, + }, + image: { + height: "surface.3xsmall", + width: "surface.3xsmall", + borderRadius: "xsmall", + }, + title: { + textStyle: "heading.large", + textAlign: "center", + }, + subtitle: { + textStyle: "title.medium", + textAlign: "center", }, }, }); -const StyledImage = styled("img", { - base: { - height: "surface.3xsmall", - width: "surface.3xsmall", - borderRadius: "xsmall", - }, -}); +const { withProvider, withContext } = createStyleContext(keyfigureRecipe); -const TitleWrapper = styled("div", { - base: { - textStyle: "heading.large", - textAlign: "center", - }, -}); +export const KeyFigureRoot = withProvider(ark.div, "root", { baseComponent: true }); -const SubTitleWrapper = styled("div", { - base: { - textStyle: "title.medium", - textAlign: "center", - }, -}); +export const KeyFigureImage = withContext(Image, "image"); + +export const KeyFigureTitle = withContext(ark.div, "title"); + +export const KeyFigureSubTitle = withContext(ark.div, "subtitle"); export interface Props { image?: { @@ -56,10 +62,10 @@ export interface Props { export const KeyFigure = ({ image, title, subtitle }: Props) => { return ( - - {!!image && } - {parse(title)} - {parse(subtitle)} - + + {!!image && } + {parse(title)} + {parse(subtitle)} + ); }; diff --git a/packages/ndla-ui/src/ResourceBox/ResourceBox.tsx b/packages/ndla-ui/src/ResourceBox/ResourceBox.tsx index 672a07e3e3..810986d1f3 100644 --- a/packages/ndla-ui/src/ResourceBox/ResourceBox.tsx +++ b/packages/ndla-ui/src/ResourceBox/ResourceBox.tsx @@ -6,99 +6,105 @@ * */ +import { ark } from "@ark-ui/react"; import { breakpoints } from "@ndla/core"; import { ShareBoxLine } from "@ndla/icons"; import { Heading, Image, Text } from "@ndla/primitives"; import { SafeLinkButton } from "@ndla/safelink"; -import { styled } from "@ndla/styled-system/jsx"; +import { sva } from "@ndla/styled-system/css"; +import { createStyleContext } from "@ndla/styled-system/jsx"; +import type { ImageMetaInformationV3DTO } from "@ndla/types-backend/image-api"; -const Container = styled("div", { +const resourceBoxRecipe = sva({ + slots: ["root", "content", "image", "text"], base: { - display: "flex", - padding: "medium", - borderRadius: "xsmall", - border: "1px solid", - borderColor: "stroke.default", - boxShadow: "full", - marginBlockEnd: "medium", - gap: "medium", - tabletWideDown: { - padding: "xsmall", + root: { + display: "flex", + padding: "medium", + borderRadius: "xsmall", + border: "1px solid", + borderColor: "stroke.default", + boxShadow: "full", + marginBlockEnd: "medium", + gap: "medium", + tabletWideDown: { + padding: "xsmall", + }, + tabletDown: { + flexDirection: "column", + gap: "0", + padding: "0", + }, }, - tabletDown: { + content: { + display: "flex", flexDirection: "column", - gap: "0", - padding: "0", + alignItems: "flex-start", + gap: "xsmall", + flex: "1", + tabletDown: { + padding: "xsmall", + }, }, - }, -}); - -const ContentWrapper = styled("div", { - base: { - display: "flex", - flexDirection: "column", - alignItems: "flex-start", - gap: "xsmall", - flex: "1", - tabletDown: { - padding: "xsmall", + image: { + objectFit: "cover", + borderRadius: "xsmall", + width: "fit-content", + aspectRatio: "1/1", + tabletDown: { + width: "100%", + borderRadius: "0", + }, }, - }, -}); - -const StyledImage = styled(Image, { - base: { - objectFit: "cover", - borderRadius: "xsmall", - width: "fit-content", - aspectRatio: "1/1", - tabletDown: { - width: "100%", - borderRadius: "0", + text: { + flex: "1", }, }, }); -const StyledText = styled(Text, { - base: { - flex: "1", - }, -}); +const { withProvider, withContext } = createStyleContext(resourceBoxRecipe); -interface ImageMeta { - src: string | undefined; - alt: string; -} +const ResourceBoxRoot = withProvider(ark.div, "root", { baseComponent: true }); + +const ResourceBoxContent = withContext(ark.div, "content", { baseComponent: true }); + +const ResourceBoxImage = withContext(Image, "image"); + +const ResourceBoxText = withContext(Text, "text"); interface Props { - image?: ImageMeta; + image?: ImageMetaInformationV3DTO; title: string; + imageAlt?: string; caption: string; url: string; buttonText: string; } -export const ResourceBox = ({ image, title, caption, url, buttonText }: Props) => { +export const ResourceBox = ({ image, title, caption, url, buttonText, imageAlt }: Props) => { return ( - + {image ? ( - ) : null} - +

{title}

- {caption} + {caption} {buttonText} -
-
+ + ); }; diff --git a/packages/ndla-ui/src/index.ts b/packages/ndla-ui/src/index.ts index 7a998a1f76..e52d36ede6 100644 --- a/packages/ndla-ui/src/index.ts +++ b/packages/ndla-ui/src/index.ts @@ -8,6 +8,7 @@ export { Concept } from "./Concept/Concept"; +export { KeyFigureEmbed } from "./Embed/KeyFigureEmbed"; export { ImageEmbed, getCrop, getFocalPoint } from "./Embed/ImageEmbed"; export { InlineTriggerButton } from "./Embed/InlineTriggerButton"; export { AudioEmbed } from "./Embed/AudioEmbed"; @@ -105,7 +106,7 @@ export { } from "./TagSelector/TagSelector"; export { Pitch } from "./Pitch/Pitch"; -export { KeyFigure } from "./KeyFigure/KeyFigure"; +export { KeyFigure, KeyFigureRoot, KeyFigureImage, KeyFigureSubTitle, KeyFigureTitle } from "./KeyFigure/KeyFigure"; export { ContactBlock, contactBlockBackgrounds } from "./ContactBlock/ContactBlock"; export type { ContactBlockBackground } from "./ContactBlock/ContactBlock"; export { CampaignBlock } from "./CampaignBlock/CampaignBlock";