feat(BOOK-53): Added component for SEO filters and support filter switching

Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Erik Tiekstra
2025-09-19 08:26:41 +00:00
parent 0e30a2d218
commit 7c92a8fc9a
30 changed files with 339 additions and 111 deletions

View File

@@ -1,45 +1,42 @@
.accordionItem {
border-bottom: 1px solid var(--Base-Border-Subtle);
}
border-bottom: 1px solid var(--Border-Default);
.accordionItem.card {
padding: var(--Spacing-x1);
}
&.card {
padding: var(--Space-x1);
.accordionItem.sidepeek {
padding: var(--Spacing-x1) 0;
.summary {
padding: var(--Space-x15) var(--Space-x2);
}
}
&.sidepeek {
padding: var(--Space-x1) 0;
.summary {
padding: var(--Space-x15) var(--Space-x1);
align-items: center;
}
}
.summary:hover {
background-color: var(--Surface-Primary-Hover);
}
}
.summary {
position: relative;
display: flex;
align-items: center;
gap: var(--Spacing-x-one-and-half) var(--Spacing-x1);
gap: var(--Space-x15) var(--Space-x1);
cursor: pointer;
color: var(--Base-Text-High-contrast);
font-family: var(--typography-Body-Bold-fontFamily);
font-size: var(--typography-Body-Bold-fontSize);
font-weight: var(--typography-Body-Bold-fontWeight);
color: var(--Text-Interactive-Default);
transition: background-color 0.3s;
}
.summary.card:hover {
background-color: var(--Base-Surface-Primary-light-Hover-alt);
}
.accordionItem.light .summary:hover {
background-color: var(--Base-Surface-Primary-light-Hover);
}
.accordionItem.subtle .summary:hover {
background-color: var(--Base-Surface-Primary-light-Normal);
}
.accordionItem.card .summary {
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
border-radius: var(--Corner-radius-md);
}
list-style: none;
.accordionItem.sidepeek .summary {
padding: var(--Spacing-x-one-and-half) var(--Spacing-x1);
align-items: center;
&::-webkit-details-marker {
display: none;
}
}
.title {
@@ -47,7 +44,7 @@
}
.content {
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2) var(--Spacing-x1);
padding: var(--Space-x15) var(--Space-x2) var(--Space-x1);
overflow: hidden;
max-height: 0;
transition: max-height 0.3s;
@@ -57,6 +54,7 @@
transition: transform 0.3s;
flex-shrink: 0;
}
details[open] .chevron {
transform: rotate(180deg);
}

View File

@@ -2,17 +2,16 @@
import { type ReactNode, useRef } from 'react'
import Body from '../../Body'
import { IconByIconName } from '../../Icons/IconByIconName'
import { MaterialIcon } from '../../Icons/MaterialIcon'
import Subtitle from '../../Subtitle'
import { accordionItemVariants } from './variants'
import styles from './accordionItem.module.css'
import type { IconName } from '../../Icons/iconName'
import type { VariantProps } from 'class-variance-authority'
import type { IconName } from '../../Icons/iconName'
import { Typography } from '../../Typography'
export interface AccordionItemProps
extends React.HtmlHTMLAttributes<HTMLDetailsElement>,
@@ -21,6 +20,7 @@ export interface AccordionItemProps
iconName?: IconName
icon?: ReactNode
subtitle?: string
showAsSubtitle?: boolean
onOpen?: () => void
}
@@ -29,10 +29,10 @@ export default function AccordionItem({
icon,
iconName,
title,
theme,
variant,
type,
className,
subtitle,
showAsSubtitle = false,
onOpen,
}: AccordionItemProps) {
const contentRef = useRef<HTMLDivElement>(null)
@@ -71,36 +71,36 @@ export default function AccordionItem({
}
return (
<li className={accordionItemVariants({ className, variant, theme })}>
<li className={accordionItemVariants({ className, type })}>
<details ref={detailsRef} onToggle={toggleAccordion}>
<summary className={styles.summary}>
{IconComp}
{variant === 'sidepeek' ? (
<Subtitle
className={styles.title}
type="two"
color="baseTextHighContrast"
>
{title}
</Subtitle>
{type === 'sidepeek' ? (
<Typography variant="Title/Subtitle/md">
<p className={styles.title}>{title}</p>
</Typography>
) : (
<div className={styles.title}>
{subtitle ? (
<Subtitle type="two" color="baseTextHighContrast">
{title}
</Subtitle>
{subtitle || showAsSubtitle ? (
<Typography variant="Title/Subtitle/md">
<p className={styles.title}>{title}</p>
</Typography>
) : (
<Body textTransform="bold" color="baseTextHighContrast">
{title}
</Body>
<Typography variant="Body/Paragraph/mdBold">
<p className={styles.title}>{title}</p>
</Typography>
)}
{subtitle && (
<Typography variant="Body/Paragraph/mdBold">
<p>{subtitle}</p>
</Typography>
)}
{subtitle && <Body color="baseTextHighContrast">{subtitle}</Body>}
</div>
)}
<MaterialIcon
icon="keyboard_arrow_down"
className={styles.chevron}
color="Icon/Interactive/Default"
color="CurrentColor"
size={20}
/>
</summary>

View File

@@ -4,18 +4,12 @@ import styles from './accordionItem.module.css'
export const accordionItemVariants = cva(styles.accordionItem, {
variants: {
variant: {
type: {
card: styles.card,
sidepeek: styles.sidepeek,
},
theme: {
default: styles.default,
light: styles.light,
subtle: styles.subtle,
},
},
defaultVariants: {
variant: 'card',
theme: 'default',
type: 'card',
},
})

View File

@@ -1,25 +1,11 @@
.accordion {
list-style: none;
}
.accordion.card {
border-radius: var(--Corner-radius-md);
}
&.card {
border-radius: var(--Corner-radius-md);
.accordion.light {
background-color: var(--Base-Surface-Primary-light-Normal);
}
.accordion.subtle {
background-color: var(--Background-Primary);
}
.accordion li:last-child {
border: none;
}
.accordion details > summary {
list-style: none;
}
.accordion details > summary::-webkit-details-marker {
display: none;
li:last-child {
border-width: 0;
}
}
}

View File

@@ -1,10 +1,9 @@
import { Children, cloneElement, isValidElement } from 'react'
import { accordionVariants } from './variants'
import type { VariantProps } from 'class-variance-authority'
import type { AccordionItemProps } from './AccordionItem'
import { accordionVariants } from './variants'
interface AccordionProps
extends React.HtmlHTMLAttributes<HTMLUListElement>,
@@ -13,14 +12,13 @@ interface AccordionProps
export default function Accordion({
children,
className,
theme,
variant,
type,
}: AccordionProps) {
return (
<ul className={accordionVariants({ className, variant, theme })}>
<ul className={accordionVariants({ className, type })}>
{Children.map(children, (child) => {
if (isValidElement<AccordionItemProps>(child)) {
return cloneElement(child, { variant, theme })
return cloneElement(child, { type })
} else {
return child
}

View File

@@ -4,18 +4,12 @@ import styles from './accordion.module.css'
export const accordionVariants = cva(styles.accordion, {
variants: {
variant: {
type: {
card: styles.card,
sidepeek: styles.sidepeek,
},
theme: {
default: styles.default,
light: styles.light,
subtle: styles.subtle,
},
},
defaultVariants: {
variant: 'card',
theme: 'default',
type: 'card',
},
})