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 Mock styles for the carousel cards. Will be removed/replaced
when the carousel functionality is implemented (SW-1542). 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 ContentCard from "@/components/ContentCard"
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 Filters from "./Filters"
import styles from "./carouselCards.module.css" import styles from "./carouselCards.module.css"
import type { CarouselCardsProps } from "@/types/components/blocks/carouselCards" import type { CarouselCardsProps } from "@/types/components/blocks/carouselCards"
@@ -16,30 +22,36 @@ export default function CarouselCards({ carousel_cards }: CarouselCardsProps) {
link, link,
} = carousel_cards } = carousel_cards
const [activeFilter, setActiveFilter] = useState(
enableFilters ? defaultFilter : null
)
const filteredCards = !activeFilter
? cards
: cards.filter(
(card) => "filterId" in card && card.filterId === activeFilter
)
return ( return (
<SectionContainer> <SectionContainer>
<SectionHeader title={heading} link={link} /> <SectionHeader title={heading} link={link} />
{enableFilters && ( {filterCategories.length > 0 && activeFilter && (
<details> <Filters
<summary>Filter data</summary> categories={filterCategories}
<div> selectedFilter={activeFilter}
{/* Filter component will go here */} onFilterSelect={setActiveFilter}
<pre className={styles.code}> />
{JSON.stringify({ filterCategories, defaultFilter }, null, 2)}
</pre>
</div>
</details>
)} )}
{/* Carousel functionality will go here */} {/* Carousel functionality will go here */}
<div className={styles.cardsContainer}> <div className={styles.cardsContainer}>
{cards.map((card) => ( {filteredCards.map((card) => (
<ContentCard <ContentCard
link={card.link}
key={card.heading} key={card.heading}
heading={card.heading} heading={card.heading}
bodyText={card.bodyText}
image={card.image} image={card.image}
bodyText={card.bodyText}
promoText={card.promoText} promoText={card.promoText}
link={card.link}
/> />
))} ))}
</div> </div>

View File

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

View File

@@ -33,7 +33,6 @@ const carouselCardsWithFilters = z.object({
}), }),
}) })
), ),
default_filter: z.nativeEnum(CarouselCardFilterEnum),
}) })
const carouselCardsWithoutFilters = z.object({ const carouselCardsWithoutFilters = z.object({
@@ -50,7 +49,6 @@ const carouselCardsWithoutFilters = z.object({
}), }),
}) })
), ),
default_filter: z.null(),
}) })
export const carouselCardsSchema = z.object({ export const carouselCardsSchema = z.object({
@@ -112,7 +110,9 @@ export const carouselCardsSchema = z.object({
filterId: group.filter_category.filter_identifier, 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 link: data.link
? { href: data.link.href, text: data.link.title } ? { href: data.link.href, text: data.link.title }
: undefined, : undefined,

View File

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