diff --git a/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.test.tsx b/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.test.tsx
new file mode 100644
index 0000000000..650542e3ff
--- /dev/null
+++ b/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.test.tsx
@@ -0,0 +1,107 @@
+import { userEvent } from '@storybook/test'
+import { render, screen } from '@testing-library/react'
+import React from 'react'
+import { config } from 'react-transition-group'
+
+import { Fieldset } from '../Fieldset'
+import { RadioButton } from '../RadioButton'
+
+import { AccordionPanel } from './AccordionPanel'
+import { AccordionPanelContent } from './AccordionPanelContent'
+import { AccordionPanelItem } from './AccordionPanelItem'
+import { AccordionPanelTrigger } from './AccordionPanelTrigger'
+
+describe('AccordionPanel', () => {
+ beforeAll(() => {
+ config.disabled = true
+ })
+
+ afterAll(() => {
+ config.disabled = false
+ })
+
+ test('アコーディオン内に配置したラジオボタンをキーボード操作できる', async () => {
+ render(
+
,
+ )
+
+ await userEvent.keyboard('[Tab]')
+ await userEvent.keyboard('[Space]')
+ expect(screen.getByRole('button', { name: 'アコーディオンパネル1' })).toHaveFocus()
+
+ await userEvent.keyboard('[Tab]')
+ await userEvent.keyboard('[Space]')
+ expect(screen.getByRole('radio', { name: 'ラジオボタン1-1' })).toHaveFocus()
+ expect(screen.getByRole('radio', { name: 'ラジオボタン1-1' })).toBeChecked()
+
+ await userEvent.keyboard('[ArrowRight]')
+ await userEvent.keyboard('[Space]')
+ expect(screen.getByRole('radio', { name: 'ラジオボタン1-2' })).toHaveFocus()
+ expect(screen.getByRole('radio', { name: 'ラジオボタン1-2' })).toBeChecked()
+
+ await userEvent.keyboard('[ArrowLeft]')
+ await userEvent.keyboard('[Space]')
+ expect(screen.getByRole('radio', { name: 'ラジオボタン1-1' })).toHaveFocus()
+ expect(screen.getByRole('radio', { name: 'ラジオボタン1-1' })).toBeChecked()
+ })
+
+ test('矢印キーでAccordionPanelItem間を移動できる', async () => {
+ render(
+ ,
+ )
+
+ await userEvent.keyboard('[Tab]')
+ await userEvent.keyboard('[Space]')
+ expect(screen.getByRole('button', { name: 'アコーディオンパネル1' })).toHaveFocus()
+
+ await userEvent.keyboard('[ArrowDown]')
+ expect(screen.getByRole('button', { name: 'アコーディオンパネル2' })).toHaveFocus()
+
+ await userEvent.keyboard('[ArrowUp]')
+ expect(screen.getByRole('button', { name: 'アコーディオンパネル1' })).toHaveFocus()
+ })
+})
diff --git a/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.tsx b/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.tsx
index ba68f3716b..ad90efeacc 100644
--- a/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.tsx
+++ b/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanel.tsx
@@ -13,14 +13,7 @@ import { tv } from 'tailwind-variants'
import { flatArrayToMap } from '../../libs/map'
-import {
- focusFirstSibling,
- focusLastSibling,
- focusNextSibling,
- focusPreviousSibling,
- getNewExpandedItems,
- keycodes,
-} from './accordionPanelHelper'
+import { getNewExpandedItems } from './accordionPanelHelper'
type Props = PropsWithChildren<{
/** アイコンの左右位置 */
@@ -71,40 +64,6 @@ export const AccordionPanel: React.FC = ({
[expandableMultiply, expandedItems],
)
- const handleKeyPress = (event: React.KeyboardEvent): void => {
- if (!parentRef?.current) {
- return
- }
-
- const keyCode = event.keyCode
- const item = event.target as HTMLElement
-
- switch (keyCode) {
- case keycodes.HOME: {
- event.preventDefault()
- focusFirstSibling(parentRef.current)
- break
- }
- case keycodes.END: {
- event.preventDefault()
- focusLastSibling(parentRef.current)
- break
- }
- case keycodes.LEFT:
- case keycodes.UP: {
- event.preventDefault()
- focusPreviousSibling(item, parentRef.current)
- break
- }
- case keycodes.RIGHT:
- case keycodes.DOWN: {
- event.preventDefault()
- focusNextSibling(item, parentRef.current)
- break
- }
- }
- }
-
useEffect(() => {
if (defaultExpanded.length > 0) setExpanded(flatArrayToMap(defaultExpanded))
}, [defaultExpanded])
@@ -121,13 +80,7 @@ export const AccordionPanel: React.FC = ({
}}
>
{/* eslint-disable-next-line smarthr/a11y-delegate-element-has-role-presentation */}
-
+
)
}
diff --git a/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanelTrigger.tsx b/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanelTrigger.tsx
index b576a47598..59a1ad7724 100644
--- a/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanelTrigger.tsx
+++ b/packages/smarthr-ui/src/components/AccordionPanel/AccordionPanelTrigger.tsx
@@ -18,7 +18,13 @@ import { TextProps } from '../Text'
import { AccordionPanelContext } from './AccordionPanel'
import { AccordionPanelItemContext } from './AccordionPanelItem'
-import { getNewExpandedItems } from './accordionPanelHelper'
+import {
+ focusFirstSibling,
+ focusLastSibling,
+ focusNextSibling,
+ focusPreviousSibling,
+ getNewExpandedItems,
+} from './accordionPanelHelper'
type Props = PropsWithChildren<{
/** ヘッダ部分のテキストのスタイル */
@@ -70,8 +76,14 @@ export const AccordionPanelTrigger: FC = ({
}
}, [className])
const { name } = useContext(AccordionPanelItemContext)
- const { iconPosition, expandedItems, onClickTrigger, onClickProps, expandableMultiply } =
- useContext(AccordionPanelContext)
+ const {
+ iconPosition,
+ expandedItems,
+ onClickTrigger,
+ onClickProps,
+ expandableMultiply,
+ parentRef,
+ } = useContext(AccordionPanelContext)
const isExpanded = getIsInclude(expandedItems, name)
@@ -89,6 +101,42 @@ export const AccordionPanelTrigger: FC = ({
}
}, [onClickTrigger, name, isExpanded, onClickProps, expandedItems, expandableMultiply])
+ const handleKeyDown: React.KeyboardEventHandler = useCallback(
+ (event): void => {
+ if (!parentRef?.current) {
+ return
+ }
+
+ const item = event.target as HTMLElement
+
+ switch (event.key) {
+ case 'Home': {
+ event.preventDefault()
+ focusFirstSibling(parentRef.current)
+ break
+ }
+ case 'End': {
+ event.preventDefault()
+ focusLastSibling(parentRef.current)
+ break
+ }
+ case 'ArrowLeft':
+ case 'ArrowUp': {
+ event.preventDefault()
+ focusPreviousSibling(item, parentRef.current)
+ break
+ }
+ case 'ArrowRight':
+ case 'ArrowDown': {
+ event.preventDefault()
+ focusNextSibling(item, parentRef.current)
+ break
+ }
+ }
+ },
+ [parentRef],
+ )
+
return (
// eslint-disable-next-line smarthr/a11y-heading-in-sectioning-content
@@ -98,6 +146,7 @@ export const AccordionPanelTrigger: FC = ({
aria-expanded={isExpanded}
aria-controls={`${name}-content`}
onClick={handleClick}
+ onKeyDown={handleKeyDown}
className={buttonStyle}
data-component="AccordionHeaderButton"
type="button"
diff --git a/packages/smarthr-ui/src/components/AccordionPanel/accordionPanelHelper.ts b/packages/smarthr-ui/src/components/AccordionPanel/accordionPanelHelper.ts
index 6feb70c6b5..b90492a4a9 100644
--- a/packages/smarthr-ui/src/components/AccordionPanel/accordionPanelHelper.ts
+++ b/packages/smarthr-ui/src/components/AccordionPanel/accordionPanelHelper.ts
@@ -21,17 +21,6 @@ export const getNewExpandedItems = (
return newState
}
-export const keycodes = {
- SPACE: 32,
- ENTER: 13,
- HOME: 36,
- END: 35,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- LEFT: 37,
-}
-
export const getSiblingButtons = (parent: HTMLDivElement): HTMLElement[] =>
Array.from(parent.querySelectorAll('[data-component="AccordionHeaderButton"]'))