Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/dropdown-menu-header-field-arrows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@astryxdesign/core': minor
---

[breaking] In a `DropdownMenu` popover with a single-line text field in its `header`, ArrowDown in the field now moves to the menu's first row, and ArrowUp on that row then returns to the field, as between a combobox's input and its list; until a field has handed ArrowDown on, ArrowUp on the first row wraps as before. Before, the field kept ArrowDown, as the `header` docs said ("It keeps its own keys and presses"). A modified arrow, an IME composition, a combobox, a spinbutton such as `NumberInput`, an input with a `list`, and controls with arrows of their own (number, range, multi-line text) keep their keys, and so do the bottom sheet and a drilled-in sub-menu view.

To keep ArrowDown in a header field, prevent its default in the field's own `onKeyDown`: a field that handled the key keeps it. There is no codemod: whether a field relies on ArrowDown staying in it cannot be read from its source.

@vjeux
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,17 @@ jobs:
--project chromium \
--output hover-layer-touch-output

# A single-line text field in a DropdownMenu header hands ArrowDown on to
# the rows, and ArrowUp on the first row returns to it with what was
# typed intact. Real focus moves and the filtered rows only exist in a
# real engine.
- name: Run DropdownMenu header field arrows contract
run: |
pnpm exec playwright test \
packages/core/src/DropdownMenu/__tests__/MenuHeaderArrows.a11y.chromium.spec.ts \
--project chromium \
--output menu-header-arrows-output

# A pointer that crosses from a Tooltip's trigger onto the tooltip in one
# step must not lose it (WCAG 1.4.13 hoverable). The defect is in the
# order the engine dispatches mouseout and mouseleave on that step, which
Expand Down
8 changes: 5 additions & 3 deletions apps/storybook/stories/DropdownMenu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1494,9 +1494,11 @@ const LABELS = ['Bug', 'Feature', 'Documentation', 'Design', 'Performance'];
/**
* A filter field above the rows and a "Create" button below them. Both sit in
* the popover beside the menu, never inside it: the menu holds only its rows,
* so it stays a valid menu to a screen reader, and the field and button keep
* their own keys and presses. The popover is a modal dialog around the menu:
* Tab walks field, menu, button and wraps at either end; Escape closes.
* so it stays a valid menu to a screen reader. The button keeps its own keys
* and presses, and so does the field, but for one hand-off: ArrowDown in the
* field moves to the first row, and ArrowUp on that row comes back. The
* popover is a modal dialog around the menu: Tab walks field, menu, button and
* wraps at either end; Escape closes.
*/
export const HeaderAndFooter: Story = {
render: () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/DropdownMenu/DropdownMenu.doc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export const docs = {
name: 'header',
type: 'ReactNode',
description:
'Content above the rows and outside the menu itself — a filter field, a toggle. A menu holds only its rows, so a control that is not a row goes here or in `footer`, never among the rows. It keeps its own keys and presses. With a header or footer the popover is a modal dialog around the menu: Tab and Shift+Tab move through the header, the menu and the footer and wrap at either end, and Escape closes. The bottom sheet shows it above the actions.',
'Content above the rows and outside the menu itself — a filter field, a toggle. A menu holds only its rows, so a control that is not a row goes here or in `footer`, never among the rows. It keeps its own keys and presses, except that in the popover ArrowDown in a single-line text field there moves to the first row of the menu, and ArrowUp on that row then returns to the field. A field whose own `onKeyDown` prevents the default of ArrowDown keeps the key. With a header or footer the popover is a modal dialog around the menu: Tab and Shift+Tab move through the header, the menu and the footer and wrap at either end, and Escape closes. The bottom sheet shows it above the actions.',
},
{
name: 'footer',
Expand Down
63 changes: 53 additions & 10 deletions packages/core/src/DropdownMenu/DropdownMenu.spec.md

Large diffs are not rendered by default.

Loading
Loading