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:
32
components/Blocks/CarouselCards/Filters/filters.module.css
Normal file
32
components/Blocks/CarouselCards/Filters/filters.module.css
Normal 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;
|
||||||
|
}
|
||||||
36
components/Blocks/CarouselCards/Filters/index.tsx
Normal file
36
components/Blocks/CarouselCards/Filters/index.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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).
|
||||||
@@ -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>
|
||||||
@@ -6,7 +6,7 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|||||||
|
|
||||||
import styles from "./contentCard.module.css"
|
import styles from "./contentCard.module.css"
|
||||||
|
|
||||||
import type { ContentCardLinkProps,ContentCardProps } from "./contentCard"
|
import type { ContentCardLinkProps, ContentCardProps } from "./contentCard"
|
||||||
|
|
||||||
export default function ContentCard({
|
export default function ContentCard({
|
||||||
heading,
|
heading,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user