Skip to content

Commit

Permalink
fix(name-rules): ignore when explicit roles don't require a name (#2629)
Browse files Browse the repository at this point in the history
* fix(name-rules): ignore when explicit roles don't require a name

* docs(standards): fix typo in ariaRole prop
  • Loading branch information
WilcoFiers committed Nov 9, 2020
1 parent e77992e commit 52fb138
Show file tree
Hide file tree
Showing 20 changed files with 201 additions and 25 deletions.
4 changes: 3 additions & 1 deletion doc/standards-object.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ The [`ariaRoles`](../lib/standards/aria-roles.js) object defines valid ARIA role
- `requiredOwned` - array(optional). List of required owned roles.
- `requiredAttrs` - array(optional). List of required attributes.
- `allowedAttrs` - array(optional). List of allowed attributes (besides any required and global ARIA attributes).
- `superclassRole` - array(optional). List of superclass roles.
- `accessibleNameRequired` - boolean(optional. Default `false`). If elements with this role require an accessible name.
- `nameFromContent` - boolean(optional. Default `false`). If the role allows name from content when calculating the accessible name.
- `unsupported` - boolean(optional. Default `false`). If the role role is unsupported. Use this property to disable a role.
- `unsupported` - boolean(optional. Default `false`). If the role is unsupported. Use this property to disable a role.

### Dpub Roles

Expand Down
2 changes: 2 additions & 0 deletions lib/core/base/metadata-function-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ import layoutTableMatches from '../../rules/layout-table-matches';
import linkInTextBlockMatches from '../../rules/link-in-text-block-matches';
import noAutoplayAudioMatches from '../../rules/no-autoplay-audio-matches';
import noEmptyRoleMatches from '../../rules/no-empty-role-matches';
import noExplicitNameRequiredMatches from '../../rules/no-explicit-name-required-matches';
import noNamingMethodMatches from '../../rules/no-naming-method-matches';
import noRoleMatches from '../../rules/no-role-matches';
import notHtmlMatches from '../../rules/not-html-matches';
Expand Down Expand Up @@ -321,6 +322,7 @@ const metadataFunctionMap = {
'link-in-text-block-matches': linkInTextBlockMatches,
'no-autoplay-audio-matches': noAutoplayAudioMatches,
'no-empty-role-matches': noEmptyRoleMatches,
'no-explicit-name-required-matches': noExplicitNameRequiredMatches,
'no-naming-method-matches': noNamingMethodMatches,
'no-role-matches': noRoleMatches,
'not-html-matches': notHtmlMatches,
Expand Down
1 change: 1 addition & 0 deletions lib/rules/button-name.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "button-name",
"selector": "button",
"matches": "no-explicit-name-required-matches",
"tags": [
"cat.name-role-value",
"wcag2a",
Expand Down
1 change: 1 addition & 0 deletions lib/rules/image-alt.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "image-alt",
"selector": "img",
"matches": "no-explicit-name-required-matches",
"tags": [
"cat.text-alternatives",
"wcag2a",
Expand Down
1 change: 1 addition & 0 deletions lib/rules/input-button-name.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "input-button-name",
"selector": "input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]",
"matches": "no-explicit-name-required-matches",
"tags": [
"cat.name-role-value",
"wcag2a",
Expand Down
1 change: 1 addition & 0 deletions lib/rules/input-image-alt.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "input-image-alt",
"selector": "input[type=\"image\"]",
"matches": "no-explicit-name-required-matches",
"tags": [
"cat.text-alternatives",
"wcag2a",
Expand Down
22 changes: 22 additions & 0 deletions lib/rules/no-explicit-name-required-matches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { isFocusable } from '../commons/dom';
import { getExplicitRole } from '../commons/aria';
import ariaRoles from '../standards/aria-roles';

/**
* Filter out elements with an explicit role that does not require an accessible name and is not focusable
*/
function noExplicitNameRequired(node, virtualNode) {
const role = getExplicitRole(virtualNode);
if (!role || ['none', 'presentation'].includes(role)) {
return true;
}

const { accessibleNameRequired } = ariaRoles[role] || {};
if (accessibleNameRequired || isFocusable(virtualNode)) {
return true;
}

return false;
}

export default noExplicitNameRequired;
1 change: 1 addition & 0 deletions lib/rules/object-alt.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "object-alt",
"selector": "object",
"matches": "no-explicit-name-required-matches",
"tags": [
"cat.text-alternatives",
"wcag2a",
Expand Down
81 changes: 62 additions & 19 deletions lib/standards/aria-roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ const ariaRoles = {
alertdialog: {
type: 'widget',
allowedAttrs: ['aria-expanded', 'aria-modal'],
superclassRole: ['alert', 'dialog']
superclassRole: ['alert', 'dialog'],
accessibleNameRequired: true
},
application: {
// Note: spec difference
type: 'landmark',
// Note: aria-expanded is not in the 1.1 spec but is
// consistently supported in ATs and was added in 1.2
allowedAttrs: ['aria-activedescendant', 'aria-expanded'],
superclassRole: ['structure']
superclassRole: ['structure'],
accessibleNameRequired: true
},
article: {
type: 'structure',
Expand All @@ -52,6 +54,7 @@ const ariaRoles = {
type: 'widget',
allowedAttrs: ['aria-expanded', 'aria-pressed'],
superclassRole: ['command'],
accessibleNameRequired: true,
nameFromContent: true
},
caption: {
Expand Down Expand Up @@ -82,6 +85,7 @@ const ariaRoles = {
// consistently supported in ATs and was added in 1.2
allowedAttrs: ['aria-checked', 'aria-readonly', 'aria-required'],
superclassRole: ['input'],
accessibleNameRequired: true,
nameFromContent: true
},
code: {
Expand All @@ -103,6 +107,8 @@ const ariaRoles = {
'aria-selected'
],
superclassRole: ['cell', 'gridcell', 'sectionhead'],
// Note: spec difference
accessibleNameRequired: false,
nameFromContent: true
},
combobox: {
Expand All @@ -120,7 +126,8 @@ const ariaRoles = {
'aria-activedescendant',
'aria-orientation'
],
superclassRole: ['select']
superclassRole: ['select'],
accessibleNameRequired: true
},
command: {
type: 'abstract',
Expand Down Expand Up @@ -152,7 +159,8 @@ const ariaRoles = {
dialog: {
type: 'widget',
allowedAttrs: ['aria-expanded', 'aria-modal'],
superclassRole: ['window']
superclassRole: ['window'],
accessibleNameRequired: true
},
directory: {
type: 'structure',
Expand Down Expand Up @@ -200,7 +208,9 @@ const ariaRoles = {
'aria-expanded',
'aria-rowcount'
],
superclassRole: ['composite', 'table']
superclassRole: ['composite', 'table'],
// Note: spec difference
accessibleNameRequired: false
},
gridcell: {
type: 'widget',
Expand Down Expand Up @@ -228,12 +238,15 @@ const ariaRoles = {
requiredAttrs: ['aria-level'],
allowedAttrs: ['aria-expanded'],
superclassRole: ['sectionhead'],
// Note: spec difference
accessibleNameRequired: false,
nameFromContent: true
},
img: {
type: 'structure',
allowedAttrs: ['aria-expanded'],
superclassRole: ['section']
superclassRole: ['section'],
accessibleNameRequired: true
},
input: {
type: 'abstract',
Expand All @@ -251,6 +264,7 @@ const ariaRoles = {
type: 'widget',
allowedAttrs: ['aria-expanded'],
superclassRole: ['command'],
accessibleNameRequired: true,
nameFromContent: true
},
list: {
Expand All @@ -270,7 +284,8 @@ const ariaRoles = {
'aria-expanded',
'aria-orientation'
],
superclassRole: ['select']
superclassRole: ['select'],
accessibleNameRequired: true
},
listitem: {
type: 'structure',
Expand Down Expand Up @@ -332,6 +347,7 @@ const ariaRoles = {
// consistently supported in ATs and was added in 1.2
allowedAttrs: ['aria-posinset', 'aria-setsize', 'aria-expanded'],
superclassRole: ['command'],
accessibleNameRequired: true,
nameFromContent: true
},
menuitemcheckbox: {
Expand All @@ -344,6 +360,7 @@ const ariaRoles = {
'aria-setsize'
],
superclassRole: ['checkbox', 'menuitem'],
accessibleNameRequired: true,
nameFromContent: true
},
menuitemradio: {
Expand All @@ -356,13 +373,15 @@ const ariaRoles = {
'aria-setsize'
],
superclassRole: ['menuitemcheckbox', 'radio'],
accessibleNameRequired: true,
nameFromContent: true
},
meter: {
type: 'structure',
allowedAttrs: ['aria-valuetext'],
requiredAttrs: ['aria-valuemax', 'aria-valuemin', 'aria-valuenow'],
superclassRole: ['range']
superclassRole: ['range'],
accessibleNameRequired: true
},
navigation: {
type: 'landmark',
Expand Down Expand Up @@ -391,6 +410,7 @@ const ariaRoles = {
'aria-setsize'
],
superclassRole: ['input'],
accessibleNameRequired: true,
nameFromContent: true
},
paragraph: {
Expand All @@ -410,7 +430,8 @@ const ariaRoles = {
'aria-valuenow',
'aria-valuetext'
],
superclassRole: ['range']
superclassRole: ['range'],
accessibleNameRequired: true
},
radio: {
type: 'widget',
Expand All @@ -427,6 +448,7 @@ const ariaRoles = {
'aria-required'
],
superclassRole: ['input'],
accessibleNameRequired: true,
nameFromContent: true
},
radiogroup: {
Expand All @@ -439,7 +461,9 @@ const ariaRoles = {
'aria-expanded',
'aria-orientation'
],
superclassRole: ['select']
superclassRole: ['select'],
// Note: spec difference
accessibleNameRequired: false
},
range: {
type: 'abstract',
Expand All @@ -448,7 +472,9 @@ const ariaRoles = {
region: {
type: 'landmark',
allowedAttrs: ['aria-expanded'],
superclassRole: ['landmark']
superclassRole: ['landmark'],
// Note: spec difference
accessibleNameRequired: false
},
roletype: {
type: 'abstract',
Expand Down Expand Up @@ -491,6 +517,8 @@ const ariaRoles = {
'aria-selected'
],
superclassRole: ['cell', 'gridcell', 'sectionhead'],
// Note: spec difference
accessibleNameRequired: false,
nameFromContent: true
},
scrollbar: {
Expand Down Expand Up @@ -527,7 +555,8 @@ const ariaRoles = {
'aria-readonly',
'aria-required'
],
superclassRole: ['textbox']
superclassRole: ['textbox'],
accessibleNameRequired: true
},
section: {
type: 'abstract',
Expand Down Expand Up @@ -573,7 +602,8 @@ const ariaRoles = {
'aria-readonly',
'aria-valuetext'
],
superclassRole: ['input', 'range']
superclassRole: ['input', 'range'],
accessibleNameRequired: true
},
spinbutton: {
type: 'widget',
Expand All @@ -589,7 +619,8 @@ const ariaRoles = {
'aria-activedescendant',
'aria-valuetext'
],
superclassRole: ['composite', 'input', 'range']
superclassRole: ['composite', 'input', 'range'],
accessibleNameRequired: true
},
status: {
type: 'widget',
Expand Down Expand Up @@ -617,6 +648,7 @@ const ariaRoles = {
requiredAttrs: ['aria-checked'],
allowedAttrs: ['aria-readonly'],
superclassRole: ['checkbox'],
accessibleNameRequired: true,
nameFromContent: true
},
tab: {
Expand All @@ -640,6 +672,8 @@ const ariaRoles = {
// table be named from content (we even had to special case
// table in commons/aria/named-from-contents)
superclassRole: ['section'],
// Note: spec difference
accessibleNameRequired: false,
nameFromContent: true
},
tablist: {
Expand All @@ -659,7 +693,9 @@ const ariaRoles = {
tabpanel: {
type: 'widget',
allowedAttrs: ['aria-expanded'],
superclassRole: ['section']
superclassRole: ['section'],
// Note: spec difference
accessibleNameRequired: false
},
term: {
type: 'structure',
Expand All @@ -678,7 +714,8 @@ const ariaRoles = {
'aria-readonly',
'aria-required'
],
superclassRole: ['input']
superclassRole: ['input'],
accessibleNameRequired: true
},
time: {
type: 'structure',
Expand All @@ -696,7 +733,8 @@ const ariaRoles = {
'aria-activedescendant',
'aria-expanded'
],
superclassRole: ['group']
superclassRole: ['group'],
accessibleNameRequired: true
},
tooltip: {
type: 'structure',
Expand All @@ -714,7 +752,9 @@ const ariaRoles = {
'aria-expanded',
'aria-orientation'
],
superclassRole: ['select']
superclassRole: ['select'],
// Note: spec difference
accessibleNameRequired: false
},
treegrid: {
type: 'composite',
Expand All @@ -730,7 +770,9 @@ const ariaRoles = {
'aria-required',
'aria-rowcount'
],
superclassRole: ['grid', 'tree']
superclassRole: ['grid', 'tree'],
// Note: spec difference
accessibleNameRequired: false
},
treeitem: {
type: 'widget',
Expand All @@ -744,6 +786,7 @@ const ariaRoles = {
'aria-setsize'
],
superclassRole: ['listitem', 'option'],
accessibleNameRequired: true,
nameFromContent: true
},
widget: {
Expand Down
Loading

0 comments on commit 52fb138

Please sign in to comment.