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>
|
||||
)
|
||||
}
|
||||
31
components/Blocks/CarouselCards/carouselCards.module.css
Normal file
31
components/Blocks/CarouselCards/carouselCards.module.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Mock styles for the carousel cards. Will be removed/replaced
|
||||
when the carousel functionality is implemented (SW-1542).
|
||||
*/
|
||||
.cardsContainer {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 100%;
|
||||
overflow-x: auto;
|
||||
overscroll-behavior-x: contain;
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
|
||||
.cardsContainer > * {
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
/* Show 2 cards on tablet */
|
||||
@media (min-width: 768px) {
|
||||
.cardsContainer {
|
||||
grid-auto-columns: calc((100% - var(--Spacing-x3)) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Show 3 cards on desktop */
|
||||
@media (min-width: 1024px) {
|
||||
.cardsContainer {
|
||||
grid-auto-columns: calc((100% - var(--Spacing-x3) * 2) / 3);
|
||||
}
|
||||
}
|
||||
60
components/Blocks/CarouselCards/index.tsx
Normal file
60
components/Blocks/CarouselCards/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"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"
|
||||
|
||||
export default function CarouselCards({ carousel_cards }: CarouselCardsProps) {
|
||||
const {
|
||||
heading,
|
||||
enableFilters,
|
||||
filterCategories,
|
||||
cards,
|
||||
defaultFilter,
|
||||
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} />
|
||||
{filterCategories.length > 0 && activeFilter && (
|
||||
<Filters
|
||||
categories={filterCategories}
|
||||
selectedFilter={activeFilter}
|
||||
onFilterSelect={setActiveFilter}
|
||||
/>
|
||||
)}
|
||||
{/* Carousel functionality will go here */}
|
||||
<div className={styles.cardsContainer}>
|
||||
{filteredCards.map((card) => (
|
||||
<ContentCard
|
||||
key={card.heading}
|
||||
heading={card.heading}
|
||||
image={card.image}
|
||||
bodyText={card.bodyText}
|
||||
promoText={card.promoText}
|
||||
link={card.link}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user