Skip to content

Commit 135a6ba

Browse files
committed
refactor(accordion): update behavior of component
1 parent 0f3a098 commit 135a6ba

File tree

10 files changed

+415
-254
lines changed

10 files changed

+415
-254
lines changed

docs/4.0/api/CAccordion.api.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
| Property | Description | Type | Default |
22
| --- | --- | --- | --- |
3+
| **activeItemKey** | The active item key. | `string | number` | undefined |
4+
| **alwaysOpen** | Make accordion items stay open when another item is opened | `boolean` | false |
35
| **className** | A string of all className you want applied to the base component. | `string` | - |
46
| **flush** | Removes the default background-color, some borders, and some rounded corners to render accordions edge-to-edge with their parent container. | `boolean` | - |

docs/4.0/api/CAccordionButton.api.mdx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
| Property | Description | Type | Default |
22
| --- | --- | --- | --- |
33
| **className** | A string of all className you want applied to the base component. | `string` | - |
4-
| **collapsed** | Set button state to collapsed. | `boolean` | - |

docs/4.0/api/CAccordionItem.api.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
| Property | Description | Type | Default |
22
| --- | --- | --- | --- |
33
| **className** | A string of all className you want applied to the base component. | `string` | - |
4+
| **itemKey** | Item key. | `string | number` | - |

docs/4.0/components/accordion.mdx

Lines changed: 239 additions & 213 deletions
Large diffs are not rendered by default.

src/components/accordion/CAccordion.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
import React, { forwardRef, HTMLAttributes } from 'react'
1+
import React, { createContext, forwardRef, HTMLAttributes, useState } from 'react'
22
import PropTypes from 'prop-types'
33
import classNames from 'classnames'
44

55
export interface CAccordionProps extends HTMLAttributes<HTMLDivElement> {
6+
/**
7+
* The active item key. [docs]
8+
*/
9+
activeItemKey?: number | string
10+
/**
11+
* Make accordion items stay open when another item is opened
12+
*/
13+
alwaysOpen?: boolean
614
/**
715
* A string of all className you want applied to the base component. [docs]
816
*/
@@ -13,18 +21,31 @@ export interface CAccordionProps extends HTMLAttributes<HTMLDivElement> {
1321
flush?: boolean
1422
}
1523

24+
export interface CAccordionContextProps {
25+
_activeItemKey?: number | string
26+
alwaysOpen?: boolean
27+
setActiveKey: React.Dispatch<React.SetStateAction<number | string | undefined>>
28+
}
29+
30+
export const CAccordionContext = createContext({} as CAccordionContextProps)
31+
1632
export const CAccordion = forwardRef<HTMLDivElement, CAccordionProps>(
17-
({ children, className, flush, ...rest }, ref) => {
33+
({ children, activeItemKey = undefined, alwaysOpen = false, className, flush, ...rest }, ref) => {
34+
const [_activeItemKey, setActiveKey] = useState(activeItemKey)
1835
const _className = classNames('accordion', { 'accordion-flush': flush }, className)
1936
return (
2037
<div className={_className} {...rest} ref={ref}>
21-
{children}
38+
<CAccordionContext.Provider value={{ _activeItemKey, alwaysOpen, setActiveKey }}>
39+
{children}
40+
</CAccordionContext.Provider>
2241
</div>
2342
)
2443
},
2544
)
2645

2746
CAccordion.propTypes = {
47+
alwaysOpen: PropTypes.bool,
48+
activeItemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
2849
children: PropTypes.node,
2950
className: PropTypes.string,
3051
flush: PropTypes.bool,

src/components/accordion/CAccordionBody.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import React, { forwardRef, HTMLAttributes } from 'react'
1+
import React, { forwardRef, HTMLAttributes, useContext } from 'react'
22
import PropTypes from 'prop-types'
33
import classNames from 'classnames'
44

5+
import { CAccordionItemContext } from './CAccordionItem'
6+
7+
import { CCollapse } from './../collapse/CCollapse'
58
export interface CAccordionBodyProps extends HTMLAttributes<HTMLDivElement> {
69
/**
710
* A string of all className you want applied to the base component. [docs]
@@ -11,12 +14,15 @@ export interface CAccordionBodyProps extends HTMLAttributes<HTMLDivElement> {
1114

1215
export const CAccordionBody = forwardRef<HTMLDivElement, CAccordionBodyProps>(
1316
({ children, className, ...rest }, ref) => {
17+
const { visible } = useContext(CAccordionItemContext)
1418
const _className = classNames('accordion-body', className)
1519

1620
return (
17-
<div className={_className} {...rest} ref={ref}>
18-
{children}
19-
</div>
21+
<CCollapse className="accordion-collpase" visible={visible}>
22+
<div className={_className} {...rest} ref={ref}>
23+
{children}
24+
</div>
25+
</CCollapse>
2026
)
2127
},
2228
)
Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
import React, { forwardRef, HTMLAttributes } from 'react'
1+
import React, { forwardRef, HTMLAttributes, useContext } from 'react'
22
import PropTypes from 'prop-types'
33
import classNames from 'classnames'
44

5+
import { CAccordionItemContext } from './CAccordionItem'
6+
57
export interface CAccordionButtonProps extends HTMLAttributes<HTMLButtonElement> {
68
/**
79
* A string of all className you want applied to the base component. [docs]
810
*/
911
className?: string
10-
/**
11-
* Set button state to collapsed. [docs]
12-
*/
13-
collapsed?: boolean
1412
}
1513

1614
export const CAccordionButton = forwardRef<HTMLButtonElement, CAccordionButtonProps>(
17-
({ children, className, collapsed, ...rest }, ref) => {
18-
const _className = classNames('accordion-button', { collapsed: collapsed }, className)
15+
({ children, className, ...rest }, ref) => {
16+
const { visible, setVisible } = useContext(CAccordionItemContext)
17+
18+
const _className = classNames('accordion-button', { collapsed: !visible }, className)
1919

2020
return (
21-
<button className={_className} {...rest} aria-expanded={!collapsed} ref={ref}>
21+
<button
22+
className={_className}
23+
{...rest}
24+
aria-expanded={!visible}
25+
onClick={() => setVisible(!visible)}
26+
ref={ref}
27+
>
2228
{children}
2329
</button>
2430
)
@@ -28,7 +34,6 @@ export const CAccordionButton = forwardRef<HTMLButtonElement, CAccordionButtonPr
2834
CAccordionButton.propTypes = {
2935
children: PropTypes.node,
3036
className: PropTypes.string,
31-
collapsed: PropTypes.bool,
3237
}
3338

3439
CAccordionButton.displayName = 'CAccordionButton'

src/components/accordion/CAccordionHeader.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, { forwardRef, HTMLAttributes } from 'react'
22
import PropTypes from 'prop-types'
33
import classNames from 'classnames'
44

5+
import { CAccordionButton } from './CAccordionButton'
6+
57
export interface CAccordionHeaderProps extends HTMLAttributes<HTMLDivElement> {
68
/**
79
* A string of all className you want applied to the base component. [docs]
@@ -15,7 +17,7 @@ export const CAccordionHeader = forwardRef<HTMLDivElement, CAccordionHeaderProps
1517

1618
return (
1719
<div className={_className} {...rest} ref={ref}>
18-
{children}
20+
<CAccordionButton>{children}</CAccordionButton>
1921
</div>
2022
)
2123
},
Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,57 @@
1-
import React, { forwardRef, HTMLAttributes } from 'react'
1+
import React, {
2+
createContext,
3+
forwardRef,
4+
HTMLAttributes,
5+
useContext,
6+
useEffect,
7+
useRef,
8+
useState,
9+
} from 'react'
210
import PropTypes from 'prop-types'
311
import classNames from 'classnames'
412

13+
import { CAccordionContext } from './CAccordion'
14+
15+
export interface CAccordionItemContextProps {
16+
setVisible: (a: boolean) => void
17+
visible?: boolean
18+
}
19+
20+
export const CAccordionItemContext = createContext({} as CAccordionItemContextProps)
21+
522
export interface CAccordionItemProps extends HTMLAttributes<HTMLDivElement> {
623
/**
724
* A string of all className you want applied to the base component. [docs]
825
*/
926
className?: string
27+
/**
28+
* Item key. [docs]
29+
*/
30+
itemKey?: number | string
1031
}
1132

1233
export const CAccordionItem = forwardRef<HTMLDivElement, CAccordionItemProps>(
13-
({ children, className, ...rest }, ref) => {
34+
({ children, className, itemKey, ...rest }, ref) => {
35+
const _itemKey = useRef(itemKey ? itemKey : Math.random().toString(36).substr(2, 9))
36+
37+
const { _activeItemKey, alwaysOpen, setActiveKey } = useContext(CAccordionContext)
38+
const [visible, setVisible] = useState(Boolean(_activeItemKey === _itemKey.current))
39+
40+
useEffect(() => {
41+
!alwaysOpen && visible && setActiveKey(_itemKey.current)
42+
}, [visible])
43+
44+
useEffect(() => {
45+
setVisible(Boolean(_activeItemKey === _itemKey.current))
46+
}, [_activeItemKey])
47+
1448
const _className = classNames('accordion-item', className)
1549

1650
return (
1751
<div className={_className} {...rest} ref={ref}>
18-
{children}
52+
<CAccordionItemContext.Provider value={{ setVisible, visible }}>
53+
{children}
54+
</CAccordionItemContext.Provider>
1955
</div>
2056
)
2157
},
@@ -24,6 +60,7 @@ export const CAccordionItem = forwardRef<HTMLDivElement, CAccordionItemProps>(
2460
CAccordionItem.propTypes = {
2561
children: PropTypes.node,
2662
className: PropTypes.string,
63+
itemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
2764
}
2865

2966
CAccordionItem.displayName = 'CAccordionItem'

0 commit comments

Comments
 (0)