Merged in feat/SW-217-teaser-card-new (pull request #607)

feat(SW-217): Create Teaser component

Approved-by: Erik Tiekstra
Approved-by: Matilda Landström
This commit is contained in:
Pontus Dreij
2024-09-27 08:33:57 +00:00
11 changed files with 199 additions and 60 deletions

View File

@@ -1,9 +1,9 @@
import SectionContainer from "@/components/Section/Container" import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header" import SectionHeader from "@/components/Section/Header"
import Card from "@/components/TempDesignSystem/Card" import Card from "@/components/TempDesignSystem/Card"
import ContentCard from "@/components/TempDesignSystem/ContentCard"
import Grids from "@/components/TempDesignSystem/Grids" import Grids from "@/components/TempDesignSystem/Grids"
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard" import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
import TeaserCard from "@/components/TempDesignSystem/TeaserCard"
import type { CardsGridProps } from "@/types/components/blocks/cardsGrid" import type { CardsGridProps } from "@/types/components/blocks/cardsGrid"
import { CardsGridEnum } from "@/types/enums/cardsGrid" import { CardsGridEnum } from "@/types/enums/cardsGrid"
@@ -22,37 +22,37 @@ export default function CardsGrid({
<Grids.Stackable> <Grids.Stackable>
{cards_grid.cards.map((card) => { {cards_grid.cards.map((card) => {
switch (card.__typename) { switch (card.__typename) {
case CardsGridEnum.cards.Card: { case CardsGridEnum.cards.Card:
return card.isContentCard ? ( return (
<ContentCard <Card
theme={cards_grid.theme ?? "one"}
key={card.system.uid} key={card.system.uid}
scriptedTopTitle={card.scripted_top_title}
heading={card.heading}
bodyText={card.body_text}
secondaryButton={card.secondaryButton}
primaryButton={card.primaryButton}
/>
)
case CardsGridEnum.cards.TeaserCard:
return (
<TeaserCard
key={card.system.uid}
title={card.heading}
description={card.body_text} description={card.body_text}
backgroundImage={card.backgroundImage}
primaryButton={card.primaryButton} primaryButton={card.primaryButton}
secondaryButton={card.secondaryButton} secondaryButton={card.secondaryButton}
sidePeekButton={card.sidePeekButton} sidePeekButton={card.sidePeekButton}
style="default" image={card.image}
title={card.heading}
/>
) : (
<Card
key={card.system.uid}
bodyText={card.body_text}
heading={card.heading}
primaryButton={card.primaryButton}
secondaryButton={card.secondaryButton}
scriptedTopTitle={card.scripted_top_title}
theme={cards_grid.theme || "one"}
/> />
) )
}
case CardsGridEnum.cards.LoyaltyCard: case CardsGridEnum.cards.LoyaltyCard:
return ( return (
<LoyaltyCard <LoyaltyCard
key={card.system.uid} key={card.system.uid}
bodyText={card.body_text}
heading={card.heading}
image={card.image} image={card.image}
heading={card.heading}
bodyText={card.body_text}
link={card.link} link={card.link}
/> />
) )

View File

@@ -7,32 +7,32 @@ import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "../Text/Subtitle" import Subtitle from "../Text/Subtitle"
import { contentCardVariants } from "./variants" import { teaserCardVariants } from "./variants"
import styles from "./contentCard.module.css" import styles from "./teaserCard.module.css"
import type { ContentCardProps } from "@/types/components/contentCard" import type { TeaserCardProps } from "@/types/components/teaserCard"
export default function ContentCard({ export default function TeaserCard({
title, title,
description, description,
primaryButton, primaryButton,
secondaryButton, secondaryButton,
sidePeekButton, sidePeekButton,
backgroundImage, image,
style = "default", style = "default",
alwaysStack = false, alwaysStack = false,
className, className,
}: ContentCardProps) { }: TeaserCardProps) {
const cardClasses = contentCardVariants({ style, alwaysStack, className }) const cardClasses = teaserCardVariants({ style, alwaysStack, className })
return ( return (
<div className={cardClasses}> <article className={cardClasses}>
{backgroundImage && ( {image && (
<div className={styles.imageContainer}> <div className={styles.imageContainer}>
<Image <Image
src={backgroundImage.url} src={image.url}
alt={backgroundImage.meta?.alt || ""} alt={image.meta?.alt || ""}
className={styles.backgroundImage} className={styles.backgroundImage}
width={399} width={399}
height={201} height={201}
@@ -70,6 +70,7 @@ export default function ContentCard({
<Link <Link
href={primaryButton.href} href={primaryButton.href}
target={primaryButton.openInNewTab ? "_blank" : undefined} target={primaryButton.openInNewTab ? "_blank" : undefined}
color="none"
> >
{primaryButton.title} {primaryButton.title}
</Link> </Link>
@@ -93,6 +94,6 @@ export default function ContentCard({
</div> </div>
)} )}
</div> </div>
</div> </article>
) )
} }

View File

@@ -1,8 +1,8 @@
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority"
import styles from "./contentCard.module.css" import styles from "./teaserCard.module.css"
export const contentCardVariants = cva(styles.card, { export const teaserCardVariants = cva(styles.card, {
variants: { variants: {
style: { style: {
default: styles.default, default: styles.default,

View File

@@ -9,9 +9,7 @@ fragment CardBlock on Card {
body_text body_text
has_primary_button has_primary_button
has_secondary_button has_secondary_button
has_sidepeek_button
heading heading
is_content_card
scripted_top_title scripted_top_title
title title
primary_button { primary_button {
@@ -52,9 +50,6 @@ fragment CardBlock on Card {
} }
} }
} }
sidepeek_button {
call_to_action_text
}
system { system {
...System ...System
} }

View File

@@ -1,8 +1,10 @@
#import "./Card.graphql" #import "./Card.graphql"
#import "./LoyaltyCard.graphql" #import "./LoyaltyCard.graphql"
#import "./TeaserCard.graphql"
#import "./Refs/Card.graphql" #import "./Refs/Card.graphql"
#import "./Refs/LoyaltyCard.graphql" #import "./Refs/LoyaltyCard.graphql"
#import "./Refs/TeaserCard.graphql"
fragment CardsGrid_ContentPage on ContentPageBlocksCardsGrid { fragment CardsGrid_ContentPage on ContentPageBlocksCardsGrid {
cards_grid { cards_grid {
layout layout
@@ -15,6 +17,7 @@ fragment CardsGrid_ContentPage on ContentPageBlocksCardsGrid {
__typename __typename
...CardBlock ...CardBlock
...LoyaltyCardBlock ...LoyaltyCardBlock
...TeaserCardBlock
} }
} }
} }
@@ -29,6 +32,7 @@ fragment CardsGrid_ContentPageRefs on ContentPageBlocksCardsGrid {
__typename __typename
...CardBlockRef ...CardBlockRef
...LoyaltyCardBlockRef ...LoyaltyCardBlockRef
...TeaserCardBlockRef
} }
} }
} }

View File

@@ -0,0 +1,36 @@
#import "../../AccountPage/Ref.graphql"
#import "../../ContentPage/Ref.graphql"
#import "../../LoyaltyPage/Ref.graphql"
#import "../../HotelPage/Ref.graphql"
fragment TeaserCardBlockRef on TeaserCard {
secondary_button {
linkConnection {
edges {
node {
__typename
...AccountPageRef
...ContentPageRef
...LoyaltyPageRef
...HotelPageRef
}
}
}
}
primary_button {
linkConnection {
edges {
node {
__typename
...AccountPageRef
...ContentPageRef
...LoyaltyPageRef
...HotelPageRef
}
}
}
}
system {
...System
}
}

View File

@@ -0,0 +1,61 @@
#import "../System.graphql"
#import "../PageLink/AccountPageLink.graphql"
#import "../PageLink/ContentPageLink.graphql"
#import "../PageLink/LoyaltyPageLink.graphql"
#import "../PageLink/HotelPageLink.graphql"
fragment TeaserCardBlock on TeaserCard {
heading
body_text
image
title
has_primary_button
has_secondary_button
has_sidepeek_button
primary_button {
is_contentstack_link
cta_text
open_in_new_tab
external_link {
title
href
}
linkConnection {
edges {
node {
__typename
...LoyaltyPageLink
...ContentPageLink
...AccountPageLink
}
}
}
}
secondary_button {
is_contentstack_link
cta_text
open_in_new_tab
external_link {
title
href
}
linkConnection {
edges {
node {
__typename
...LoyaltyPageLink
...ContentPageLink
...AccountPageLink
...HotelPageLink
}
}
}
}
sidepeek_button {
call_to_action_text
}
system {
...System
}
}

View File

@@ -15,17 +15,10 @@ export const cardBlockSchema = z.object({
body_text: z.string().optional().default(""), body_text: z.string().optional().default(""),
has_primary_button: z.boolean().default(false), has_primary_button: z.boolean().default(false),
has_secondary_button: z.boolean().default(false), has_secondary_button: z.boolean().default(false),
has_sidepeek_button: z.boolean().optional().default(false),
heading: z.string().optional().default(""), heading: z.string().optional().default(""),
is_content_card: z.boolean().optional().default(false),
primary_button: buttonSchema, primary_button: buttonSchema,
scripted_top_title: z.string().optional(), scripted_top_title: z.string().optional(),
secondary_button: buttonSchema, secondary_button: buttonSchema,
sidepeek_button: z
.object({
call_to_action_text: z.string().optional(),
})
.optional(),
system: systemSchema, system: systemSchema,
title: z.string().optional(), title: z.string().optional(),
}) })
@@ -36,23 +29,53 @@ export function transformCardBlock(card: typeof cardBlockSchema._type) {
backgroundImage: card.background_image, backgroundImage: card.background_image,
body_text: card.body_text, body_text: card.body_text,
heading: card.heading, heading: card.heading,
isContentCard: card.is_content_card,
primaryButton: card.has_primary_button ? card.primary_button : undefined, primaryButton: card.has_primary_button ? card.primary_button : undefined,
scripted_top_title: card.scripted_top_title, scripted_top_title: card.scripted_top_title,
secondaryButton: card.has_secondary_button secondaryButton: card.has_secondary_button
? card.secondary_button ? card.secondary_button
: undefined, : undefined,
sidePeekButton:
card.has_sidepeek_button && card.sidepeek_button?.call_to_action_text
? {
title: card.sidepeek_button.call_to_action_text,
}
: undefined,
system: card.system, system: card.system,
title: card.title, title: card.title,
} }
} }
export const teaserCardBlockSchema = z.object({
__typename: z.literal(CardsGridEnum.cards.TeaserCard),
heading: z.string().default(""),
body_text: z.string().default(""),
image: tempImageVaultAssetSchema,
primary_button: buttonSchema,
secondary_button: buttonSchema,
has_primary_button: z.boolean().default(false),
has_secondary_button: z.boolean().default(false),
has_sidepeek_button: z.boolean().default(false),
side_peek_button: z
.object({
title: z.string().optional().default(""),
})
.optional(),
system: systemSchema,
})
export function transformTeaserCardBlock(
card: typeof teaserCardBlockSchema._type
) {
return {
__typename: card.__typename,
body_text: card.body_text,
heading: card.heading,
primaryButton: card.has_primary_button ? card.primary_button : undefined,
secondaryButton: card.has_secondary_button
? card.secondary_button
: undefined,
sidePeekButton: card.has_sidepeek_button
? card.side_peek_button
: undefined,
image: card.image,
system: card.system,
}
}
const loyaltyCardBlockSchema = z.object({ const loyaltyCardBlockSchema = z.object({
__typename: z.literal(CardsGridEnum.cards.LoyaltyCard), __typename: z.literal(CardsGridEnum.cards.LoyaltyCard),
body_text: z.string().optional(), body_text: z.string().optional(),
@@ -77,6 +100,7 @@ export const cardsGridSchema = z.object({
node: z.discriminatedUnion("__typename", [ node: z.discriminatedUnion("__typename", [
cardBlockSchema, cardBlockSchema,
loyaltyCardBlockSchema, loyaltyCardBlockSchema,
teaserCardBlockSchema,
]), ]),
}) })
), ),
@@ -95,6 +119,8 @@ export const cardsGridSchema = z.object({
cards: data.cardConnection.edges.map((card) => { cards: data.cardConnection.edges.map((card) => {
if (card.node.__typename === CardsGridEnum.cards.Card) { if (card.node.__typename === CardsGridEnum.cards.Card) {
return transformCardBlock(card.node) return transformCardBlock(card.node)
} else if (card.node.__typename === CardsGridEnum.cards.TeaserCard) {
return transformTeaserCardBlock(card.node)
} else { } else {
return { return {
__typename: card.node.__typename, __typename: card.node.__typename,
@@ -118,7 +144,11 @@ export const cardBlockRefsSchema = z.object({
system: systemSchema, system: systemSchema,
}) })
export function transformCardBlockRefs(card: typeof cardBlockRefsSchema._type) { export function transformCardBlockRefs(
card:
| typeof cardBlockRefsSchema._type
| typeof teaserCardBlockRefsSchema._type
) {
const cards = [card.system] const cards = [card.system]
if (card.primary_button) { if (card.primary_button) {
cards.push(card.primary_button) cards.push(card.primary_button)
@@ -135,6 +165,13 @@ const loyaltyCardBlockRefsSchema = z.object({
system: systemSchema, system: systemSchema,
}) })
export const teaserCardBlockRefsSchema = z.object({
__typename: z.literal(CardsGridEnum.cards.TeaserCard),
primary_button: linkConnectionRefsSchema,
secondary_button: linkConnectionRefsSchema,
system: systemSchema,
})
export const cardGridRefsSchema = z.object({ export const cardGridRefsSchema = z.object({
cards_grid: z cards_grid: z
.object({ .object({
@@ -144,6 +181,7 @@ export const cardGridRefsSchema = z.object({
node: z.discriminatedUnion("__typename", [ node: z.discriminatedUnion("__typename", [
cardBlockRefsSchema, cardBlockRefsSchema,
loyaltyCardBlockRefsSchema, loyaltyCardBlockRefsSchema,
teaserCardBlockRefsSchema,
]), ]),
}) })
), ),
@@ -152,7 +190,10 @@ export const cardGridRefsSchema = z.object({
.transform((data) => { .transform((data) => {
return data.cardConnection.edges return data.cardConnection.edges
.map(({ node }) => { .map(({ node }) => {
if (node.__typename === CardsGridEnum.cards.Card) { if (
node.__typename === CardsGridEnum.cards.Card ||
node.__typename === CardsGridEnum.cards.TeaserCard
) {
return transformCardBlockRefs(node) return transformCardBlockRefs(node)
} else { } else {
const loyaltyCards = [node.system] const loyaltyCards = [node.system]

View File

@@ -1,6 +1,6 @@
import { VariantProps } from "class-variance-authority" import { VariantProps } from "class-variance-authority"
import { contentCardVariants } from "@/components/TempDesignSystem/ContentCard/variants" import { teaserCardVariants } from "@/components/TempDesignSystem/TeaserCard/variants"
import { ImageVaultAsset } from "@/types/components/imageVault" import { ImageVaultAsset } from "@/types/components/imageVault"
import type { CardProps } from "@/components/TempDesignSystem/Card/card" import type { CardProps } from "@/components/TempDesignSystem/Card/card"
@@ -9,13 +9,13 @@ interface SidePeekButton {
title: string title: string
} }
export interface ContentCardProps export interface TeaserCardProps
extends VariantProps<typeof contentCardVariants> { extends VariantProps<typeof teaserCardVariants> {
title: string title: string
description: string description: string
primaryButton?: CardProps["primaryButton"] primaryButton?: CardProps["primaryButton"]
secondaryButton?: CardProps["secondaryButton"] secondaryButton?: CardProps["secondaryButton"]
sidePeekButton?: SidePeekButton sidePeekButton?: SidePeekButton
backgroundImage?: ImageVaultAsset image?: ImageVaultAsset
className?: string className?: string
} }

View File

@@ -2,5 +2,6 @@ export namespace CardsGridEnum {
export const enum cards { export const enum cards {
Card = "Card", Card = "Card",
LoyaltyCard = "LoyaltyCard", LoyaltyCard = "LoyaltyCard",
TeaserCard = "TeaserCard",
} }
} }