feat(BOOK-757): Moved TeaserCard to design system and added stories

Approved-by: Bianca Widstam
This commit is contained in:
Erik Tiekstra
2026-01-21 07:50:43 +00:00
parent 8a143a2916
commit b91504e7c6
18 changed files with 465 additions and 107 deletions

View File

@@ -0,0 +1,343 @@
import type { Meta, StoryObj } from "@storybook/nextjs-vite"
import { TeaserCard } from "./TeaserCard.tsx"
import { config } from "./variants.ts"
const PRIMARY_BUTTON = {
title: "Primary action",
href: "#",
openInNewTab: false,
}
const SECONDARY_BUTTON = {
...PRIMARY_BUTTON,
title: "Secondary action",
}
const SIDEPEEK_CONTENT = {
heading: "Sidepeek heading",
content: {
json: {
type: "doc",
attrs: {},
uid: "8126b570ffef4090a78f8c863d73c3b8",
children: [
{
type: "p",
attrs: {},
uid: "ed82964e32764cf589e07a251014543b",
children: [
{
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vitae neque non ipsum efficitur hendrerit at ut nulla. Cras in tellus et ligula posuere ullamcorper. Praesent pulvinar rutrum metus ut gravida.",
},
],
},
{
type: "h3",
attrs: {},
uid: "799903bc123d479d9bcf29cb4ba24b65",
children: [
{
text: "Lorem ipsum",
},
],
},
{
type: "p",
attrs: {},
uid: "00886e5b0a5d4268930a1472b58e9170",
children: [
{
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vitae neque non ipsum efficitur hendrerit at ut nulla. Cras in tellus et ligula posuere ullamcorper. Praesent pulvinar rutrum metus ut gravida.",
},
],
},
{
type: "p",
attrs: {},
uid: "6bd20356e8bd4612a99e1af6061f861c",
children: [
{
text: "",
},
{
uid: "28e963603b714055b948a038539bfd98",
type: "a",
attrs: {
url: "https://www.scandichotels.com/en",
target: "_blank",
},
children: [
{
text: "Learn more about this",
},
],
},
{
text: "",
},
],
},
{
type: "h3",
attrs: {},
uid: "9db195292c5b49ac970edc8b4be19081",
children: [
{
text: "Dolor sit amet",
},
],
},
{
type: "p",
attrs: {},
uid: "f45861b9146040ff8a6076cc2bc4d218",
children: [
{
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vitae neque non ipsum efficitur hendrerit at ut nulla. Cras in tellus et ligula posuere ullamcorper. Praesent pulvinar rutrum metus ut gravida.",
},
],
},
{
uid: "a40eb77a4c26401689255e0caa2f564a",
type: "ul",
children: [
{
type: "li",
attrs: {},
uid: "701d76e63827483cb80180056d1b396e",
children: [
{
text: "Lorem ipsum",
id: "",
},
],
},
{
type: "li",
attrs: {},
uid: "f3f28b61c4b54162a7c723995a38a063",
children: [
{
text: "Dolor sit amet consectetur adipiscing elit.",
id: "",
},
],
},
{
type: "li",
attrs: {},
uid: "27f51d6ad42e4dafb5037b5ee790f0b2",
children: [
{
text: "Curabitur vitae neque non ipsum efficitur",
id: "",
},
],
},
],
attrs: {},
},
],
_version: 16,
},
embedded_itemsConnection: {
edges: [],
},
},
primary_button: {
title: "Sidepeek primary action",
href: "#",
openInNewTab: false,
},
}
const DEFAULT_ARGS = {
heading: "Lorem ipsum",
bodyText:
"Dolor sit amet, consectetur adipiscing elit. Curabitur vitae neque non ipsum efficitur hendrerit at ut nulla.",
image: {
id: 1,
url: "./img/img2.jpg",
title: "Placeholder image",
meta: {
alt: "Placeholder image",
caption: "This is a placeholder image",
},
focalPoint: { x: 50, y: 50 },
dimensions: { width: 1920, height: 1189, aspectRatio: 1.61 },
},
primaryButton: PRIMARY_BUTTON,
secondaryButton: SECONDARY_BUTTON,
alwaysStack: false,
}
const meta: Meta<typeof TeaserCard> = {
title: "Core Components/Cards/TeaserCard",
parameters: {
docs: {
description: {
component:
"The card itself does not have a maximum width, but it will adapt to the width of its container. The card is mostly used together with other content cards. It is recommended to use the ContentCard inside a grid or a container with a set maximum width for best results.",
},
},
},
component: TeaserCard,
argTypes: {
heading: {
control: "text",
table: {
type: { summary: "string" },
},
},
bodyText: {
control: "text",
table: {
type: { summary: "string" },
},
},
primaryButton: {
control: "object",
table: {
type: {
summary:
"{ title: string, href: string, openInNewTab?: boolean, onPress?: () => void } | undefined",
},
},
},
secondaryButton: {
control: "object",
table: {
type: {
summary:
"{ title: string, href: string, openInNewTab?: boolean, onPress?: () => void } | undefined",
},
},
},
sidePeekButton: {
control: "object",
table: {
type: {
summary: "{ call_to_action_text: string } | undefined",
},
},
},
sidePeekContent: {
control: "object",
table: {
type: {
summary: "any | undefined",
},
},
},
image: {
control: "object",
table: {
type: {
summary: "ImageVaultAsset | undefined",
detail:
"{ id: number, url: string, meta: {alt?: string | null, caption?: string | null}, focalPoint: { x: number, y: number }, dimensions: { width: number, height: number, aspectRatio: number } }",
},
},
},
style: {
control: "select",
options: Object.keys(config.variants.style),
table: {
type: { summary: Object.keys(config.variants.style).join(" | ") },
},
defaultValue: config.defaultVariants.style,
},
alwaysStack: {
control: "boolean",
table: {
type: { summary: "boolean" },
},
defaultValue: config.defaultVariants.alwaysStack.toString(),
description:
"If true, the buttons will always be stacked vertically, regardless if these would fit next to each other.",
},
},
args: { ...DEFAULT_ARGS },
globals: {
backgrounds: { default: "storybookLight" },
},
decorators: [
(Story, context) => {
const showMultipleStyles = [
"with sidepeek",
"without image",
"always stack buttons",
].some((substring) => context.name.toLowerCase().includes(substring))
if (showMultipleStyles) {
return (
<div
style={{
display: "grid",
gap: "1em",
gridTemplateColumns: `repeat(${Object.keys(config.variants.style).length}, 400px)`,
}}
>
{Object.keys(config.variants.style).map((style, ix) => {
return (
<TeaserCard
key={ix}
{...context.args}
style={style as keyof typeof config.variants.style}
/>
)
})}
</div>
)
}
return (
<div style={{ maxWidth: "400px" }}>
<Story />
</div>
)
},
],
}
export default meta
type Story = StoryObj<typeof TeaserCard>
export const Default: Story = {
args: {
...meta.args,
},
}
export const Featured: Story = {
args: {
...meta.args,
style: "featured",
},
}
export const WithSidepeek: Story = {
args: {
...meta.args,
sidePeekButton: {
call_to_action_text: "Side peek action",
},
sidePeekContent: SIDEPEEK_CONTENT,
style: "featured",
},
}
export const WithoutImage: Story = {
args: {
...meta.args,
image: undefined,
},
}
export const AlwaysStackButtons: Story = {
args: {
...meta.args,
alwaysStack: true,
},
}