From 69f778b71b4c78f89334dcb0c75bedc2005609f5 Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 22 Apr 2024 11:11:12 -0500 Subject: [PATCH 01/14] fix(aria-roles): correct abstract roles (types) for aria-roles Too many aria roles were set to widget or otherwise were incorrect, added a comment source for where I got my information for which abstract roles were the type of which roles Refs: #4371 --- lib/standards/aria-roles.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/standards/aria-roles.js b/lib/standards/aria-roles.js index 340fdb9b14..0359909671 100644 --- a/lib/standards/aria-roles.js +++ b/lib/standards/aria-roles.js @@ -1,4 +1,5 @@ // Source: https://www.w3.org/TR/wai-aria-1.1/#roles +// Source for abstract roles: https://www.w3.org/TR/wai-aria/#abstract_roles and https://www.w3.org/WAI/ARIA/1.2/class-diagram/rdf_model.svg /* easiest way to see allowed roles is to filter out the global ones from the list of inherited states and properties. The dpub spec @@ -17,13 +18,13 @@ */ const ariaRoles = { alert: { - type: 'widget', + type: 'structure', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded'], superclassRole: ['section'] }, alertdialog: { - type: 'widget', + type: 'window', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded', 'aria-modal'], superclassRole: ['alert', 'dialog'], @@ -31,7 +32,7 @@ const ariaRoles = { }, application: { // Note: spec difference - type: 'landmark', + type: 'structure', // 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'], @@ -119,7 +120,7 @@ const ariaRoles = { nameFromContent: true }, combobox: { - type: 'widget', + type: 'composite', requiredAttrs: ['aria-expanded', 'aria-controls'], allowedAttrs: [ 'aria-owns', @@ -169,7 +170,7 @@ const ariaRoles = { prohibitedAttrs: ['aria-label', 'aria-labelledby'] }, dialog: { - type: 'widget', + type: 'window', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded', 'aria-modal'], superclassRole: ['window'], @@ -301,7 +302,7 @@ const ariaRoles = { superclassRole: ['section'] }, listbox: { - type: 'widget', + type: 'composite', requiredOwned: ['group', 'option'], allowedAttrs: [ 'aria-multiselectable', @@ -328,7 +329,7 @@ const ariaRoles = { nameFromContent: true }, log: { - type: 'widget', + type: 'structure', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded'], superclassRole: ['section'] @@ -340,7 +341,7 @@ const ariaRoles = { superclassRole: ['landmark'] }, marquee: { - type: 'widget', + type: 'structure', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded'], superclassRole: ['section'] @@ -696,7 +697,7 @@ const ariaRoles = { accessibleNameRequired: true }, status: { - type: 'widget', + type: 'structure', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded'], superclassRole: ['section'] @@ -816,7 +817,7 @@ const ariaRoles = { superclassRole: ['section'] }, timer: { - type: 'widget', + type: 'structure', // Spec difference: Aria-expanded removed in 1.2 allowedAttrs: ['aria-expanded'], superclassRole: ['status'] From ffe37f6f3dfe58f945718581dcdc5c2aef74f498 Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 22 Apr 2024 12:48:42 -0500 Subject: [PATCH 02/14] fix(aria-roles): table with role application is structure Previously there was a test to confirm a table with role of application was a data table, but the spec indicates that has an anstract role of structure meaning this should be a structure table instead This commit is just a test correction Refs: #4421 --- test/commons/table/is-data-table.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/commons/table/is-data-table.js b/test/commons/table/is-data-table.js index c3607faf0b..d78c25e7bb 100644 --- a/test/commons/table/is-data-table.js +++ b/test/commons/table/is-data-table.js @@ -32,6 +32,14 @@ describe('table.isDataTable', function () { assert.isFalse(axe.commons.table.isDataTable(node)); }); + it('should be false if the table has role=application, which is an astract role of structure', function () { + fixture.innerHTML = '
'; + + var node = fixture.querySelector('table'); + axe.testUtils.flatTreeSetup(fixture.firstChild); + assert.isFalse(axe.commons.table.isDataTable(node)); + }); + it('should be true if the table is inside an editable area', function () { fixture.innerHTML = '
' + @@ -71,13 +79,6 @@ describe('table.isDataTable', function () { }); describe('should be true if the table has a landmark role', function () { - it('application', function () { - fixture.innerHTML = '
'; - - var node = fixture.querySelector('table'); - axe.testUtils.flatTreeSetup(fixture.firstChild); - assert.isTrue(axe.commons.table.isDataTable(node)); - }); it('banner', function () { fixture.innerHTML = '
'; From aa1aa20043b03bb24783a46e040931804f2a1c5a Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 22 Apr 2024 13:17:17 -0500 Subject: [PATCH 03/14] fix(aria-roles): inline widgets consider composite type widget not inline rule corrected isWidgetType check to consider composite widget types --- lib/rules/widget-not-inline-matches.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rules/widget-not-inline-matches.js b/lib/rules/widget-not-inline-matches.js index 61e5f22706..1e6a7602da 100644 --- a/lib/rules/widget-not-inline-matches.js +++ b/lib/rules/widget-not-inline-matches.js @@ -18,7 +18,8 @@ const matchesFns = [ ]; function isWidgetType(vNode) { - return getRoleType(vNode) === 'widget'; + const roleType = getRoleType(vNode); + return roleType === 'widget' || roleType === 'composite'; } function isNotAreaElement(vNode) { From 2d185f65e11c79cddcd48ac66a672866e3f083a3 Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Thu, 25 Apr 2024 08:14:31 -0500 Subject: [PATCH 04/14] fix(aria-roles): address pr comments - learned sometimes we intentionally spec change - added `window` as a supported type, and `composite` which was missing from the readme - undid my change to `isWidgetType` Refs: #4371 --- doc/standards-object.md | 6 ++++-- lib/rules/widget-not-inline-matches.js | 3 +-- lib/standards/aria-roles.js | 10 ++++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/doc/standards-object.md b/doc/standards-object.md index a439b1ed41..cd34776d96 100644 --- a/doc/standards-object.md +++ b/doc/standards-object.md @@ -67,9 +67,11 @@ The [`ariaRoles`](../lib/standards/aria-roles.js) object defines valid ARIA role - `type` - string(required). [The role type](https://www.w3.org/TR/wai-aria-1.1/#roles_categorization). Valid types are: - `abstract` - - `widget` - - `structure` + - `composite` - `landmark` + - `structure` + - `widget` + - `window` - `requiredContext` - array(optional). List of required parent roles. - `requiredOwned` - array(optional). List of required owned roles. - `requiredAttrs` - array(optional). List of required attributes. diff --git a/lib/rules/widget-not-inline-matches.js b/lib/rules/widget-not-inline-matches.js index 1e6a7602da..61e5f22706 100644 --- a/lib/rules/widget-not-inline-matches.js +++ b/lib/rules/widget-not-inline-matches.js @@ -18,8 +18,7 @@ const matchesFns = [ ]; function isWidgetType(vNode) { - const roleType = getRoleType(vNode); - return roleType === 'widget' || roleType === 'composite'; + return getRoleType(vNode) === 'widget'; } function isNotAreaElement(vNode) { diff --git a/lib/standards/aria-roles.js b/lib/standards/aria-roles.js index 0359909671..7a6d325773 100644 --- a/lib/standards/aria-roles.js +++ b/lib/standards/aria-roles.js @@ -1,5 +1,5 @@ // Source: https://www.w3.org/TR/wai-aria-1.1/#roles -// Source for abstract roles: https://www.w3.org/TR/wai-aria/#abstract_roles and https://www.w3.org/WAI/ARIA/1.2/class-diagram/rdf_model.svg +// Source for abstract roles (types): https://www.w3.org/TR/wai-aria/#abstract_roles /* easiest way to see allowed roles is to filter out the global ones from the list of inherited states and properties. The dpub spec @@ -32,7 +32,7 @@ const ariaRoles = { }, application: { // Note: spec difference - type: 'structure', + 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'], @@ -120,7 +120,8 @@ const ariaRoles = { nameFromContent: true }, combobox: { - type: 'composite', + // Note: spec difference + type: 'widget', requiredAttrs: ['aria-expanded', 'aria-controls'], allowedAttrs: [ 'aria-owns', @@ -302,7 +303,8 @@ const ariaRoles = { superclassRole: ['section'] }, listbox: { - type: 'composite', + // Note: spec difference + type: 'widget', requiredOwned: ['group', 'option'], allowedAttrs: [ 'aria-multiselectable', From 417e6b1a03d3359db37d87a67f5b2ee9687107c2 Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Thu, 25 Apr 2024 08:23:20 -0500 Subject: [PATCH 05/14] fix(aria-roles): reverted test change as well test change was only for data tables, but we didn't want to keep that type change Refs: #4371 --- test/commons/table/is-data-table.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/commons/table/is-data-table.js b/test/commons/table/is-data-table.js index d78c25e7bb..c3607faf0b 100644 --- a/test/commons/table/is-data-table.js +++ b/test/commons/table/is-data-table.js @@ -32,14 +32,6 @@ describe('table.isDataTable', function () { assert.isFalse(axe.commons.table.isDataTable(node)); }); - it('should be false if the table has role=application, which is an astract role of structure', function () { - fixture.innerHTML = '
'; - - var node = fixture.querySelector('table'); - axe.testUtils.flatTreeSetup(fixture.firstChild); - assert.isFalse(axe.commons.table.isDataTable(node)); - }); - it('should be true if the table is inside an editable area', function () { fixture.innerHTML = '
' + @@ -79,6 +71,13 @@ describe('table.isDataTable', function () { }); describe('should be true if the table has a landmark role', function () { + it('application', function () { + fixture.innerHTML = '
'; + + var node = fixture.querySelector('table'); + axe.testUtils.flatTreeSetup(fixture.firstChild); + assert.isTrue(axe.commons.table.isDataTable(node)); + }); it('banner', function () { fixture.innerHTML = '
'; From facd65f6f528122ba9c748e587ae3cf1ebd2a22a Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Thu, 25 Apr 2024 13:35:08 -0500 Subject: [PATCH 06/14] fix(aria-roles): exception for focus-order-semantics add `window` to has-widget-role, renamed Refs: #4371 --- .../has-widget-or-window-role-evaluate.js | 26 + ...le.json => has-widget-or-window-role.json} | 4 +- lib/checks/aria/has-widget-role-evaluate.js | 20 - lib/rules/focus-order-semantics.json | 2 +- locales/_template.json | 2 +- test/checks/aria/has-widget-or-window-role.js | 118 ++++ test/checks/aria/has-widget-role.js | 572 ------------------ 7 files changed, 148 insertions(+), 596 deletions(-) create mode 100644 lib/checks/aria/has-widget-or-window-role-evaluate.js rename lib/checks/aria/{has-widget-role.json => has-widget-or-window-role.json} (67%) delete mode 100644 lib/checks/aria/has-widget-role-evaluate.js create mode 100644 test/checks/aria/has-widget-or-window-role.js delete mode 100644 test/checks/aria/has-widget-role.js diff --git a/lib/checks/aria/has-widget-or-window-role-evaluate.js b/lib/checks/aria/has-widget-or-window-role-evaluate.js new file mode 100644 index 0000000000..676b7d6f62 --- /dev/null +++ b/lib/checks/aria/has-widget-or-window-role-evaluate.js @@ -0,0 +1,26 @@ +import { getRoleType } from '../../commons/aria'; + +const acceptedRoles = { + widget: true, + composite: true, + window: true +}; + +/** + * Check if an elements `role` attribute uses any widget, composite, window abstract role values. + * + * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property. + * + * @memberof checks + * @return {Boolean} True if the element uses a `widget`, `composite`, or `window` abstract role (type). False otherwise. + */ +// # TODO: change to abstract role for widget and window +function hasWidgetOrWindowRoleEvaluate(node) { + const role = node.getAttribute('role'); + if (role === null) { + return false; + } + return !!acceptedRoles[getRoleType(role)]; +} + +export default hasWidgetOrWindowRoleEvaluate; diff --git a/lib/checks/aria/has-widget-role.json b/lib/checks/aria/has-widget-or-window-role.json similarity index 67% rename from lib/checks/aria/has-widget-role.json rename to lib/checks/aria/has-widget-or-window-role.json index bda9ad343c..be3f30f7d2 100644 --- a/lib/checks/aria/has-widget-role.json +++ b/lib/checks/aria/has-widget-or-window-role.json @@ -1,6 +1,6 @@ { - "id": "has-widget-role", - "evaluate": "has-widget-role-evaluate", + "id": "has-widget-or-window-role", + "evaluate": "has-widget-or-window-role-evaluate", "options": [], "metadata": { "impact": "minor", diff --git a/lib/checks/aria/has-widget-role-evaluate.js b/lib/checks/aria/has-widget-role-evaluate.js deleted file mode 100644 index c3344ef207..0000000000 --- a/lib/checks/aria/has-widget-role-evaluate.js +++ /dev/null @@ -1,20 +0,0 @@ -import { getRoleType } from '../../commons/aria'; - -/** - * Check if an elements `role` attribute uses any widget or composite role values. - * - * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property. - * - * @memberof checks - * @return {Boolean} True if the element uses a `widget` or `composite` role. False otherwise. - */ -function hasWidgetRoleEvaluate(node) { - const role = node.getAttribute('role'); - if (role === null) { - return false; - } - const roleType = getRoleType(role); - return roleType === 'widget' || roleType === 'composite'; -} - -export default hasWidgetRoleEvaluate; diff --git a/lib/rules/focus-order-semantics.json b/lib/rules/focus-order-semantics.json index 8c73c2b9f8..65bc657253 100644 --- a/lib/rules/focus-order-semantics.json +++ b/lib/rules/focus-order-semantics.json @@ -9,6 +9,6 @@ "help": "Elements in the focus order should have an appropriate role" }, "all": [], - "any": ["has-widget-role", "valid-scrollable-semantics"], + "any": ["has-widget-or-window-role", "valid-scrollable-semantics"], "none": [] } diff --git a/locales/_template.json b/locales/_template.json index 6ea35122f6..2c09e751c9 100644 --- a/locales/_template.json +++ b/locales/_template.json @@ -576,7 +576,7 @@ }, "fail": "Element does not have global ARIA attribute" }, - "has-widget-role": { + "has-widget-or-window-role": { "pass": "Element has a widget role.", "fail": "Element does not have a widget role." }, diff --git a/test/checks/aria/has-widget-or-window-role.js b/test/checks/aria/has-widget-or-window-role.js new file mode 100644 index 0000000000..2adae9971e --- /dev/null +++ b/test/checks/aria/has-widget-or-window-role.js @@ -0,0 +1,118 @@ +describe('has-widget-or-window-role', function () { + 'use strict'; + + let node; + const fixture = document.getElementById('fixture'); + const checkContext = axe.testUtils.MockCheckContext(); + const evaluate = currentNode => + axe.testUtils + .getCheckEvaluate('has-widget-or-window-role') + .call(checkContext, currentNode); + const roles = { + widget: { + button: true, + checkbox: true, + gridcell: true, + link: true, + menuitem: true, + menuitemcheckbox: true, + menuitemradio: true, + option: true, + progressbar: true, + radio: true, + scrollbar: true, + searchbox: true, + slider: true, + spinbutton: true, + switch: true, + tab: true, + tabpanel: true, + textbox: true, + treeitem: true + }, + composite: { + combobox: true, + grid: true, + listbox: true, + menu: true, + menubar: true, + radiogroup: true, + tablist: true, + tree: true, + treegrid: true, + + application: false, + article: false, + cell: false, + columnheader: false, + definition: false, + directory: false, + document: false, + feed: false, + figure: false, + group: false, + heading: false, + img: false, + list: false, + listitem: false, + math: false, + none: false, + note: false, + presentation: false, + row: false, + rowgroup: false, + rowheader: false, + table: false, + term: false, + toolbar: false + }, + window: { + alertdialog: true, + dialog: true + }, + landmark: { + banner: false, + complimentary: false, + contentinfo: false, + form: false, + name: false, + navigation: false, + region: false, + search: false + } + }; + + afterEach(function () { + node.innerHTML = ''; + checkContext._data = null; + }); + + it('should return false for elements with no role', function () { + node = document.createElement('div'); + fixture.appendChild(node); + + assert.isFalse(evaluate(node)); + }); + + it('should return false for elements with nonsensical roles', function () { + node = document.createElement('div'); + node.setAttribute('role', 'buttonbuttonbutton'); + fixture.appendChild(node); + + assert.isFalse(evaluate(node)); + }); + + Object.keys(roles).forEach(category => { + describe(category, function () { + Object.keys(roles[category]).forEach(role => { + it(`should return ${roles[category][role]} for role="${role}"`, function () { + node = document.createElement('div'); + node.setAttribute('role', role); + fixture.appendChild(node); + + assert.equal(evaluate(node), roles[category][role]); + }); + }); + }); + }); +}); diff --git a/test/checks/aria/has-widget-role.js b/test/checks/aria/has-widget-role.js deleted file mode 100644 index f7324cadc9..0000000000 --- a/test/checks/aria/has-widget-role.js +++ /dev/null @@ -1,572 +0,0 @@ -describe('has-widget-role', function () { - 'use strict'; - - var fixture = document.getElementById('fixture'); - var node; - var checkContext = axe.testUtils.MockCheckContext(); - - afterEach(function () { - node.innerHTML = ''; - checkContext._data = null; - }); - - it('should return false for elements with no role', function () { - node = document.createElement('div'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for elements with nonsensical roles', function () { - node = document.createElement('div'); - node.setAttribute('role', 'buttonbuttonbutton'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - // Widget roles - it('should return true for role=button', function () { - node = document.createElement('div'); - node.setAttribute('role', 'button'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=checkbox', function () { - node = document.createElement('div'); - node.setAttribute('role', 'checkbox'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=gridcell', function () { - node = document.createElement('div'); - node.setAttribute('role', 'gridcell'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=link', function () { - node = document.createElement('div'); - node.setAttribute('role', 'link'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=menuitem', function () { - node = document.createElement('div'); - node.setAttribute('role', 'menuitem'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=menuitemcheckbox', function () { - node = document.createElement('div'); - node.setAttribute('role', 'menuitemcheckbox'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=menuitemradio', function () { - node = document.createElement('div'); - node.setAttribute('role', 'menuitemradio'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=option', function () { - node = document.createElement('div'); - node.setAttribute('role', 'option'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=progressbar', function () { - node = document.createElement('div'); - node.setAttribute('role', 'progressbar'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=radio', function () { - node = document.createElement('div'); - node.setAttribute('role', 'radio'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=scrollbar', function () { - node = document.createElement('div'); - node.setAttribute('role', 'scrollbar'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=searchbox', function () { - node = document.createElement('div'); - node.setAttribute('role', 'searchbox'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=slider', function () { - node = document.createElement('div'); - node.setAttribute('role', 'slider'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=spinbutton', function () { - node = document.createElement('div'); - node.setAttribute('role', 'spinbutton'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=switch', function () { - node = document.createElement('div'); - node.setAttribute('role', 'switch'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=tab', function () { - node = document.createElement('div'); - node.setAttribute('role', 'tab'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=tabpanel', function () { - node = document.createElement('div'); - node.setAttribute('role', 'tabpanel'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=textbox', function () { - node = document.createElement('div'); - node.setAttribute('role', 'textbox'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=treeitem', function () { - node = document.createElement('div'); - node.setAttribute('role', 'treeitem'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - // Composite widget roles - it('should return true for role=combobox', function () { - node = document.createElement('div'); - node.setAttribute('role', 'combobox'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=grid', function () { - node = document.createElement('div'); - node.setAttribute('role', 'grid'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=listbox', function () { - node = document.createElement('div'); - node.setAttribute('role', 'listbox'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=menu', function () { - node = document.createElement('div'); - node.setAttribute('role', 'menu'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=menubar', function () { - node = document.createElement('div'); - node.setAttribute('role', 'menubar'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=radiogroup', function () { - node = document.createElement('div'); - node.setAttribute('role', 'radiogroup'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=tablist', function () { - node = document.createElement('div'); - node.setAttribute('role', 'tablist'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=tree', function () { - node = document.createElement('div'); - node.setAttribute('role', 'tree'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return true for role=treegrid', function () { - node = document.createElement('div'); - node.setAttribute('role', 'treegrid'); - fixture.appendChild(node); - assert.isTrue( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=application', function () { - node = document.createElement('div'); - node.setAttribute('role', 'application'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=article', function () { - node = document.createElement('div'); - node.setAttribute('role', 'article'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=cell', function () { - node = document.createElement('div'); - node.setAttribute('role', 'cell'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=columnheader', function () { - node = document.createElement('div'); - node.setAttribute('role', 'columnheader'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=definition', function () { - node = document.createElement('div'); - node.setAttribute('role', 'definition'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=directory', function () { - node = document.createElement('div'); - node.setAttribute('role', 'directory'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=document', function () { - node = document.createElement('div'); - node.setAttribute('role', 'document'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=feed', function () { - node = document.createElement('div'); - node.setAttribute('role', 'feed'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=figure', function () { - node = document.createElement('div'); - node.setAttribute('role', 'figure'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=group', function () { - node = document.createElement('div'); - node.setAttribute('role', 'group'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=heading', function () { - node = document.createElement('div'); - node.setAttribute('role', 'heading'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=img', function () { - node = document.createElement('div'); - node.setAttribute('role', 'img'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=list', function () { - node = document.createElement('div'); - node.setAttribute('role', 'list'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=listitem', function () { - node = document.createElement('div'); - node.setAttribute('role', 'listitem'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=math', function () { - node = document.createElement('div'); - node.setAttribute('role', 'math'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=none', function () { - node = document.createElement('div'); - node.setAttribute('role', 'none'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=note', function () { - node = document.createElement('div'); - node.setAttribute('role', 'note'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=presentation', function () { - node = document.createElement('div'); - node.setAttribute('role', 'presentation'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=row', function () { - node = document.createElement('div'); - node.setAttribute('role', 'row'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=rowgroup', function () { - node = document.createElement('div'); - node.setAttribute('role', 'rowgroup'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=rowheader', function () { - node = document.createElement('div'); - node.setAttribute('role', 'rowheader'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=table', function () { - node = document.createElement('div'); - node.setAttribute('role', 'table'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=term', function () { - node = document.createElement('div'); - node.setAttribute('role', 'term'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=toolbar', function () { - node = document.createElement('div'); - node.setAttribute('role', 'toolbar'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - // Landmark Roles - it('should return false for role=banner', function () { - node = document.createElement('div'); - node.setAttribute('role', 'banner'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=complementary', function () { - node = document.createElement('div'); - node.setAttribute('role', 'complementary'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=contentinfo', function () { - node = document.createElement('div'); - node.setAttribute('role', 'contentinfo'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=form', function () { - node = document.createElement('div'); - node.setAttribute('role', 'form'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=main', function () { - node = document.createElement('div'); - node.setAttribute('role', 'main'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=navigation', function () { - node = document.createElement('div'); - node.setAttribute('role', 'navigation'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=region', function () { - node = document.createElement('div'); - node.setAttribute('role', 'region'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); - - it('should return false for role=search', function () { - node = document.createElement('div'); - node.setAttribute('role', 'search'); - fixture.appendChild(node); - assert.isFalse( - axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) - ); - }); -}); From bfa95ae2ff233de32390777f735cd35ef1e50fa4 Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Thu, 25 Apr 2024 14:35:45 -0500 Subject: [PATCH 07/14] fix(aria-roles): updated locales better pass/fail english message, removed outdated translations Refs: #4371 --- lib/checks/aria/has-widget-or-window-role.json | 4 ++-- locales/_template.json | 4 ++-- locales/da.json | 4 ---- locales/de.json | 4 ---- locales/el.json | 4 ---- locales/es.json | 4 ---- locales/eu.json | 4 ---- locales/fr.json | 4 ---- locales/he.json | 4 ---- locales/it.json | 4 ---- locales/ja.json | 4 ---- locales/ko.json | 4 ---- locales/no_NB.json | 4 ---- locales/pl.json | 4 ---- locales/pt_BR.json | 4 ---- locales/zh_CN.json | 4 ---- locales/zh_TW.json | 4 ---- 17 files changed, 4 insertions(+), 64 deletions(-) diff --git a/lib/checks/aria/has-widget-or-window-role.json b/lib/checks/aria/has-widget-or-window-role.json index be3f30f7d2..f5fed0b464 100644 --- a/lib/checks/aria/has-widget-or-window-role.json +++ b/lib/checks/aria/has-widget-or-window-role.json @@ -5,8 +5,8 @@ "metadata": { "impact": "minor", "messages": { - "pass": "Element has a widget role.", - "fail": "Element does not have a widget role." + "pass": "Element has a widget or window role.", + "fail": "Element does not have a widget or window role." } } } diff --git a/locales/_template.json b/locales/_template.json index 2c09e751c9..cbdf75ed68 100644 --- a/locales/_template.json +++ b/locales/_template.json @@ -577,8 +577,8 @@ "fail": "Element does not have global ARIA attribute" }, "has-widget-or-window-role": { - "pass": "Element has a widget role.", - "fail": "Element does not have a widget role." + "pass": "Element has a widget or window role.", + "fail": "Element does not have a widget or window role." }, "invalidrole": { "pass": "ARIA role is valid", diff --git a/locales/da.json b/locales/da.json index 56c9a5ccd7..a8f54214ed 100644 --- a/locales/da.json +++ b/locales/da.json @@ -365,10 +365,6 @@ "plural": "'aria-errormessage'-værdi ${data.values}` bør bruge en teknik til at annoncere beskeden (fx 'aria-live', 'aria-describedby', 'role=alert', osv.)" } }, - "has-widget-role": { - "pass": "Elementet har en 'widget'-rolle.", - "fail": "Elementet har ikke en 'widget'-rolle." - }, "invalidrole": { "pass": "ARIA-rollen er korrekt", "fail": "Rollen skal være en af de mulige ARIA-roller" diff --git a/locales/de.json b/locales/de.json index 8f35d0c653..bd8a24b0a0 100644 --- a/locales/de.json +++ b/locales/de.json @@ -576,10 +576,6 @@ }, "fail": "Das Element hat keine globalen ARIA Attribute." }, - "has-widget-role": { - "pass": "Element hat eine widget-Rolle.", - "fail": "Das Element besitzt keine widget-Rolle." - }, "invalidrole": { "pass": "ARIA Rolle ist gültig.", "fail": { diff --git a/locales/el.json b/locales/el.json index f8f805c424..4355fafeb1 100644 --- a/locales/el.json +++ b/locales/el.json @@ -539,10 +539,6 @@ }, "fail": "Το στοιχείο δεν έχει καθολικό χαρακτηριστικό ARIA" }, - "has-widget-role": { - "pass": "Το στοιχείο έχει ρόλο widget.", - "fail": "Το στοιχείο δεν έχει ρόλο widget." - }, "invalidrole": { "pass": "Ο ρόλος ARIA είναι έγκυρος", "fail": { diff --git a/locales/es.json b/locales/es.json index 7a0b5c0b3f..1f5aace76e 100644 --- a/locales/es.json +++ b/locales/es.json @@ -356,10 +356,6 @@ "plural": "En aria-errormessage, valor ${data.values}`, se debe usar una técnica para anunciar el mensaje (p. ej., aria-live, aria-describedby, role=alert, etc.)" } }, - "has-widget-role": { - "pass": "El elemento tiene un rol de widget.", - "fail": "El elemento no tiene un rol de widget." - }, "invalidrole": { "pass": "El rol ARIA es válido", "fail": "El rol debe ser uno de los roles ARIA válidos" diff --git a/locales/eu.json b/locales/eu.json index 03b534cfc7..e4130d3756 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -356,10 +356,6 @@ "plural": "aria-errormessagen, bailioa ${data.values}`, mezua iragartzeko teknika bat erabili behar da (adibidez: aria-live, aria-describedby, role = alert, etab.)." } }, - "has-widget-role": { - "pass": "Elementuak widget rola du.", - "fail": "Elementuak ez du widget rolik." - }, "invalidrole": { "pass": "ARIA rola baliozkoa da", "fail": "Rolak baliozko ARIA rola izan behar du" diff --git a/locales/fr.json b/locales/fr.json index 9bd71725e4..b570785184 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -519,10 +519,6 @@ }, "fail": "L’élément n’a pas d’attribut ARIA global" }, - "has-widget-role": { - "pass": "L’élément a un rôle widget.", - "fail": "L’élément n’a pas de rôle widget." - }, "invalidrole": { "pass": "Le rôle ARIA est valide", "fail": { diff --git a/locales/he.json b/locales/he.json index fd0d2eaa50..5502bac62d 100644 --- a/locales/he.json +++ b/locales/he.json @@ -529,10 +529,6 @@ }, "fail": "לאלמנט אין תכונת ARIA גלובלית: " }, - "has-widget-role": { - "pass": "לאלמנט יש תפקיד של וגדג'ט.", - "fail": "לאלמנט אין תפקיד של וגדג'ט." - }, "invalidrole": { "pass": "תפקיד ARIA קביל", "fail": { diff --git a/locales/it.json b/locales/it.json index 6f1b723259..6aa14e6090 100644 --- a/locales/it.json +++ b/locales/it.json @@ -573,10 +573,6 @@ }, "fail": "L'elemento non ha un attributo ARIA globale" }, - "has-widget-role": { - "pass": "L'elemento ha un ruolo widget.", - "fail": "L'elemento non ha un ruolo widget." - }, "invalidrole": { "pass": "Il ruolo ARIA è valido", "fail": { diff --git a/locales/ja.json b/locales/ja.json index 161fdba32e..ce1f6e0ebe 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -575,10 +575,6 @@ }, "fail": "要素にはグローバルなARIA属性が指定されていません" }, - "has-widget-role": { - "pass": "要素にはwidgetロールが存在します", - "fail": "要素にはwidgetロールが存在しません" - }, "invalidrole": { "pass": "ARIAロールが有効です", "fail": { diff --git a/locales/ko.json b/locales/ko.json index 9c304d1d7f..d86ffeed5b 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -524,10 +524,6 @@ }, "fail": "엘리먼트가 전역 ARIA 어트리뷰트를 가지고 있지 않습니다." }, - "has-widget-role": { - "pass": "엘리먼트가 위젯 역할(role)을 가지고 있습니다.", - "fail": "엘리먼트가 위젯 역할(role)을 가지고 있지 않습니다." - }, "invalidrole": { "pass": "ARIA 역할(role)이 유효합니다.", "fail": { diff --git a/locales/no_NB.json b/locales/no_NB.json index 4dbf8dbe9d..ed73ec0bb4 100644 --- a/locales/no_NB.json +++ b/locales/no_NB.json @@ -365,10 +365,6 @@ "plural": "'aria-errormessage'-verdier ${data.values}` skal bruke en metode for å annonsere beskeden (f.eks. 'aria-live', 'aria-describedby', 'role=alert', osv.)" } }, - "has-widget-role": { - "pass": "Elementet har en 'widget'-rolle.", - "fail": "Elementet har ikke en 'widget'-rolle." - }, "invalidrole": { "pass": "ARIA-rollen er korrekt", "fail": "Rollen skal være en av de mulige ARIA-roller" diff --git a/locales/pl.json b/locales/pl.json index 1caafcabda..7c2f2d1f76 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -575,10 +575,6 @@ }, "fail": "Element nie ma ogólnego atrybutu ARIA" }, - "has-widget-role": { - "pass": "Element ma rolę widżetu.", - "fail": "Element nie ma roli widżetu." - }, "invalidrole": { "pass": "Rola ARIA jest poprawna.", "fail": { diff --git a/locales/pt_BR.json b/locales/pt_BR.json index 404d5ab293..68d4cf2da9 100644 --- a/locales/pt_BR.json +++ b/locales/pt_BR.json @@ -504,10 +504,6 @@ }, "fail": "O elemento não tem atributo ARIA global" }, - "has-widget-role": { - "pass": "Elemento tem um 'role' de 'widget'.", - "fail": "Elemento não tem um 'role' de 'widget'." - }, "invalidrole": { "pass": "O ARIA 'role' é válido", "fail": { diff --git a/locales/zh_CN.json b/locales/zh_CN.json index ef5e6876dc..089cf0ce34 100644 --- a/locales/zh_CN.json +++ b/locales/zh_CN.json @@ -576,10 +576,6 @@ }, "fail": "元素没有全局 ARIA 属性" }, - "has-widget-role": { - "pass": "元素有小部件角色", - "fail": "元素没有小部件角色" - }, "invalidrole": { "pass": "ARIA 角色有效", "fail": { diff --git a/locales/zh_TW.json b/locales/zh_TW.json index 37a9d3262b..ba6c50f4c8 100644 --- a/locales/zh_TW.json +++ b/locales/zh_TW.json @@ -573,10 +573,6 @@ }, "fail": "元素沒有全域 ARIA 屬性" }, - "has-widget-role": { - "pass": "元素具有小部件角色", - "fail": "元素沒有小部件角色" - }, "invalidrole": { "pass": "ARIA 角色有效", "fail": { From a07c4882ba5fed7f44b845a613b035e65c153a6a Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 29 Apr 2024 08:25:25 -0500 Subject: [PATCH 08/14] Revert "fix(aria-roles): exception for focus-order-semantics" Addresses some feedback from Wilco in the PR that this isn't the change we want This reverts commit facd65f6f528122ba9c748e587ae3cf1ebd2a22a. --- .../has-widget-or-window-role-evaluate.js | 26 - lib/checks/aria/has-widget-role-evaluate.js | 20 + ...-window-role.json => has-widget-role.json} | 4 +- lib/rules/focus-order-semantics.json | 2 +- locales/_template.json | 6 +- test/checks/aria/has-widget-or-window-role.js | 118 ---- test/checks/aria/has-widget-role.js | 572 ++++++++++++++++++ 7 files changed, 598 insertions(+), 150 deletions(-) delete mode 100644 lib/checks/aria/has-widget-or-window-role-evaluate.js create mode 100644 lib/checks/aria/has-widget-role-evaluate.js rename lib/checks/aria/{has-widget-or-window-role.json => has-widget-role.json} (69%) delete mode 100644 test/checks/aria/has-widget-or-window-role.js create mode 100644 test/checks/aria/has-widget-role.js diff --git a/lib/checks/aria/has-widget-or-window-role-evaluate.js b/lib/checks/aria/has-widget-or-window-role-evaluate.js deleted file mode 100644 index 676b7d6f62..0000000000 --- a/lib/checks/aria/has-widget-or-window-role-evaluate.js +++ /dev/null @@ -1,26 +0,0 @@ -import { getRoleType } from '../../commons/aria'; - -const acceptedRoles = { - widget: true, - composite: true, - window: true -}; - -/** - * Check if an elements `role` attribute uses any widget, composite, window abstract role values. - * - * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property. - * - * @memberof checks - * @return {Boolean} True if the element uses a `widget`, `composite`, or `window` abstract role (type). False otherwise. - */ -// # TODO: change to abstract role for widget and window -function hasWidgetOrWindowRoleEvaluate(node) { - const role = node.getAttribute('role'); - if (role === null) { - return false; - } - return !!acceptedRoles[getRoleType(role)]; -} - -export default hasWidgetOrWindowRoleEvaluate; diff --git a/lib/checks/aria/has-widget-role-evaluate.js b/lib/checks/aria/has-widget-role-evaluate.js new file mode 100644 index 0000000000..c3344ef207 --- /dev/null +++ b/lib/checks/aria/has-widget-role-evaluate.js @@ -0,0 +1,20 @@ +import { getRoleType } from '../../commons/aria'; + +/** + * Check if an elements `role` attribute uses any widget or composite role values. + * + * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property. + * + * @memberof checks + * @return {Boolean} True if the element uses a `widget` or `composite` role. False otherwise. + */ +function hasWidgetRoleEvaluate(node) { + const role = node.getAttribute('role'); + if (role === null) { + return false; + } + const roleType = getRoleType(role); + return roleType === 'widget' || roleType === 'composite'; +} + +export default hasWidgetRoleEvaluate; diff --git a/lib/checks/aria/has-widget-or-window-role.json b/lib/checks/aria/has-widget-role.json similarity index 69% rename from lib/checks/aria/has-widget-or-window-role.json rename to lib/checks/aria/has-widget-role.json index f5fed0b464..9f79d1487a 100644 --- a/lib/checks/aria/has-widget-or-window-role.json +++ b/lib/checks/aria/has-widget-role.json @@ -1,6 +1,6 @@ { - "id": "has-widget-or-window-role", - "evaluate": "has-widget-or-window-role-evaluate", + "id": "has-widget-role", + "evaluate": "has-widget-role-evaluate", "options": [], "metadata": { "impact": "minor", diff --git a/lib/rules/focus-order-semantics.json b/lib/rules/focus-order-semantics.json index 65bc657253..8c73c2b9f8 100644 --- a/lib/rules/focus-order-semantics.json +++ b/lib/rules/focus-order-semantics.json @@ -9,6 +9,6 @@ "help": "Elements in the focus order should have an appropriate role" }, "all": [], - "any": ["has-widget-or-window-role", "valid-scrollable-semantics"], + "any": ["has-widget-role", "valid-scrollable-semantics"], "none": [] } diff --git a/locales/_template.json b/locales/_template.json index cbdf75ed68..6ea35122f6 100644 --- a/locales/_template.json +++ b/locales/_template.json @@ -576,9 +576,9 @@ }, "fail": "Element does not have global ARIA attribute" }, - "has-widget-or-window-role": { - "pass": "Element has a widget or window role.", - "fail": "Element does not have a widget or window role." + "has-widget-role": { + "pass": "Element has a widget role.", + "fail": "Element does not have a widget role." }, "invalidrole": { "pass": "ARIA role is valid", diff --git a/test/checks/aria/has-widget-or-window-role.js b/test/checks/aria/has-widget-or-window-role.js deleted file mode 100644 index 2adae9971e..0000000000 --- a/test/checks/aria/has-widget-or-window-role.js +++ /dev/null @@ -1,118 +0,0 @@ -describe('has-widget-or-window-role', function () { - 'use strict'; - - let node; - const fixture = document.getElementById('fixture'); - const checkContext = axe.testUtils.MockCheckContext(); - const evaluate = currentNode => - axe.testUtils - .getCheckEvaluate('has-widget-or-window-role') - .call(checkContext, currentNode); - const roles = { - widget: { - button: true, - checkbox: true, - gridcell: true, - link: true, - menuitem: true, - menuitemcheckbox: true, - menuitemradio: true, - option: true, - progressbar: true, - radio: true, - scrollbar: true, - searchbox: true, - slider: true, - spinbutton: true, - switch: true, - tab: true, - tabpanel: true, - textbox: true, - treeitem: true - }, - composite: { - combobox: true, - grid: true, - listbox: true, - menu: true, - menubar: true, - radiogroup: true, - tablist: true, - tree: true, - treegrid: true, - - application: false, - article: false, - cell: false, - columnheader: false, - definition: false, - directory: false, - document: false, - feed: false, - figure: false, - group: false, - heading: false, - img: false, - list: false, - listitem: false, - math: false, - none: false, - note: false, - presentation: false, - row: false, - rowgroup: false, - rowheader: false, - table: false, - term: false, - toolbar: false - }, - window: { - alertdialog: true, - dialog: true - }, - landmark: { - banner: false, - complimentary: false, - contentinfo: false, - form: false, - name: false, - navigation: false, - region: false, - search: false - } - }; - - afterEach(function () { - node.innerHTML = ''; - checkContext._data = null; - }); - - it('should return false for elements with no role', function () { - node = document.createElement('div'); - fixture.appendChild(node); - - assert.isFalse(evaluate(node)); - }); - - it('should return false for elements with nonsensical roles', function () { - node = document.createElement('div'); - node.setAttribute('role', 'buttonbuttonbutton'); - fixture.appendChild(node); - - assert.isFalse(evaluate(node)); - }); - - Object.keys(roles).forEach(category => { - describe(category, function () { - Object.keys(roles[category]).forEach(role => { - it(`should return ${roles[category][role]} for role="${role}"`, function () { - node = document.createElement('div'); - node.setAttribute('role', role); - fixture.appendChild(node); - - assert.equal(evaluate(node), roles[category][role]); - }); - }); - }); - }); -}); diff --git a/test/checks/aria/has-widget-role.js b/test/checks/aria/has-widget-role.js new file mode 100644 index 0000000000..f7324cadc9 --- /dev/null +++ b/test/checks/aria/has-widget-role.js @@ -0,0 +1,572 @@ +describe('has-widget-role', function () { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var node; + var checkContext = axe.testUtils.MockCheckContext(); + + afterEach(function () { + node.innerHTML = ''; + checkContext._data = null; + }); + + it('should return false for elements with no role', function () { + node = document.createElement('div'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for elements with nonsensical roles', function () { + node = document.createElement('div'); + node.setAttribute('role', 'buttonbuttonbutton'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + // Widget roles + it('should return true for role=button', function () { + node = document.createElement('div'); + node.setAttribute('role', 'button'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=checkbox', function () { + node = document.createElement('div'); + node.setAttribute('role', 'checkbox'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=gridcell', function () { + node = document.createElement('div'); + node.setAttribute('role', 'gridcell'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=link', function () { + node = document.createElement('div'); + node.setAttribute('role', 'link'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=menuitem', function () { + node = document.createElement('div'); + node.setAttribute('role', 'menuitem'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=menuitemcheckbox', function () { + node = document.createElement('div'); + node.setAttribute('role', 'menuitemcheckbox'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=menuitemradio', function () { + node = document.createElement('div'); + node.setAttribute('role', 'menuitemradio'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=option', function () { + node = document.createElement('div'); + node.setAttribute('role', 'option'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=progressbar', function () { + node = document.createElement('div'); + node.setAttribute('role', 'progressbar'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=radio', function () { + node = document.createElement('div'); + node.setAttribute('role', 'radio'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=scrollbar', function () { + node = document.createElement('div'); + node.setAttribute('role', 'scrollbar'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=searchbox', function () { + node = document.createElement('div'); + node.setAttribute('role', 'searchbox'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=slider', function () { + node = document.createElement('div'); + node.setAttribute('role', 'slider'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=spinbutton', function () { + node = document.createElement('div'); + node.setAttribute('role', 'spinbutton'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=switch', function () { + node = document.createElement('div'); + node.setAttribute('role', 'switch'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=tab', function () { + node = document.createElement('div'); + node.setAttribute('role', 'tab'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=tabpanel', function () { + node = document.createElement('div'); + node.setAttribute('role', 'tabpanel'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=textbox', function () { + node = document.createElement('div'); + node.setAttribute('role', 'textbox'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=treeitem', function () { + node = document.createElement('div'); + node.setAttribute('role', 'treeitem'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + // Composite widget roles + it('should return true for role=combobox', function () { + node = document.createElement('div'); + node.setAttribute('role', 'combobox'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=grid', function () { + node = document.createElement('div'); + node.setAttribute('role', 'grid'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=listbox', function () { + node = document.createElement('div'); + node.setAttribute('role', 'listbox'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=menu', function () { + node = document.createElement('div'); + node.setAttribute('role', 'menu'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=menubar', function () { + node = document.createElement('div'); + node.setAttribute('role', 'menubar'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=radiogroup', function () { + node = document.createElement('div'); + node.setAttribute('role', 'radiogroup'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=tablist', function () { + node = document.createElement('div'); + node.setAttribute('role', 'tablist'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=tree', function () { + node = document.createElement('div'); + node.setAttribute('role', 'tree'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return true for role=treegrid', function () { + node = document.createElement('div'); + node.setAttribute('role', 'treegrid'); + fixture.appendChild(node); + assert.isTrue( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=application', function () { + node = document.createElement('div'); + node.setAttribute('role', 'application'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=article', function () { + node = document.createElement('div'); + node.setAttribute('role', 'article'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=cell', function () { + node = document.createElement('div'); + node.setAttribute('role', 'cell'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=columnheader', function () { + node = document.createElement('div'); + node.setAttribute('role', 'columnheader'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=definition', function () { + node = document.createElement('div'); + node.setAttribute('role', 'definition'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=directory', function () { + node = document.createElement('div'); + node.setAttribute('role', 'directory'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=document', function () { + node = document.createElement('div'); + node.setAttribute('role', 'document'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=feed', function () { + node = document.createElement('div'); + node.setAttribute('role', 'feed'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=figure', function () { + node = document.createElement('div'); + node.setAttribute('role', 'figure'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=group', function () { + node = document.createElement('div'); + node.setAttribute('role', 'group'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=heading', function () { + node = document.createElement('div'); + node.setAttribute('role', 'heading'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=img', function () { + node = document.createElement('div'); + node.setAttribute('role', 'img'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=list', function () { + node = document.createElement('div'); + node.setAttribute('role', 'list'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=listitem', function () { + node = document.createElement('div'); + node.setAttribute('role', 'listitem'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=math', function () { + node = document.createElement('div'); + node.setAttribute('role', 'math'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=none', function () { + node = document.createElement('div'); + node.setAttribute('role', 'none'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=note', function () { + node = document.createElement('div'); + node.setAttribute('role', 'note'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=presentation', function () { + node = document.createElement('div'); + node.setAttribute('role', 'presentation'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=row', function () { + node = document.createElement('div'); + node.setAttribute('role', 'row'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=rowgroup', function () { + node = document.createElement('div'); + node.setAttribute('role', 'rowgroup'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=rowheader', function () { + node = document.createElement('div'); + node.setAttribute('role', 'rowheader'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=table', function () { + node = document.createElement('div'); + node.setAttribute('role', 'table'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=term', function () { + node = document.createElement('div'); + node.setAttribute('role', 'term'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=toolbar', function () { + node = document.createElement('div'); + node.setAttribute('role', 'toolbar'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + // Landmark Roles + it('should return false for role=banner', function () { + node = document.createElement('div'); + node.setAttribute('role', 'banner'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=complementary', function () { + node = document.createElement('div'); + node.setAttribute('role', 'complementary'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=contentinfo', function () { + node = document.createElement('div'); + node.setAttribute('role', 'contentinfo'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=form', function () { + node = document.createElement('div'); + node.setAttribute('role', 'form'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=main', function () { + node = document.createElement('div'); + node.setAttribute('role', 'main'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=navigation', function () { + node = document.createElement('div'); + node.setAttribute('role', 'navigation'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=region', function () { + node = document.createElement('div'); + node.setAttribute('role', 'region'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); + + it('should return false for role=search', function () { + node = document.createElement('div'); + node.setAttribute('role', 'search'); + fixture.appendChild(node); + assert.isFalse( + axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node) + ); + }); +}); From f11c03fc73f6a932e320415d8a060dbd17b4a98a Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 29 Apr 2024 08:39:24 -0500 Subject: [PATCH 09/14] fix(aria-roles): support window role in valid-scrollable-semantics reflected in focus-order-semantics --- .../valid-scrollable-semantics-evaluate.js | 2 ++ .../checks/aria/valid-scrollable-semantics.js | 24 +++++++++++++++++++ .../focus-order-semantics.html | 5 ++++ .../focus-order-semantics.json | 4 +++- 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/checks/aria/valid-scrollable-semantics-evaluate.js b/lib/checks/aria/valid-scrollable-semantics-evaluate.js index 7c3615dbcc..933bd3e69a 100644 --- a/lib/checks/aria/valid-scrollable-semantics-evaluate.js +++ b/lib/checks/aria/valid-scrollable-semantics-evaluate.js @@ -16,11 +16,13 @@ const VALID_TAG_NAMES_FOR_SCROLLABLE_REGIONS = { * appropriate for scrollable elements found in the focus order. */ const VALID_ROLES_FOR_SCROLLABLE_REGIONS = { + alertdialog: true, application: true, article: true, banner: false, complementary: true, contentinfo: true, + dialog: true, form: true, main: true, navigation: true, diff --git a/test/checks/aria/valid-scrollable-semantics.js b/test/checks/aria/valid-scrollable-semantics.js index 26ea72533a..63c7f1d6fe 100644 --- a/test/checks/aria/valid-scrollable-semantics.js +++ b/test/checks/aria/valid-scrollable-semantics.js @@ -106,6 +106,18 @@ describe('valid-scrollable-semantics', function () { ); }); + it('should return true for role=alertdialog', function () { + var node = document.createElement('div'); + node.setAttribute('role', 'alertdialog'); + fixture.appendChild(node); + flatTreeSetup(fixture); + assert.isTrue( + axe.testUtils + .getCheckEvaluate('valid-scrollable-semantics') + .call(checkContext, node) + ); + }); + it('should return true for role=article', function () { var node = document.createElement('div'); node.setAttribute('role', 'article'); @@ -118,6 +130,18 @@ describe('valid-scrollable-semantics', function () { ); }); + it('should return true for role=dialog', function () { + var node = document.createElement('div'); + node.setAttribute('role', 'dialog'); + fixture.appendChild(node); + flatTreeSetup(fixture); + assert.isTrue( + axe.testUtils + .getCheckEvaluate('valid-scrollable-semantics') + .call(checkContext, node) + ); + }); + it('should return true for nav elements', function () { var node = document.createElement('nav'); fixture.appendChild(node); diff --git a/test/integration/rules/focus-order-semantics/focus-order-semantics.html b/test/integration/rules/focus-order-semantics/focus-order-semantics.html index 7ac09790ae..3a95bd5cea 100644 --- a/test/integration/rules/focus-order-semantics/focus-order-semantics.html +++ b/test/integration/rules/focus-order-semantics/focus-order-semantics.html @@ -32,6 +32,11 @@

Valid landmark roles for scrollable containers

+

Valid window roles for scrollable containers

+
+
+ +

Valid scrollable HTML tags for scrollable regions, not selected by this rule diff --git a/test/integration/rules/focus-order-semantics/focus-order-semantics.json b/test/integration/rules/focus-order-semantics/focus-order-semantics.json index dac1ba8f13..cc1365bbe5 100644 --- a/test/integration/rules/focus-order-semantics/focus-order-semantics.json +++ b/test/integration/rules/focus-order-semantics/focus-order-semantics.json @@ -11,7 +11,9 @@ ["#pass7"], ["#pass8"], ["#pass9"], - ["#pass10"] + ["#pass10"], + ["#pass11"], + ["#pass12"] ], "violations": [ ["#violation1"], From d85631696321b0c2def1336d070f188011b779bc Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 29 Apr 2024 08:52:50 -0500 Subject: [PATCH 10/14] fix(aria-roles): window roles and marque/timer expectations in focus-order-semantics addressing more pr feedback --- lib/checks/aria/valid-scrollable-semantics-evaluate.js | 1 + .../rules/focus-order-semantics/focus-order-semantics.html | 7 +++++-- .../rules/focus-order-semantics/focus-order-semantics.json | 7 +++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/checks/aria/valid-scrollable-semantics-evaluate.js b/lib/checks/aria/valid-scrollable-semantics-evaluate.js index 933bd3e69a..755cbf4724 100644 --- a/lib/checks/aria/valid-scrollable-semantics-evaluate.js +++ b/lib/checks/aria/valid-scrollable-semantics-evaluate.js @@ -16,6 +16,7 @@ const VALID_TAG_NAMES_FOR_SCROLLABLE_REGIONS = { * appropriate for scrollable elements found in the focus order. */ const VALID_ROLES_FOR_SCROLLABLE_REGIONS = { + alert: true, alertdialog: true, application: true, article: true, diff --git a/test/integration/rules/focus-order-semantics/focus-order-semantics.html b/test/integration/rules/focus-order-semantics/focus-order-semantics.html index 3a95bd5cea..296b1cb939 100644 --- a/test/integration/rules/focus-order-semantics/focus-order-semantics.html +++ b/test/integration/rules/focus-order-semantics/focus-order-semantics.html @@ -19,6 +19,8 @@

Invalid landmark roles for scrollable containers

+
+

Valid landmark roles for scrollable containers

@@ -31,11 +33,12 @@

Valid landmark roles for scrollable containers

+

Valid window roles for scrollable containers

-
- +
+

Valid scrollable HTML tags for scrollable regions, not selected by this diff --git a/test/integration/rules/focus-order-semantics/focus-order-semantics.json b/test/integration/rules/focus-order-semantics/focus-order-semantics.json index cc1365bbe5..0cfe89330d 100644 --- a/test/integration/rules/focus-order-semantics/focus-order-semantics.json +++ b/test/integration/rules/focus-order-semantics/focus-order-semantics.json @@ -13,12 +13,15 @@ ["#pass9"], ["#pass10"], ["#pass11"], - ["#pass12"] + ["#pass12"], + ["#pass13"] ], "violations": [ ["#violation1"], ["#violation2"], ["#violation3"], - ["#violation4"] + ["#violation4"], + ["#violation5"], + ["#violation6"] ] } From dc966d3a5de35d0c4e1946ba8c1096b9f70763c7 Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 29 Apr 2024 08:54:15 -0500 Subject: [PATCH 11/14] Revert "fix(aria-roles): updated locales" This reverts commit bfa95ae2ff233de32390777f735cd35ef1e50fa4. --- lib/checks/aria/has-widget-role.json | 4 ++-- locales/da.json | 4 ++++ locales/de.json | 4 ++++ locales/el.json | 4 ++++ locales/es.json | 4 ++++ locales/eu.json | 4 ++++ locales/fr.json | 4 ++++ locales/he.json | 4 ++++ locales/it.json | 4 ++++ locales/ja.json | 4 ++++ locales/ko.json | 4 ++++ locales/no_NB.json | 4 ++++ locales/pl.json | 4 ++++ locales/pt_BR.json | 4 ++++ locales/zh_CN.json | 4 ++++ locales/zh_TW.json | 4 ++++ 16 files changed, 62 insertions(+), 2 deletions(-) diff --git a/lib/checks/aria/has-widget-role.json b/lib/checks/aria/has-widget-role.json index 9f79d1487a..bda9ad343c 100644 --- a/lib/checks/aria/has-widget-role.json +++ b/lib/checks/aria/has-widget-role.json @@ -5,8 +5,8 @@ "metadata": { "impact": "minor", "messages": { - "pass": "Element has a widget or window role.", - "fail": "Element does not have a widget or window role." + "pass": "Element has a widget role.", + "fail": "Element does not have a widget role." } } } diff --git a/locales/da.json b/locales/da.json index a8f54214ed..56c9a5ccd7 100644 --- a/locales/da.json +++ b/locales/da.json @@ -365,6 +365,10 @@ "plural": "'aria-errormessage'-værdi ${data.values}` bør bruge en teknik til at annoncere beskeden (fx 'aria-live', 'aria-describedby', 'role=alert', osv.)" } }, + "has-widget-role": { + "pass": "Elementet har en 'widget'-rolle.", + "fail": "Elementet har ikke en 'widget'-rolle." + }, "invalidrole": { "pass": "ARIA-rollen er korrekt", "fail": "Rollen skal være en af de mulige ARIA-roller" diff --git a/locales/de.json b/locales/de.json index bd8a24b0a0..8f35d0c653 100644 --- a/locales/de.json +++ b/locales/de.json @@ -576,6 +576,10 @@ }, "fail": "Das Element hat keine globalen ARIA Attribute." }, + "has-widget-role": { + "pass": "Element hat eine widget-Rolle.", + "fail": "Das Element besitzt keine widget-Rolle." + }, "invalidrole": { "pass": "ARIA Rolle ist gültig.", "fail": { diff --git a/locales/el.json b/locales/el.json index 4355fafeb1..f8f805c424 100644 --- a/locales/el.json +++ b/locales/el.json @@ -539,6 +539,10 @@ }, "fail": "Το στοιχείο δεν έχει καθολικό χαρακτηριστικό ARIA" }, + "has-widget-role": { + "pass": "Το στοιχείο έχει ρόλο widget.", + "fail": "Το στοιχείο δεν έχει ρόλο widget." + }, "invalidrole": { "pass": "Ο ρόλος ARIA είναι έγκυρος", "fail": { diff --git a/locales/es.json b/locales/es.json index 1f5aace76e..7a0b5c0b3f 100644 --- a/locales/es.json +++ b/locales/es.json @@ -356,6 +356,10 @@ "plural": "En aria-errormessage, valor ${data.values}`, se debe usar una técnica para anunciar el mensaje (p. ej., aria-live, aria-describedby, role=alert, etc.)" } }, + "has-widget-role": { + "pass": "El elemento tiene un rol de widget.", + "fail": "El elemento no tiene un rol de widget." + }, "invalidrole": { "pass": "El rol ARIA es válido", "fail": "El rol debe ser uno de los roles ARIA válidos" diff --git a/locales/eu.json b/locales/eu.json index e4130d3756..03b534cfc7 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -356,6 +356,10 @@ "plural": "aria-errormessagen, bailioa ${data.values}`, mezua iragartzeko teknika bat erabili behar da (adibidez: aria-live, aria-describedby, role = alert, etab.)." } }, + "has-widget-role": { + "pass": "Elementuak widget rola du.", + "fail": "Elementuak ez du widget rolik." + }, "invalidrole": { "pass": "ARIA rola baliozkoa da", "fail": "Rolak baliozko ARIA rola izan behar du" diff --git a/locales/fr.json b/locales/fr.json index b570785184..9bd71725e4 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -519,6 +519,10 @@ }, "fail": "L’élément n’a pas d’attribut ARIA global" }, + "has-widget-role": { + "pass": "L’élément a un rôle widget.", + "fail": "L’élément n’a pas de rôle widget." + }, "invalidrole": { "pass": "Le rôle ARIA est valide", "fail": { diff --git a/locales/he.json b/locales/he.json index 5502bac62d..fd0d2eaa50 100644 --- a/locales/he.json +++ b/locales/he.json @@ -529,6 +529,10 @@ }, "fail": "לאלמנט אין תכונת ARIA גלובלית: " }, + "has-widget-role": { + "pass": "לאלמנט יש תפקיד של וגדג'ט.", + "fail": "לאלמנט אין תפקיד של וגדג'ט." + }, "invalidrole": { "pass": "תפקיד ARIA קביל", "fail": { diff --git a/locales/it.json b/locales/it.json index 6aa14e6090..6f1b723259 100644 --- a/locales/it.json +++ b/locales/it.json @@ -573,6 +573,10 @@ }, "fail": "L'elemento non ha un attributo ARIA globale" }, + "has-widget-role": { + "pass": "L'elemento ha un ruolo widget.", + "fail": "L'elemento non ha un ruolo widget." + }, "invalidrole": { "pass": "Il ruolo ARIA è valido", "fail": { diff --git a/locales/ja.json b/locales/ja.json index ce1f6e0ebe..161fdba32e 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -575,6 +575,10 @@ }, "fail": "要素にはグローバルなARIA属性が指定されていません" }, + "has-widget-role": { + "pass": "要素にはwidgetロールが存在します", + "fail": "要素にはwidgetロールが存在しません" + }, "invalidrole": { "pass": "ARIAロールが有効です", "fail": { diff --git a/locales/ko.json b/locales/ko.json index d86ffeed5b..9c304d1d7f 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -524,6 +524,10 @@ }, "fail": "엘리먼트가 전역 ARIA 어트리뷰트를 가지고 있지 않습니다." }, + "has-widget-role": { + "pass": "엘리먼트가 위젯 역할(role)을 가지고 있습니다.", + "fail": "엘리먼트가 위젯 역할(role)을 가지고 있지 않습니다." + }, "invalidrole": { "pass": "ARIA 역할(role)이 유효합니다.", "fail": { diff --git a/locales/no_NB.json b/locales/no_NB.json index ed73ec0bb4..4dbf8dbe9d 100644 --- a/locales/no_NB.json +++ b/locales/no_NB.json @@ -365,6 +365,10 @@ "plural": "'aria-errormessage'-verdier ${data.values}` skal bruke en metode for å annonsere beskeden (f.eks. 'aria-live', 'aria-describedby', 'role=alert', osv.)" } }, + "has-widget-role": { + "pass": "Elementet har en 'widget'-rolle.", + "fail": "Elementet har ikke en 'widget'-rolle." + }, "invalidrole": { "pass": "ARIA-rollen er korrekt", "fail": "Rollen skal være en av de mulige ARIA-roller" diff --git a/locales/pl.json b/locales/pl.json index 7c2f2d1f76..1caafcabda 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -575,6 +575,10 @@ }, "fail": "Element nie ma ogólnego atrybutu ARIA" }, + "has-widget-role": { + "pass": "Element ma rolę widżetu.", + "fail": "Element nie ma roli widżetu." + }, "invalidrole": { "pass": "Rola ARIA jest poprawna.", "fail": { diff --git a/locales/pt_BR.json b/locales/pt_BR.json index 68d4cf2da9..404d5ab293 100644 --- a/locales/pt_BR.json +++ b/locales/pt_BR.json @@ -504,6 +504,10 @@ }, "fail": "O elemento não tem atributo ARIA global" }, + "has-widget-role": { + "pass": "Elemento tem um 'role' de 'widget'.", + "fail": "Elemento não tem um 'role' de 'widget'." + }, "invalidrole": { "pass": "O ARIA 'role' é válido", "fail": { diff --git a/locales/zh_CN.json b/locales/zh_CN.json index 089cf0ce34..ef5e6876dc 100644 --- a/locales/zh_CN.json +++ b/locales/zh_CN.json @@ -576,6 +576,10 @@ }, "fail": "元素没有全局 ARIA 属性" }, + "has-widget-role": { + "pass": "元素有小部件角色", + "fail": "元素没有小部件角色" + }, "invalidrole": { "pass": "ARIA 角色有效", "fail": { diff --git a/locales/zh_TW.json b/locales/zh_TW.json index ba6c50f4c8..37a9d3262b 100644 --- a/locales/zh_TW.json +++ b/locales/zh_TW.json @@ -573,6 +573,10 @@ }, "fail": "元素沒有全域 ARIA 屬性" }, + "has-widget-role": { + "pass": "元素具有小部件角色", + "fail": "元素沒有小部件角色" + }, "invalidrole": { "pass": "ARIA 角色有效", "fail": { From 9b15ae0bccbe1dccc637c2de13085f6b7da7f74b Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 29 Apr 2024 09:00:43 -0500 Subject: [PATCH 12/14] fix(aria-roles): log and status allowed in focus-order-semantics extends prior work by accounting for all standards aria roles changes --- lib/checks/aria/valid-scrollable-semantics-evaluate.js | 4 +++- .../rules/focus-order-semantics/focus-order-semantics.html | 6 ++++-- .../rules/focus-order-semantics/focus-order-semantics.json | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/checks/aria/valid-scrollable-semantics-evaluate.js b/lib/checks/aria/valid-scrollable-semantics-evaluate.js index 755cbf4724..fef446f35e 100644 --- a/lib/checks/aria/valid-scrollable-semantics-evaluate.js +++ b/lib/checks/aria/valid-scrollable-semantics-evaluate.js @@ -25,10 +25,12 @@ const VALID_ROLES_FOR_SCROLLABLE_REGIONS = { contentinfo: true, dialog: true, form: true, + log: true, main: true, navigation: true, region: true, - search: false + search: false, + status: true }; /** diff --git a/test/integration/rules/focus-order-semantics/focus-order-semantics.html b/test/integration/rules/focus-order-semantics/focus-order-semantics.html index 296b1cb939..9e8593a408 100644 --- a/test/integration/rules/focus-order-semantics/focus-order-semantics.html +++ b/test/integration/rules/focus-order-semantics/focus-order-semantics.html @@ -34,11 +34,13 @@

Valid landmark roles for scrollable containers

+
+

Valid window roles for scrollable containers

-
- +
+

Valid scrollable HTML tags for scrollable regions, not selected by this diff --git a/test/integration/rules/focus-order-semantics/focus-order-semantics.json b/test/integration/rules/focus-order-semantics/focus-order-semantics.json index 0cfe89330d..b9ed3b20c1 100644 --- a/test/integration/rules/focus-order-semantics/focus-order-semantics.json +++ b/test/integration/rules/focus-order-semantics/focus-order-semantics.json @@ -14,7 +14,9 @@ ["#pass10"], ["#pass11"], ["#pass12"], - ["#pass13"] + ["#pass13"], + ["#pass14"], + ["#pass15"] ], "violations": [ ["#violation1"], From 5594d8f1ff45703bed81e4a137afb42b6449d0ee Mon Sep 17 00:00:00 2001 From: Ava Gaiety W Date: Mon, 29 Apr 2024 12:50:21 -0500 Subject: [PATCH 13/14] fix(aria-roles): target-size tests consider changed aria roles last feedback yay --- .../rules/target-size/target-size.html | 35 +++++++++++++++++++ .../rules/target-size/target-size.json | 7 ++++ 2 files changed, 42 insertions(+) diff --git a/test/integration/rules/target-size/target-size.html b/test/integration/rules/target-size/target-size.html index 3c4b0be940..d3cef72ee3 100644 --- a/test/integration/rules/target-size/target-size.html +++ b/test/integration/rules/target-size/target-size.html @@ -28,6 +28,41 @@

+
+ +
Cookies
+
+ +
+ + +
+ +
+ + +
+ +
+ +
Log
+
+ +
+ +
Marquee
+
+ +
+ +
Status
+
+ +
+ +
Timer
+
+

Date: Wed, 1 May 2024 11:13:33 -0600 Subject: [PATCH 14/14] fix(aria-allowed-attr): allow meter role allowed aria-* attributes on meter element (#4435) Turns out we didn't list the `meter` element's implicit role. Ref: https://github.com/w3c/html-aria/issues/517 QA notes: verify a `meter` element allows non-global aria attributes from the [`meter` role](https://www.w3.org/TR/wai-aria-1.2/#meter). --- lib/commons/standards/implicit-html-roles.js | 1 + test/integration/rules/aria-allowed-attr/passes.html | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/commons/standards/implicit-html-roles.js b/lib/commons/standards/implicit-html-roles.js index 64455b1cb2..b302f0319b 100644 --- a/lib/commons/standards/implicit-html-roles.js +++ b/lib/commons/standards/implicit-html-roles.js @@ -151,6 +151,7 @@ const implicitHtmlRoles = { main: 'main', math: 'math', menu: 'list', + meter: 'meter', nav: 'navigation', ol: 'list', optgroup: 'group', diff --git a/test/integration/rules/aria-allowed-attr/passes.html b/test/integration/rules/aria-allowed-attr/passes.html index dfef5d3ed5..c1ebea91bf 100644 --- a/test/integration/rules/aria-allowed-attr/passes.html +++ b/test/integration/rules/aria-allowed-attr/passes.html @@ -2118,7 +2118,16 @@

- + + 40 +