Merged in feat/SW-1443-card-gallery-destination-overview (pull request #1362)

feat(SW-1443): added cardGallery block to destination overview page instead of carousel functionality

* feat(SW-1443): added cardGallery block to destination overview page instead of carousel functionality


Approved-by: Fredrik Thorsson
This commit is contained in:
Erik Tiekstra
2025-02-18 14:42:36 +00:00
parent f14eb05262
commit 2781a41110
16 changed files with 337 additions and 102 deletions
@@ -0,0 +1,25 @@
.cardsList {
list-style: none;
display: none;
gap: var(--Spacing-x4) var(--Spacing-x1);
}
.navigationButton {
top: 30%;
}
@media screen and (min-width: 768px) {
.carousel {
display: none;
}
.cardsList {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (min-width: 1024px) {
.cardsList {
grid-template-columns: repeat(3, 1fr);
}
}
+67
View File
@@ -0,0 +1,67 @@
"use client"
import { useState } from "react"
import { Carousel } from "@/components/Carousel"
import ContentCard from "@/components/ContentCard"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link"
import TabFilters from "@/components/TabFilters"
import styles from "./cardGallery.module.css"
import type { CardGalleryProps } from "@/types/components/blocks/cardGallery"
export default function CardGallery({ card_gallery }: CardGalleryProps) {
const { heading, defaultFilter, filterCategories, cards, link } = card_gallery
const [activeFilter, setActiveFilter] = useState(defaultFilter)
const filteredCards = cards.filter((card) => card.filterId === activeFilter)
return (
<SectionContainer>
<SectionHeader title={heading} link={link} />
{filterCategories.length > 0 && activeFilter && (
<TabFilters
categories={filterCategories}
selectedFilter={activeFilter}
onFilterSelect={setActiveFilter}
/>
)}
<ul className={styles.cardsList}>
{filteredCards.map((card, index) => (
<li key={`${card.heading}-${index}`}>
<ContentCard
heading={card.heading}
image={card.image}
bodyText={card.bodyText}
promoText={card.promoText}
link={card.link}
/>
</li>
))}
</ul>
<Carousel className={styles.carousel}>
<Carousel.Content>
{filteredCards.map((card, index) => (
<Carousel.Item key={`${card.heading}-${index}`}>
<ContentCard
heading={card.heading}
image={card.image}
bodyText={card.bodyText}
promoText={card.promoText}
link={card.link}
/>
</Carousel.Item>
))}
</Carousel.Content>
<Carousel.Previous className={styles.navigationButton} />
<Carousel.Next className={styles.navigationButton} />
<Carousel.Dots />
</Carousel>
<SectionLink link={link} variant="mobile" />
</SectionContainer>
)
}
@@ -1,32 +0,0 @@
.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;
}
@@ -1,36 +0,0 @@
"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>
)
}
+2 -3
View File
@@ -7,8 +7,7 @@ import ContentCard from "@/components/ContentCard"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link"
import Filters from "./Filters"
import TabFilters from "@/components/TabFilters"
import styles from "./carouselCards.module.css"
@@ -43,7 +42,7 @@ export default function CarouselCards({ carousel_cards }: CarouselCardsProps) {
link={link}
/>
{filterCategories.length > 0 && activeFilter && (
<Filters
<TabFilters
categories={filterCategories}
selectedFilter={activeFilter}
onFilterSelect={setActiveFilter}
+8
View File
@@ -9,6 +9,7 @@ import UspGrid from "@/components/Blocks/UspGrid"
import JsonToHtml from "@/components/JsonToHtml"
import AccordionSection from "./Accordion"
import CardGallery from "./CardGallery"
import FullWidthCampaign from "./FullWidthCampaign"
import HotelListing from "./HotelListing"
import JoinScandicFriends from "./JoinScandicFriends"
@@ -61,6 +62,13 @@ export default function Blocks({ blocks }: BlocksProps) {
key={`${block.carousel_cards.heading}-${idx}`}
/>
)
case BlocksEnums.block.CardGallery:
return (
<CardGallery
card_gallery={block.card_gallery}
key={`${block.card_gallery.heading}-${idx}`}
/>
)
case BlocksEnums.block.HotelListing:
const { heading, contentType, locationFilter, hotelsToInclude } =
block.hotel_listing