Skip to content

Commit c5f45cc

Browse files
committed
Adjusts keydown functionality to use React API
1 parent 93b94d8 commit c5f45cc

File tree

2 files changed

+20
-24
lines changed

2 files changed

+20
-24
lines changed

source/03-components/Accordion/Accordion.tsx

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import clsx from 'clsx';
22
import { GessoComponent } from 'gesso';
3-
import { useEffect, useId, useRef, useState } from 'react';
3+
import { KeyboardEvent, createRef, useId, useMemo, useState } from 'react';
44
import styles from './accordion.module.css';
5-
import stylesAccordionItem from './accordion-item.module.css';
65
import getCssVar from '../../06-utility/getCssVar';
76
import { KEYCODE } from '../../00-config/constants';
87
import AccordionItem, { AccordionItemProps } from './AccordionItem';
@@ -22,13 +21,17 @@ function Accordion({
2221
modifierClasses,
2322
}: AccordionProps): JSX.Element {
2423
const accordionId = useId();
25-
const accordionRef = useRef(null);
2624
const [accordionItemsStatus, setAccordionItemsStatus] = useState(
2725
accordionItems.map((item, index) => ({
2826
...item,
2927
id: `${accordionId}-${index}`,
3028
})),
3129
);
30+
const accordionItemRefs = useMemo(() => {
31+
const refs: { [key: string]: React.RefObject<HTMLButtonElement> } = {};
32+
accordionItemsStatus.forEach(item => (refs[item.id] = createRef()));
33+
return refs;
34+
}, [accordionItemsStatus]);
3235

3336
const openAccordionItem = (items: AccordionItemProps[], index: number) => {
3437
return items.with(index, {
@@ -65,23 +68,19 @@ function Accordion({
6568
};
6669

6770
const handleKeydown = (event: KeyboardEvent) => {
68-
const currentTarget = event.target as HTMLElement;
69-
const accordion = accordionRef.current as HTMLElement | null;
70-
const ACCORDION_TOGGLE_CLASS = stylesAccordionItem.toggle;
71+
const currentTarget = event.target as HTMLButtonElement;
7172

7273
// Create the array of toggle elements for the accordion group
73-
const triggers = Array.prototype.slice.call(
74-
accordion ? accordion.querySelectorAll(`.${ACCORDION_TOGGLE_CLASS}`) : [],
75-
);
74+
const triggers = Object.values(accordionItemRefs).map(ref => ref.current);
7675

7776
// Is this coming from an accordion header?
78-
if (currentTarget.tagName === 'BUTTON') {
77+
if (triggers && currentTarget.tagName === 'BUTTON') {
7978
// Up/ Down arrow and Control + Page Up/ Page Down keyboard operations
8079
if (
8180
event.code === KEYCODE.UP ||
8281
event.code === KEYCODE.DOWN ||
83-
event.code === KEYCODE.PAGEDOWN ||
84-
event.code === KEYCODE.UP
82+
event.code === KEYCODE.PAGEUP ||
83+
event.code === KEYCODE.PAGEDOWN
8584
) {
8685
const index = triggers.indexOf(currentTarget);
8786
let direction;
@@ -92,40 +91,33 @@ function Accordion({
9291
}
9392
const triggerLength = triggers.length;
9493
const newIndex = (index + triggerLength + direction) % triggerLength;
95-
triggers[newIndex].focus();
94+
triggers[newIndex]?.focus();
9695
event.preventDefault();
9796
} else if (event.code === KEYCODE.HOME || event.code === KEYCODE.END) {
9897
switch (event.code) {
9998
// Go to first accordion
10099
case KEYCODE.HOME:
101-
triggers[0].focus();
100+
triggers[0]?.focus();
102101
break;
103102
// Go to last accordion
104103
case KEYCODE.END:
105-
triggers[triggers.length - 1].focus();
104+
triggers[triggers.length - 1]?.focus();
106105
break;
107106
default:
108-
triggers[0].focus();
107+
triggers[0]?.focus();
109108
break;
110109
}
111110
event.preventDefault();
112111
}
113112
}
114113
};
115114

116-
useEffect(() => {
117-
const accordion = accordionRef.current as HTMLElement | null;
118-
if (accordion) {
119-
accordion.addEventListener('keydown', handleKeydown);
120-
}
121-
});
122-
123115
return (
124116
<>
125117
<div
126-
ref={accordionRef}
127118
className={clsx(styles.accordion, modifierClasses)}
128119
id={accordionId}
120+
onKeyDown={handleKeydown}
129121
>
130122
<div className={styles.content}>
131123
{accordionItemsStatus.map(item => {
@@ -134,6 +126,7 @@ function Accordion({
134126
key={item.id}
135127
{...item}
136128
accordionSpeed={accordionSpeed}
129+
toggleRef={accordionItemRefs[item.id]}
137130
handleClick={() => handleClick(item.id, item.isOpen)}
138131
/>
139132
);

source/03-components/Accordion/AccordionItem.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface AccordionItemProps extends GessoComponent {
1111
titleElement?: ElementType;
1212
isOpen?: boolean;
1313
accordionSpeed?: string;
14+
toggleRef?: React.RefObject<HTMLButtonElement>;
1415
handleClick: MouseEventHandler;
1516
}
1617

@@ -21,6 +22,7 @@ function AccordionItem({
2122
titleElement: TitleElement = 'h3',
2223
isOpen,
2324
accordionSpeed,
25+
toggleRef,
2426
modifierClasses,
2527
handleClick,
2628
}: AccordionItemProps): JSX.Element {
@@ -52,6 +54,7 @@ function AccordionItem({
5254
id={buttonId}
5355
aria-expanded={isOpen}
5456
aria-controls={sectionId}
57+
ref={toggleRef}
5558
onClick={handleClick}
5659
>
5760
{title}

0 commit comments

Comments
 (0)