chore(BOOK-754): Moved ContentCard to design system and added stories
Approved-by: Bianca Widstam Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { ContentCard } from "@scandic-hotels/design-system/ContentCard"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { Carousel } from "@/components/Carousel"
|
import { Carousel } from "@/components/Carousel"
|
||||||
@@ -8,7 +9,6 @@ import {
|
|||||||
CarouselNext,
|
CarouselNext,
|
||||||
CarouselPrevious,
|
CarouselPrevious,
|
||||||
} from "@/components/Carousel/CarouselNavigation"
|
} from "@/components/Carousel/CarouselNavigation"
|
||||||
import { ContentCard } from "@/components/ContentCard"
|
|
||||||
|
|
||||||
import styles from "./allCampaigns.module.css"
|
import styles from "./allCampaigns.module.css"
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
|
import { ContentCard } from "@scandic-hotels/design-system/ContentCard"
|
||||||
|
|
||||||
import { Carousel } from "@/components/Carousel"
|
import { Carousel } from "@/components/Carousel"
|
||||||
import { ContentCard } from "@/components/ContentCard"
|
|
||||||
import { Section } from "@/components/Section"
|
import { Section } from "@/components/Section"
|
||||||
import { SectionHeader } from "@/components/Section/Header"
|
import { SectionHeader } from "@/components/Section/Header"
|
||||||
import SectionLink from "@/components/Section/Link"
|
import SectionLink from "@/components/Section/Link"
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
|
import { ContentCard } from "@scandic-hotels/design-system/ContentCard"
|
||||||
|
|
||||||
import { Carousel } from "@/components/Carousel"
|
import { Carousel } from "@/components/Carousel"
|
||||||
import { ContentCard } from "@/components/ContentCard"
|
|
||||||
import { Section } from "@/components/Section"
|
import { Section } from "@/components/Section"
|
||||||
import { SectionHeader } from "@/components/Section/Header"
|
import { SectionHeader } from "@/components/Section/Header"
|
||||||
import SectionLink from "@/components/Section/Link"
|
import SectionLink from "@/components/Section/Link"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { cx } from "class-variance-authority"
|
import { cx } from "class-variance-authority"
|
||||||
|
|
||||||
import { ContentCard } from "@/components/ContentCard"
|
import { ContentCard } from "@scandic-hotels/design-system/ContentCard"
|
||||||
|
|
||||||
import styles from "./campaignCardList.module.css"
|
import styles from "./campaignCardList.module.css"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getTheme } from "@scandic-hotels/common/utils/theme/serverContext"
|
import { getTheme } from "@scandic-hotels/common/utils/theme/serverContext"
|
||||||
|
import { ContentCard } from "@scandic-hotels/design-system/ContentCard"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { Carousel } from "@/components/Carousel"
|
import { Carousel } from "@/components/Carousel"
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
CarouselNext,
|
CarouselNext,
|
||||||
CarouselPrevious,
|
CarouselPrevious,
|
||||||
} from "@/components/Carousel/CarouselNavigation"
|
} from "@/components/Carousel/CarouselNavigation"
|
||||||
import { ContentCard } from "@/components/ContentCard"
|
|
||||||
import CampaignHero from "@/components/ContentType/CampaignPage/Hero"
|
import CampaignHero from "@/components/ContentType/CampaignPage/Hero"
|
||||||
import CampaignCardList from "@/components/ContentType/HotelPage/Campaigns/CampaignCardList"
|
import CampaignCardList from "@/components/ContentType/HotelPage/Campaigns/CampaignCardList"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
import type { Meta, StoryObj } from "@storybook/nextjs-vite"
|
|
||||||
import { fn } from "storybook/test"
|
|
||||||
|
|
||||||
import { themes } from "../../../../.storybook/preview"
|
|
||||||
|
|
||||||
import { Card } from "../"
|
|
||||||
import { Button } from "../../Button"
|
|
||||||
import { Typography } from "../../Typography"
|
|
||||||
|
|
||||||
type CompositionProps = React.ComponentPropsWithoutRef<typeof Card> & {
|
|
||||||
_onPrimaryPress?: () => void
|
|
||||||
_onSecondaryPress?: () => void
|
|
||||||
inMainArea: boolean
|
|
||||||
showTitle: boolean
|
|
||||||
showPreamble: boolean
|
|
||||||
showPrimaryButton: boolean
|
|
||||||
showSecondaryButton: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const meta: Meta<CompositionProps> = {
|
|
||||||
title: "Compositions/Card",
|
|
||||||
component: Card,
|
|
||||||
decorators: [
|
|
||||||
(Story, context) => {
|
|
||||||
if (context.name.toLowerCase().indexOf("all themes") >= 0) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1>{context.name}</h1>
|
|
||||||
{Object.entries(themes.themes).map(([key, value], ix) => {
|
|
||||||
return (
|
|
||||||
<div key={ix} className={value} style={{ padding: "1em 0" }}>
|
|
||||||
<h2 style={{ paddingBottom: "0.5em" }}>{key}</h2>
|
|
||||||
<Story />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<Story />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
],
|
|
||||||
argTypes: {
|
|
||||||
inMainArea: {
|
|
||||||
name: "Is in main area",
|
|
||||||
},
|
|
||||||
showTitle: {
|
|
||||||
name: "Composition: Show title",
|
|
||||||
},
|
|
||||||
showPreamble: {
|
|
||||||
name: "Composition: Show preamble",
|
|
||||||
},
|
|
||||||
showPrimaryButton: {
|
|
||||||
name: "Composition: Show primary button",
|
|
||||||
},
|
|
||||||
showSecondaryButton: {
|
|
||||||
name: "Composition: Show secondary button",
|
|
||||||
},
|
|
||||||
_onPrimaryPress: {
|
|
||||||
table: {
|
|
||||||
disable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
_onSecondaryPress: {
|
|
||||||
table: {
|
|
||||||
disable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
render: ({
|
|
||||||
inMainArea,
|
|
||||||
showTitle,
|
|
||||||
showPreamble,
|
|
||||||
showPrimaryButton,
|
|
||||||
showSecondaryButton,
|
|
||||||
...args
|
|
||||||
}) => (
|
|
||||||
<Card {...args}>
|
|
||||||
{showTitle && (
|
|
||||||
<Typography variant="Title/md">
|
|
||||||
<h3>Content Card</h3>
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showPreamble && (
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
|
||||||
<p>
|
|
||||||
Mattis sit duis pulvinar ultricies auctor euismod. Augue mattis
|
|
||||||
mauris at est iaculis pulvinar pulvinar.
|
|
||||||
</p>
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showPrimaryButton && inMainArea && (
|
|
||||||
<Button size="lg" variant="Primary" onPress={args._onPrimaryPress}>
|
|
||||||
Primary action
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showPrimaryButton && !inMainArea && (
|
|
||||||
<Button size="sm" variant="Tertiary" onPress={args._onPrimaryPress}>
|
|
||||||
Primary action
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showSecondaryButton && (
|
|
||||||
<Button
|
|
||||||
size={inMainArea ? "lg" : "sm"}
|
|
||||||
variant="Secondary"
|
|
||||||
onPress={args._onSecondaryPress}
|
|
||||||
>
|
|
||||||
Secondary action
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
export default meta
|
|
||||||
|
|
||||||
type Story = StoryObj<CompositionProps>
|
|
||||||
|
|
||||||
export const ContentCardMainArea: Story = {
|
|
||||||
args: {
|
|
||||||
as: "Default",
|
|
||||||
inMainArea: true,
|
|
||||||
showTitle: true,
|
|
||||||
showPreamble: true,
|
|
||||||
showPrimaryButton: true,
|
|
||||||
showSecondaryButton: true,
|
|
||||||
_onPrimaryPress: fn(),
|
|
||||||
_onSecondaryPress: fn(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ContentCardNonMainArea: Story = {
|
|
||||||
args: {
|
|
||||||
as: "Default",
|
|
||||||
inMainArea: false,
|
|
||||||
showTitle: true,
|
|
||||||
showPreamble: true,
|
|
||||||
showPrimaryButton: true,
|
|
||||||
showSecondaryButton: true,
|
|
||||||
_onPrimaryPress: fn(),
|
|
||||||
_onSecondaryPress: fn(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ContentCardMainAreaAllThemes: Story = {
|
|
||||||
...ContentCardMainArea,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ContentCardNonMainAreaAllThemes: Story = {
|
|
||||||
...ContentCardNonMainArea,
|
|
||||||
}
|
|
||||||
@@ -2,18 +2,47 @@ import type { Meta, StoryObj } from "@storybook/nextjs-vite"
|
|||||||
|
|
||||||
import { fn } from "storybook/test"
|
import { fn } from "storybook/test"
|
||||||
|
|
||||||
|
import { MaterialIcon } from "../Icons/MaterialIcon/index.tsx"
|
||||||
import { ChipButton } from "./ChipButton.tsx"
|
import { ChipButton } from "./ChipButton.tsx"
|
||||||
import { config as chipButtonConfig } from "./variants"
|
import { config as chipButtonConfig } from "./variants"
|
||||||
import { MaterialIcon } from "../Icons/MaterialIcon/index.tsx"
|
|
||||||
|
|
||||||
const meta: Meta<typeof ChipButton> = {
|
const meta: Meta<typeof ChipButton> = {
|
||||||
title: "Core Components/ChipButton",
|
title: "Core Components/ChipButton",
|
||||||
component: ChipButton,
|
component: ChipButton,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
|
children: {
|
||||||
|
table: {
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
variant: {
|
variant: {
|
||||||
control: "select",
|
control: "select",
|
||||||
type: "string",
|
|
||||||
options: Object.keys(chipButtonConfig.variants.variant),
|
options: Object.keys(chipButtonConfig.variants.variant),
|
||||||
|
table: {
|
||||||
|
type: {
|
||||||
|
summary: Object.keys(chipButtonConfig.variants.variant).join(" | "),
|
||||||
|
},
|
||||||
|
defaultValue: { summary: chipButtonConfig.defaultVariants.variant },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: "select",
|
||||||
|
options: Object.keys(chipButtonConfig.variants.size),
|
||||||
|
table: {
|
||||||
|
type: {
|
||||||
|
summary: Object.keys(chipButtonConfig.variants.size).join(" | "),
|
||||||
|
},
|
||||||
|
defaultValue: { summary: "Large" },
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Sets the size of the ChipButton component. This only affects the `FilterRounded` variant.",
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
control: "boolean",
|
||||||
|
table: {
|
||||||
|
type: { summary: "boolean" },
|
||||||
|
defaultValue: { summary: "false" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
onPress: {
|
onPress: {
|
||||||
table: {
|
table: {
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import type { Meta, StoryObj } from "@storybook/nextjs-vite"
|
||||||
|
|
||||||
|
import { ContentCard } from "./ContentCard.tsx"
|
||||||
|
|
||||||
|
const 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 },
|
||||||
|
}
|
||||||
|
const IMAGE_2 = {
|
||||||
|
...IMAGE,
|
||||||
|
id: 2,
|
||||||
|
url: "./img/img3.jpg",
|
||||||
|
dimensions: { width: 1920, height: 2880, aspectRatio: 0.67 },
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_ARGS = {
|
||||||
|
heading: "Lorem ipsum",
|
||||||
|
bodyText:
|
||||||
|
"Dolor sit amet, consectetur adipiscing elit. Curabitur vitae neque non ipsum efficitur hendrerit at ut nulla.",
|
||||||
|
link: {
|
||||||
|
href: "#",
|
||||||
|
text: "Learn more",
|
||||||
|
},
|
||||||
|
image: IMAGE,
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta: Meta<typeof ContentCard> = {
|
||||||
|
title: "Core Components/Cards/ContentCard",
|
||||||
|
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: ContentCard,
|
||||||
|
argTypes: {
|
||||||
|
heading: {
|
||||||
|
control: "text",
|
||||||
|
table: {
|
||||||
|
type: { summary: "string" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bodyText: {
|
||||||
|
control: "text",
|
||||||
|
table: {
|
||||||
|
type: { summary: "string" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
promoText: {
|
||||||
|
control: "text",
|
||||||
|
table: {
|
||||||
|
type: { summary: "string | undefined" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
control: "object",
|
||||||
|
description:
|
||||||
|
"The link of where the card should navigate to. The whole card is clickable.",
|
||||||
|
table: {
|
||||||
|
type: {
|
||||||
|
summary:
|
||||||
|
"{href: string, openInNewTab?: boolean, isExternal?: boolean}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
control: "object",
|
||||||
|
table: {
|
||||||
|
type: {
|
||||||
|
summary: "ImageVaultAsset",
|
||||||
|
detail:
|
||||||
|
"{ id: number, url: string, meta: {alt?: string | null, caption?: string | null}, focalPoint: { x: number, y: number }, dimensions: { width: number, height: number, aspectRatio: number } }",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: { ...DEFAULT_ARGS },
|
||||||
|
globals: {
|
||||||
|
backgrounds: { default: "storybookLight" },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof ContentCard>
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
...meta.args,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithPromoText: Story = {
|
||||||
|
args: {
|
||||||
|
...meta.args,
|
||||||
|
promoText: "Popular choice",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MultipleCards: Story = {
|
||||||
|
args: {
|
||||||
|
...meta.args,
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(2, 1fr)",
|
||||||
|
gap: "16px",
|
||||||
|
width: "min(800px, 100%)",
|
||||||
|
margin: "0 auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ContentCard {...args} promoText="Popular choice" />
|
||||||
|
<ContentCard {...args} image={IMAGE_2} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}
|
||||||
@@ -4,13 +4,12 @@ import { cx } from "class-variance-authority"
|
|||||||
import NextLink from "next/link"
|
import NextLink from "next/link"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { ChipStatic } from "@scandic-hotels/design-system/ChipStatic"
|
|
||||||
import Image from "@scandic-hotels/design-system/Image"
|
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
||||||
|
|
||||||
import styles from "./contentCard.module.css"
|
import styles from "./contentCard.module.css"
|
||||||
|
|
||||||
import type { ImageVaultAsset } from "@scandic-hotels/common/utils/imageVault"
|
import type { ImageVaultAsset } from "@scandic-hotels/common/utils/imageVault"
|
||||||
|
import { ChipStatic } from "../ChipStatic"
|
||||||
|
import Image from "../Image"
|
||||||
|
import { Typography } from "../Typography"
|
||||||
|
|
||||||
interface ContentCardProps {
|
interface ContentCardProps {
|
||||||
link?: {
|
link?: {
|
||||||
@@ -30,10 +29,11 @@ export function ContentCard({
|
|||||||
image,
|
image,
|
||||||
bodyText,
|
bodyText,
|
||||||
promoText,
|
promoText,
|
||||||
className = "",
|
className,
|
||||||
link,
|
link,
|
||||||
}: ContentCardProps) {
|
}: ContentCardProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
const card = (
|
const card = (
|
||||||
<article className={cx(styles.card, className)}>
|
<article className={cx(styles.card, className)}>
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ContentCard } from "./ContentCard"
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"./Chips": "./lib/components/Chips/index.tsx",
|
"./Chips": "./lib/components/Chips/index.tsx",
|
||||||
"./ChipStatic": "./lib/components/ChipStatic/index.tsx",
|
"./ChipStatic": "./lib/components/ChipStatic/index.tsx",
|
||||||
"./CodeRateCard": "./lib/components/RateCard/Code/index.tsx",
|
"./CodeRateCard": "./lib/components/RateCard/Code/index.tsx",
|
||||||
|
"./ContentCard": "./lib/components/ContentCard/index.tsx",
|
||||||
"./DeprecatedSelect": "./lib/components/DeprecatedSelect/index.tsx",
|
"./DeprecatedSelect": "./lib/components/DeprecatedSelect/index.tsx",
|
||||||
"./Divider": "./lib/components/Divider/index.tsx",
|
"./Divider": "./lib/components/Divider/index.tsx",
|
||||||
"./FacilityToIcon": "./lib/components/FacilityToIcon/index.tsx",
|
"./FacilityToIcon": "./lib/components/FacilityToIcon/index.tsx",
|
||||||
|
|||||||
Reference in New Issue
Block a user