-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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,61 @@ | ||
# dialog-focus | ||
|
||
Move focus to inside dialog or set dialog after trigger. | ||
|
||
> When a open dialog, move focus to an element contained in the dialog. Or [Inserting dynamic content into the Document Object Model immediately following its trigger element](https://www.w3.org/WAI/WCAG21/Techniques/client-side-script/SCR26). | ||
[Understanding Success Criterion 2\.4\.3: Focus Order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html) | ||
|
||
## :white_check_mark: Correct | ||
|
||
```html | ||
<button type="button" aria-haspopup="dialog">open</button> | ||
|
||
<dialog> | ||
<button type="type">OK</button> | ||
</dialog> | ||
|
||
<script> | ||
const dialog = document.querySelector('dialog'); | ||
const openButton = document.querySelector('button[aria-haspopup="dialog"]'); | ||
openButton.addEventListener('click', (e) => { | ||
dialog.showModal(); | ||
}); | ||
</script> | ||
``` | ||
|
||
```html | ||
<button type="button" aria-haspopup="dialog">open</button> | ||
|
||
<div role="dialog" hidden> | ||
<button type="type">OK</button> | ||
</div> | ||
|
||
<script> | ||
const dialog = document.querySelector('[role=dialog]'); | ||
const openButton = document.querySelector('button[aria-haspopup="dialog"]'); | ||
openButton.addEventListener('click', (e) => { | ||
dialog.hidden = false; | ||
}); | ||
</script> | ||
``` | ||
|
||
## :warning: Incorrect | ||
|
||
```html | ||
<button type="button" aria-haspopup="dialog">open</button> | ||
|
||
<a href="https://example.com">link</a> | ||
|
||
<div role="dialog" hidden> | ||
<button type="type">OK</button> | ||
</div> | ||
|
||
<script> | ||
const dialog = document.querySelector('[role=dialog]'); | ||
const openButton = document.querySelector('button[aria-haspopup="dialog"]'); | ||
openButton.addEventListener('click', (e) => { | ||
dialog.hidden = false; | ||
}); | ||
</script> | ||
``` |
This file contains 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,69 @@ | ||
import { createRule } from '@acot/core'; | ||
import type { ComputedAccessibleNode } from '@acot/types'; | ||
|
||
type Options = {}; | ||
|
||
export default createRule<Options>({ | ||
type: 'contextual', | ||
selector: '[aria-haspopup="dialog"]', | ||
meta: { | ||
description: 'Move focus to inside dialog or set dialog after trigger.', | ||
tags: ['wcag2.1a', '2.4.3 Focus Order'], | ||
recommended: true, | ||
}, | ||
|
||
test: async (context, node) => { | ||
try { | ||
await node.click(); | ||
|
||
const activeElementHasParentDialogRole = async () => { | ||
if (document.activeElement === null) { | ||
return false; | ||
} | ||
|
||
const ax = (await (window as any).getComputedAccessibleNode( | ||
document.activeElement, | ||
)) as ComputedAccessibleNode; | ||
|
||
const findParentDialogRoleAXNode = ( | ||
computedAXNode: ComputedAccessibleNode, | ||
): ComputedAccessibleNode | null => { | ||
if (computedAXNode.role === 'dialog') { | ||
return computedAXNode; | ||
} | ||
|
||
return computedAXNode.parent != undefined | ||
? findParentDialogRoleAXNode(computedAXNode.parent) | ||
: null; | ||
}; | ||
|
||
return findParentDialogRoleAXNode(ax)?.role === 'dialog'; | ||
}; | ||
|
||
const hasDialogRoleInParentByClick = await context.page.evaluate( | ||
activeElementHasParentDialogRole, | ||
); | ||
|
||
context.debug({ hasDialogRoleInParentByClick }); | ||
|
||
if (!hasDialogRoleInParentByClick) { | ||
await context.page.keyboard.press('Tab'); | ||
|
||
const hasDialogRoleInParentByPressTab = await context.page.evaluate( | ||
activeElementHasParentDialogRole, | ||
); | ||
|
||
context.debug({ hasDialogRoleInParentByPressTab }); | ||
|
||
if (!hasDialogRoleInParentByPressTab) { | ||
await context.report({ | ||
node, | ||
message: `Move focus to inside dialog or set dialog after trigger.`, | ||
}); | ||
} | ||
} | ||
} catch (e) { | ||
context.debug('error: ', e); | ||
} | ||
}, | ||
}); |
This file contains 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