From efa993256c011741c85fbf9c4f7f4c287461663c Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Mon, 7 Sep 2026 12:37:23 +0200 Subject: [PATCH] fix(ActionSheet): prevent Tab from escaping the popover & deprecate in favor of `Menu` --- .../components/ActionSheet/ActionSheet.mdx | 6 +++++- .../ActionSheet/ActionSheet.stories.tsx | 7 ++++++- .../main/src/components/ActionSheet/index.tsx | 5 +++++ .../ActionSheet/test/ActionSheet.gallery.tsx | 17 +++++++++++++++ .../ActionSheet/test/ActionSheet.spec.tsx | 21 +++++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 packages/main/src/components/ActionSheet/test/ActionSheet.gallery.tsx create mode 100644 packages/main/src/components/ActionSheet/test/ActionSheet.spec.tsx diff --git a/packages/main/src/components/ActionSheet/ActionSheet.mdx b/packages/main/src/components/ActionSheet/ActionSheet.mdx index a414e68c0e8..4ccb8418418 100644 --- a/packages/main/src/components/ActionSheet/ActionSheet.mdx +++ b/packages/main/src/components/ActionSheet/ActionSheet.mdx @@ -5,7 +5,11 @@ import * as ComponentStories from './ActionSheet.stories'; - +
diff --git a/packages/main/src/components/ActionSheet/ActionSheet.stories.tsx b/packages/main/src/components/ActionSheet/ActionSheet.stories.tsx index e2a9885d326..074110e5c10 100644 --- a/packages/main/src/components/ActionSheet/ActionSheet.stories.tsx +++ b/packages/main/src/components/ActionSheet/ActionSheet.stories.tsx @@ -36,7 +36,12 @@ const meta = { parameters: { chromatic: { disableSnapshot: true }, }, - tags: ['extends:@ui5/webcomponents', 'cem-module:ResponsivePopover', 'package:@ui5/webcomponents-react'], + tags: [ + 'extends:@ui5/webcomponents', + 'cem-module:ResponsivePopover', + 'package:@ui5/webcomponents-react', + 'deprecated', + ], } satisfies Meta; export default meta; diff --git a/packages/main/src/components/ActionSheet/index.tsx b/packages/main/src/components/ActionSheet/index.tsx index fc21641abb8..ad8cdd0d2d7 100644 --- a/packages/main/src/components/ActionSheet/index.tsx +++ b/packages/main/src/components/ActionSheet/index.tsx @@ -115,6 +115,7 @@ function ActionSheetButton(props: ActionSheetButtonPropTypes) { * - Always provide a Cancel button on mobile phones. * - Avoid scrolling on action sheets. * + * @deprecated This component has a number of limitations. Use a `Menu` instead of the `ActionSheet` whenever possible. */ const ActionSheet = forwardRef((props, ref) => { const { accessibilityAttributes, children, className, header, headerText, hideCancelButton, onOpen, open, ...rest } = @@ -204,6 +205,10 @@ const ActionSheet = forwardRef((p .querySelector(`[data-action-btn-index="${Math.min(currentIndex + 5, childrenLength - 1)}"]`) .focus(); break; + case 'Tab': + // prevent focus from escaping popover + e.preventDefault(); + break; case 'Home': e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="0"]`).focus(); diff --git a/packages/main/src/components/ActionSheet/test/ActionSheet.gallery.tsx b/packages/main/src/components/ActionSheet/test/ActionSheet.gallery.tsx new file mode 100644 index 00000000000..b09361b81d5 --- /dev/null +++ b/packages/main/src/components/ActionSheet/test/ActionSheet.gallery.tsx @@ -0,0 +1,17 @@ +import { Button } from '../../../webComponents/Button/index.js'; +import { ActionSheet } from '../index.js'; + +export const ActionSheetKeyboardTestComp = () => { + return ( + <> + + + {new Array(5).fill('').map((_, index) => ( + + ))} + + + ); +}; diff --git a/packages/main/src/components/ActionSheet/test/ActionSheet.spec.tsx b/packages/main/src/components/ActionSheet/test/ActionSheet.spec.tsx new file mode 100644 index 00000000000..a75e26036ab --- /dev/null +++ b/packages/main/src/components/ActionSheet/test/ActionSheet.spec.tsx @@ -0,0 +1,21 @@ +import { expect, test } from '../../../../../../playwright/fixtures/gallery-fixtures.js'; + +test.describe('ActionSheet', () => { + test('Tab does not move focus or escape the popover', async ({ mount, page }) => { + await mount('ActionSheet/ActionSheetKeyboardTestComp'); + await expect(page.locator('[ui5-responsive-popover]')).toBeVisible(); + + const btn = (index: number) => page.locator(`[data-action-btn-index="${index}"]`); + + await expect(btn(0)).toBeFocused(); + + await page.keyboard.press('ArrowDown'); + await expect(btn(1)).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(btn(1)).toBeFocused(); + + await page.keyboard.press('Shift+Tab'); + await expect(btn(1)).toBeFocused(); + }); +});