Merged in feat/SW-1384-filter-functionality (pull request #1262)

feat(SW-1384): Implement filtering for CarouselCards component

* feat(SW-1384): Implement filtering for CarouselCards component

* fix(SW-1384): Simplify CarouselCards filters scrolling styles

* refactor(SW-1384): Simplify CarouselCards filtering logic


Approved-by: Christian Andolf
This commit is contained in:
Chuma Mcphoy (We Ahead)
2025-02-06 14:32:55 +00:00
parent 3d1295e4d5
commit 7c8d6cbc2e
8 changed files with 98 additions and 23 deletions

View File

@@ -0,0 +1,32 @@
.container {
display: flex;
gap: var(--Spacing-x1);
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
}
.filter,
.filterSelected {
border-radius: var(--Corner-radius-Rounded);
padding: var(--Spacing-x1) var(--Spacing-x2);
transition: all 0.2s ease-in-out;
scroll-snap-align: start;
flex-shrink: 0;
font-size: var(--typography-Caption-Bold-Mobile-fontSize);
font-family: var(--typography-Body-Regular-fontFamily);
font-weight: 400;
cursor: pointer;
}
.filter {
color: var(--UI-Text-High-contrast);
background: transparent;
border: 1px solid var(--UI-Input-Controls-Border-Hover);
}
.filterSelected {
color: var(--Base-Text-Inverted);
background: var(--Base-Button-Tertiary-Fill-Normal);
border: 1px solid transparent;
}

View File

@@ -0,0 +1,36 @@
"use client"
import styles from "./filters.module.css"
import type { CarouselCardFilter } from "@/types/enums/carouselCards"
interface FiltersProps {
categories: Array<{ identifier: CarouselCardFilter; label: string }>
selectedFilter: CarouselCardFilter
onFilterSelect: (filter: CarouselCardFilter) => void
}
export default function Filters({
categories,
selectedFilter,
onFilterSelect,
}: FiltersProps) {
return (
<div className={styles.container}>
{categories.map((category) => (
<button
key={category.identifier}
onClick={() => onFilterSelect(category.identifier)}
className={
selectedFilter === category.identifier
? styles.filterSelected
: styles.filter
}
type="button"
>
{category.label}
</button>
))}
</div>
)
}

View File

@@ -1,8 +1,3 @@
.code {
padding: var(--Spacing-x2);
background: var(--Base-Surface-Secondary-light-Normal);
}
/*
Mock styles for the carousel cards. Will be removed/replaced
when the carousel functionality is implemented (SW-1542).

View File

@@ -1,7 +1,13 @@
"use client"
import { useState } from "react"
import ContentCard from "@/components/ContentCard"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import Filters from "./Filters"
import styles from "./carouselCards.module.css"
import type { CarouselCardsProps } from "@/types/components/blocks/carouselCards"
@@ -16,30 +22,36 @@ export default function CarouselCards({ carousel_cards }: CarouselCardsProps) {
link,
} = carousel_cards
const [activeFilter, setActiveFilter] = useState(
enableFilters ? defaultFilter : null
)
const filteredCards = !activeFilter
? cards
: cards.filter(
(card) => "filterId" in card && card.filterId === activeFilter
)
return (
<SectionContainer>
<SectionHeader title={heading} link={link} />
{enableFilters && (
<details>
<summary>Filter data</summary>
<div>
{/* Filter component will go here */}
<pre className={styles.code}>
{JSON.stringify({ filterCategories, defaultFilter }, null, 2)}
</pre>
</div>
</details>
{filterCategories.length > 0 && activeFilter && (
<Filters
categories={filterCategories}
selectedFilter={activeFilter}
onFilterSelect={setActiveFilter}
/>
)}
{/* Carousel functionality will go here */}
<div className={styles.cardsContainer}>
{cards.map((card) => (
{filteredCards.map((card) => (
<ContentCard
link={card.link}
key={card.heading}
heading={card.heading}
bodyText={card.bodyText}
image={card.image}
bodyText={card.bodyText}
promoText={card.promoText}
link={card.link}
/>
))}
</div>

View File

@@ -6,7 +6,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./contentCard.module.css"
import type { ContentCardLinkProps,ContentCardProps } from "./contentCard"
import type { ContentCardLinkProps, ContentCardProps } from "./contentCard"
export default function ContentCard({
heading,

View File

@@ -23,7 +23,6 @@ fragment CarouselCards_StartPage on StartPageBlocksCarouselCards {
carousel_cards {
heading
enable_filters
default_filter
card_groups {
filter_category {
filter_identifier

View File

@@ -33,7 +33,6 @@ const carouselCardsWithFilters = z.object({
}),
})
),
default_filter: z.nativeEnum(CarouselCardFilterEnum),
})
const carouselCardsWithoutFilters = z.object({
@@ -50,7 +49,6 @@ const carouselCardsWithoutFilters = z.object({
}),
})
),
default_filter: z.null(),
})
export const carouselCardsSchema = z.object({
@@ -112,7 +110,9 @@ export const carouselCardsSchema = z.object({
filterId: group.filter_category.filter_identifier,
}))
),
defaultFilter: data.default_filter,
defaultFilter:
data.card_groups[0]?.filter_category.filter_identifier ??
filterCategories[0]?.identifier,
link: data.link
? { href: data.link.href, text: data.link.title }
: undefined,

View File

@@ -1,5 +1,6 @@
export const CarouselCardFilterEnum = {
offers: "offers",
popular_destinations: "popular_destinations",
popular_hotels: "popular_hotels",
popular_cities: "popular_cities",
spa_wellness: "spa_wellness",