Skip to content

Commit 4ddf4ff

Browse files
committed
✨ Improve customizability of Accordion component
1 parent 91f0389 commit 4ddf4ff

File tree

9 files changed

+160
-32
lines changed

9 files changed

+160
-32
lines changed

src/components/Accordion/Accordion.astro

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,56 @@
22
import type { AccordionProps } from './accordion'
33
44
import ArrowDown from '../../icons/arrow-down.svg?raw'
5+
import Plus from '../../icons/plus.svg?raw'
56
67
import styles from './accordion.module.scss'
78
89
interface Props extends AccordionProps {}
910
1011
const {
11-
items
12+
items,
13+
icon,
14+
reverse,
15+
className
1216
} = Astro.props
17+
18+
const classes = [
19+
styles.accordion,
20+
reverse && styles.reverse,
21+
icon === 'plus' && styles.plus,
22+
className
23+
]
1324
---
1425

15-
<ul class={styles.accordion} data-id="w-accordion">
26+
<ul class:list={classes} data-id="w-accordion">
1627
{items.map((item: AccordionProps['items'][0]) => (
1728
<li>
18-
<button class={styles.title} data-toggle="true">
29+
<button
30+
class:list={[styles.title, item.reverse && styles.reverse]}
31+
data-toggle="true"
32+
>
1933
{item.title}
20-
<Fragment set:html={ArrowDown} />
34+
{icon !== 'none' && (
35+
<Fragment set:html={icon === 'plus' ? Plus : ArrowDown} />
36+
)}
2137
</button>
2238
<div class={styles.wrapper}>
23-
<div class={styles.content}>
24-
<Fragment set:html={item.content} />
25-
</div>
39+
<div class={styles.content} set:html={item.content} />
2640
</div>
2741
</li>
2842
))}
2943
</ul>
3044

3145
<script>
32-
const accordions = document.querySelectorAll('[data-id="w-accordion"]')
33-
34-
Array.from(accordions).forEach(element => {
35-
element.addEventListener('click', event => {
36-
const target = event.target as HTMLDivElement
37-
38-
if (target.dataset.toggle) {
39-
target.dataset.open = target.dataset.open === 'true'
40-
? 'false'
41-
: 'true'
42-
}
43-
})
44-
})
46+
import { on } from '../../utils/DOMUtils'
47+
48+
on('[data-id="w-accordion"]', 'click', (event: Event) => {
49+
const target = event.target as HTMLDivElement
50+
51+
if (target.dataset.toggle) {
52+
target.dataset.open = target.dataset.open === 'true'
53+
? 'false'
54+
: 'true'
55+
}
56+
}, true)
4557
</script>

src/components/Accordion/Accordion.svelte

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
<script lang="ts">
22
import type { AccordionProps } from './accordion'
33
4+
import { classNames } from '../../utils/classNames'
5+
46
import ArrowDown from '../../icons/arrow-down.svg?raw'
7+
import Plus from '../../icons/plus.svg?raw'
58
69
import styles from './accordion.module.scss'
710
8-
export let items: AccordionProps['items']
11+
export let items: AccordionProps['items'] = []
12+
export let icon: AccordionProps['icon'] = null
13+
export let reverse: AccordionProps['reverse'] = false
14+
export let className: AccordionProps['className'] = ''
915
1016
let state = Array(items.length).fill(false)
1117
@@ -15,18 +21,30 @@
1521
: state[i]
1622
)
1723
}
24+
25+
const classes = classNames([
26+
styles.accordion,
27+
reverse && styles.reverse,
28+
icon === 'plus' && styles.plus,
29+
className
30+
])
1831
</script>
1932

20-
<ul class={styles.accordion}>
33+
<ul class={classes}>
2134
{#each items as item, index}
2235
<li>
2336
<button
24-
class={styles.title}
37+
class={classNames([
38+
styles.title,
39+
item.reverse && styles.reverse
40+
])}
2541
data-open={state[index]}
2642
on:click={() => toggle(index)}
2743
>
2844
{item.title}
29-
{@html ArrowDown}
45+
{#if icon !== 'none'}
46+
{@html icon === 'plus' ? Plus : ArrowDown}
47+
{/if}
3048
</button>
3149
<div class={styles.wrapper}>
3250
<div class={styles.content}>

src/components/Accordion/Accordion.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import React, { useState } from 'react'
22
import type { AccordionProps } from './accordion'
33

4+
import { classNames } from '../../utils/classNames'
5+
46
import ArrowDown from '../../icons/arrow-down.svg?raw'
7+
import Plus from '../../icons/plus.svg?raw'
58

69
import styles from './accordion.module.scss'
710

8-
const Accordion = ({ items }: AccordionProps) => {
11+
const Accordion = ({
12+
items,
13+
icon,
14+
reverse,
15+
className
16+
}: AccordionProps) => {
917
const [state, setState] = useState(Array(items.length).fill(false))
1018

1119
const toggle = (index: number) => {
@@ -15,20 +23,34 @@ const Accordion = ({ items }: AccordionProps) => {
1523
))
1624
}
1725

26+
const classes = classNames([
27+
styles.accordion,
28+
reverse && styles.reverse,
29+
icon === 'plus' && styles.plus,
30+
className
31+
])
32+
1833
return (
19-
<ul className={styles.accordion}>
34+
<ul className={classes}>
2035
{items.map((item, index) => (
2136
<li key={index}>
2237
<button
23-
className={styles.title}
2438
data-open={state[index]}
2539
onClick={() => toggle(index)}
26-
dangerouslySetInnerHTML={{ __html: `${item.title} ${ArrowDown}` }}
40+
className={classNames([
41+
styles.title,
42+
item.reverse && styles.reverse
43+
])}
44+
dangerouslySetInnerHTML={{ __html: icon === 'none'
45+
? item.title
46+
: `${item.title} ${icon === 'plus' ? Plus : ArrowDown}`
47+
}}
2748
/>
2849
<div className={styles.wrapper}>
29-
<div className={styles.content}>
30-
<div dangerouslySetInnerHTML={{ __html: item.content }} />
31-
</div>
50+
<div
51+
className={styles.content}
52+
dangerouslySetInnerHTML={{ __html: item.content }}
53+
/>
3254
</div>
3355
</li>
3456
))}

src/components/Accordion/accordion.module.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
@include spacing(0);
55
list-style-type: none;
66

7+
&.reverse .title {
8+
@include layout(row-reverse, h-end);
9+
}
10+
11+
&.plus .title[data-open="true"] svg {
12+
transform: rotate(135deg);
13+
}
14+
715
li {
816
@include border(primary-50, bottom);
917
@include spacing(py-sm, px-none, m0);
@@ -27,6 +35,10 @@
2735
background: transparent;
2836
cursor: pointer;
2937

38+
&.reverse {
39+
@include layout(row-reverse);
40+
}
41+
3042
svg {
3143
@include transition(transform);
3244
@include size(15px);

src/components/Accordion/accordion.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@ export type AccordionProps = {
22
items: {
33
title: string
44
content: string
5+
reverse?: boolean
56
}[]
7+
icon?: 'plus' | 'none' | undefined | null
8+
reverse?: boolean
9+
className?: string
610
}

src/components/Icon/map.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Home from '../../icons/home.svg?raw'
1313
import Info from '../../icons/info.svg?raw'
1414
import Moon from '../../icons/moon.svg?raw'
1515
import Order from '../../icons/order.svg?raw'
16+
import Plus from '../../icons/plus.svg?raw'
1617
import Search from '../../icons/search.svg?raw'
1718
import Sun from '../../icons/sun.svg?raw'
1819
import Warning from '../../icons/warning.svg?raw'
@@ -33,6 +34,7 @@ const iconMap = {
3334
'info': Info,
3435
'moon': Moon,
3536
'order': Order,
37+
'plus': Plus,
3638
'search': Search,
3739
'sun': Sun,
3840
'warning': Warning

src/icons/close.svg

Lines changed: 1 addition & 1 deletion
Loading

src/icons/plus.svg

Lines changed: 3 additions & 0 deletions
Loading

src/pages/components/accordion.astro

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,23 @@ import AstroAccordion from '@components/Accordion/Accordion.astro'
66
import SvelteAccordion from '@components/Accordion/Accordion.svelte'
77
import ReactAccordion from '@components/Accordion/Accordion.tsx'
88
9+
import { getSections } from '@helpers'
910
import { accordionItems } from '@data'
11+
12+
const alternatingAccordionItems = [
13+
accordionItems[0],
14+
{
15+
...accordionItems[1],
16+
reverse: true
17+
},
18+
accordionItems[2]
19+
]
20+
21+
const sections = getSections({
22+
title: 'accordions',
23+
components: [AstroAccordion, SvelteAccordion, ReactAccordion],
24+
showSubTitle: true
25+
})
1026
---
1127

1228
<Layout>
@@ -25,4 +41,43 @@ import { accordionItems } from '@data'
2541
<ReactAccordion items={accordionItems} client:load />
2642
</ComponentWrapper>
2743
</div>
44+
45+
{sections.map(section => (
46+
<h1>{section.title}</h1>
47+
<Fragment>
48+
{section.subTitle && <h2 set:html={section.subTitle} />}
49+
</Fragment>
50+
<div class="grid md-2 lg-3">
51+
<ComponentWrapper title="Default">
52+
<section.component items={accordionItems} />
53+
</ComponentWrapper>
54+
55+
<ComponentWrapper title="Plus icon">
56+
<section.component
57+
items={accordionItems}
58+
icon="plus"
59+
/>
60+
</ComponentWrapper>
61+
62+
<ComponentWrapper title="Without icon">
63+
<section.component
64+
items={accordionItems}
65+
icon="none"
66+
/>
67+
</ComponentWrapper>
68+
69+
<ComponentWrapper title="Reverse layout">
70+
<section.component
71+
items={accordionItems}
72+
reverse={true}
73+
/>
74+
</ComponentWrapper>
75+
76+
<ComponentWrapper title="Alternating layout">
77+
<section.component
78+
items={alternatingAccordionItems}
79+
/>
80+
</ComponentWrapper>
81+
</div>
82+
))}
2883
</Layout>

0 commit comments

Comments
 (0)