Repository navigation
test(a11y): Menu Button contract and DropdownMenu/MoreMenu trigger binding #7469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
5fa1e04
test(a11y): Menu Button contract and DropdownMenu/MoreMenu trigger bi…
cixzhang 43023d1
test(a11y): menu-button contract honors the press model, keeps the op…
cixzhang 0ec7adf
test(a11y): the menu-button Chromium lane tolerates the one expectati…
cixzhang 6733496
test(a11y): menu-button Chromium lane blocks on required rows only; a…
cixzhang 08c3336
Merge remote-tracking branch 'origin/main' into a11y-menu-button-cont…
cixzhang 673b45d
test(a11y): menu-button story ids match the A11y/MenuButton title; CI…
cixzhang 5a11b26
Merge remote-tracking branch 'origin/main' into a11y-menu-button-cont…
cixzhang ab6b654
test(a11y): run the aborted-press check on every menu-button state an…
cixzhang 42c924f
test(a11y): name the header/footer popover exclusion and mark the arr…
cixzhang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
|
||
| /** | ||
| * @file MenuButtonA11y.stories.tsx | ||
| * @input The DropdownMenu and MoreMenu menu-button binding states | ||
| * @output One checked-in story per state for the Chromium accessibility lane; excluded from the visual matrix | ||
| * @position Browser fixtures for component:DropdownMenu AR4 and component:MoreMenu AR1 (MENU_BUTTON_PATTERN) | ||
| */ | ||
|
|
||
| import type {Meta, StoryObj} from '@storybook/react'; | ||
| import {DropdownMenu} from '@astryxdesign/core/DropdownMenu'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| import {MoreMenu} from '@astryxdesign/core/MoreMenu'; | ||
|
|
||
| const meta = { | ||
| title: 'A11y/Menu Button', | ||
| tags: ['no-visual'], | ||
| parameters: {layout: 'padded'}, | ||
| } satisfies Meta; | ||
|
|
||
| export default meta; | ||
| type Story = StoryObj<typeof meta>; | ||
|
|
||
| const items = [ | ||
| {label: 'Rename'}, | ||
| {label: 'Duplicate'}, | ||
| {label: 'Archive', isDisabled: true}, | ||
| {label: 'Delete'}, | ||
| ]; | ||
|
|
||
| function After() { | ||
| return <button type="button">After the menu</button>; | ||
| } | ||
|
|
||
| export const DropdownButtonClosed: Story = { | ||
| render: () => ( | ||
| <div> | ||
| <DropdownMenu | ||
| button={{label: 'Actions'}} | ||
| items={items} | ||
| presentation="popover" | ||
| /> | ||
| <After /> | ||
| </div> | ||
| ), | ||
| }; | ||
|
|
||
| export const DropdownButtonOpen: Story = { | ||
| render: () => ( | ||
| <div> | ||
| <DropdownMenu | ||
| button={{label: 'Actions'}} | ||
| items={items} | ||
| presentation="popover" | ||
| isMenuOpen | ||
| onOpenChange={() => {}} | ||
| /> | ||
| <After /> | ||
| </div> | ||
| ), | ||
| }; | ||
|
|
||
| export const DropdownRenderTriggerClosed: Story = { | ||
| render: () => ( | ||
| <div> | ||
| <DropdownMenu | ||
| items={items} | ||
| presentation="popover" | ||
| renderTrigger={({ref, ...props}) => ( | ||
| <button type="button" ref={ref} {...props}> | ||
| Row actions | ||
| </button> | ||
| )} | ||
| /> | ||
| <After /> | ||
| </div> | ||
| ), | ||
| }; | ||
|
|
||
| export const DropdownButtonDisabled: Story = { | ||
| render: () => ( | ||
| <div> | ||
| <DropdownMenu | ||
| button={{label: 'Actions', isDisabled: true}} | ||
| items={items} | ||
| presentation="popover" | ||
| /> | ||
| <After /> | ||
| </div> | ||
| ), | ||
| }; | ||
|
|
||
| export const MoreMenuClosed: Story = { | ||
| render: () => ( | ||
| <div> | ||
| <MoreMenu label="More options" items={items} presentation="popover" /> | ||
| <After /> | ||
| </div> | ||
| ), | ||
| }; | ||
|
|
||
| export const MoreMenuDisabled: Story = { | ||
| render: () => ( | ||
| <div> | ||
| <MoreMenu | ||
| label="More options" | ||
| items={items} | ||
| presentation="popover" | ||
| isDisabled | ||
| /> | ||
| <After /> | ||
| </div> | ||
| ), | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
internal/a11y-spec/src/patterns/menu-button.chromium.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
|
||
| /** | ||
| * @file menu-button.chromium.spec.ts | ||
| * @input MENU_BUTTON_PATTERN and its fixtures in real Chromium | ||
| * @output Proof that every expectation passes conforming fixtures and fails its mutation at the layers only a browser observes | ||
| * @position Contract self-test, Chromium lane | ||
| */ | ||
|
|
||
| import {expect, test, type CDPSession, type Page} from '@playwright/test'; | ||
| import {describeExpectation, requiredLayers} from '../contract'; | ||
| import {checkAccessibilitySpec, type ExpectationResult} from '../check'; | ||
| import { | ||
| CHROMIUM_OBSERVES, | ||
| createChromiumHarness, | ||
| holdMotionStill, | ||
| } from '../harness/chromium'; | ||
| import {MENU_BUTTON_PATTERN} from './menu-button'; | ||
| import { | ||
| MENU_BUTTON_MUTATIONS, | ||
| CONFORMING_FIXTURES, | ||
| FIRST_ITEM_SELECTOR, | ||
| LAST_ITEM_SELECTOR, | ||
| MENU_SELECTOR, | ||
| SUBJECT_SELECTOR, | ||
| fixture, | ||
| type MenuButtonFixture, | ||
| } from './menu-button.fixtures'; | ||
|
|
||
| function fixturePage(html: string): string { | ||
| return `<!doctype html><html lang="en"><body>${html}<button type="button" id="after">After menu</button></body></html>`; | ||
| } | ||
|
|
||
| async function results( | ||
| page: Page, | ||
| cdp: CDPSession, | ||
| target: MenuButtonFixture, | ||
| only?: readonly string[], | ||
| ): Promise<readonly ExpectationResult[]> { | ||
| const run = await checkAccessibilitySpec({ | ||
| spec: MENU_BUTTON_PATTERN, | ||
| binding: 'fixture', | ||
| state: target.id, | ||
| facts: target.facts, | ||
| only, | ||
| mount: async () => { | ||
| await page.setContent(fixturePage(target.html)); | ||
| await holdMotionStill(page); | ||
| return createChromiumHarness({ | ||
| page, | ||
| cdp, | ||
| subject: page.locator(SUBJECT_SELECTOR), | ||
| related: { | ||
| menu: page.locator(MENU_SELECTOR), | ||
| 'first-item': page.locator(FIRST_ITEM_SELECTOR), | ||
| 'last-item': page.locator(LAST_ITEM_SELECTOR), | ||
| }, | ||
| }); | ||
| }, | ||
| }); | ||
| return run.results; | ||
| } | ||
|
|
||
| test.describe('menu-button contract — conforming fixtures', () => { | ||
| for (const id of CONFORMING_FIXTURES) { | ||
| test(`${id}: every applicable expectation passes`, async ({page}) => { | ||
| const cdp = await page.context().newCDPSession(page); | ||
| const observed = await results(page, cdp, fixture(id)); | ||
| expect( | ||
| observed | ||
| .filter( | ||
| result => | ||
| result.status !== 'pass' && result.status !== 'not-applicable', | ||
| ) | ||
| .map(result => `${result.expectation}: ${result.detail ?? ''}`), | ||
| ).toEqual([]); | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| test.describe('menu-button contract — deliberately violating fixtures', () => { | ||
| for (const expectation of MENU_BUTTON_PATTERN.expectations) { | ||
| if ( | ||
| !requiredLayers(expectation).every(layer => | ||
| CHROMIUM_OBSERVES.includes(layer), | ||
| ) | ||
| ) { | ||
| continue; | ||
| } | ||
| for (const fixtureId of MENU_BUTTON_MUTATIONS[expectation.id] ?? []) { | ||
| test(`${describeExpectation(expectation)} — fails against ${fixtureId}`, async ({ | ||
| page, | ||
| }) => { | ||
| const cdp = await page.context().newCDPSession(page); | ||
| const [result] = await results(page, cdp, fixture(fixtureId), [ | ||
| expectation.id, | ||
| ]); | ||
| expect(result?.status, result?.detail ?? 'no result').toBe('fail'); | ||
| expect(result?.detail ?? '').not.toBe(''); | ||
| }); | ||
| } | ||
| } | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mainreplaced this two-dot scope check in every PR evidence step with.github/scripts/pr-scope-touches.sh, loaded fromBASE_SHA(#7470): on a branch cut before the base gained a file, the base-tip diff lists that file as changed. Once this lands, every older branch would run this step against spec files its head lacks and failpr-a11ywith “No tests found” until rebased. After rebasing, reuse the Link step's helper block with this pattern.