Skip to content

Commit

Permalink
Merge pull request #1773 from IBMa/dev-916
Browse files Browse the repository at this point in the history
fixrule(`element_accesskey_labelled`) fix the label requirement for the elements with accesskey attribute
  • Loading branch information
ErickRenteria authored Dec 14, 2023
2 parents 4d5c2d0 + e8f468d commit 4be30c1
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,19 @@ <h3 id="ruleMessage"></h3>

### Why is this important?

The label of an HTML element with an `accesskey` attribute allows the user agent to display a list of access keys with a name describing each access key’s function. It also allows voice control users to speak the label to activate its function.
The label of an element with an `accesskey` attribute allows the user agent to display a list of access keys with a name describing each access key’s function.
The label also allows voice control users to speak the label to activate its function.

<!-- This is where the code snippet is injected -->
<div id="locSnippet"></div>

### What to do

* Provide a label using a `title` attribute (e.g. `<a title="Activities" accesskey="A" href="/Consortium/activities">Activities</a>`);
* **Or**, use `<label for="">` or `aria-labelledby` to designate visible text as the label;
* **Or**, use an input embedded in a `<label>` (e.g. `<label><input type="checkbox" accesskey="A"/>foo</label>`);
* **Or**, if the element does not have a visible label, provide a label using the `aria-label` attribute (e.g. `aria-label="Activities"`).
Provide an accessible name for the element with the `accesskey` attribute using one of the following examples:
* `title` attribute (e.g., `<p title="Activity" accesskey="A" href="/Consortium/activity">Activity: This is a paragraph that users need to jump to.</a>`)
* `<label for="an id">` or `aria-labelledby` to designate visible text as the label
* `<input>` embedded in a `<label>` (e.g., `<label><input type="checkbox" accesskey="A"/>Activity</label>`)
* `aria-label` attribute if the element does not have a visible label (e.g., `aria-label="Activity"`)

</script></mark-down>
<!-- End main panel -->
Expand All @@ -69,12 +71,14 @@ <h3 id="ruleMessage"></h3>
### About this requirement

* [IBM 3.3.2 Labels and Instructions](https://www.ibm.com/able/requirements/requirements/#3_3_2)
* [WebAIM - The accesskey attribute](https://webaim.org/techniques/keyboard/accesskey#spec)
* [ARIA practices - Developing a Keyboard Interface](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/)
* [WebAIM - The accesskey attribute](https://webaim.org/techniques/keyboard/accesskey/#spec)
* [ARIA practices - Developing a Keyboard Interface](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/)
* [ARIA practices - Providing Accessible Names and Descriptions](https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/)


### Who does this affect?

* People using a screen reader, including blind, low vision and neurodivergent people
* People using a screen reader, including blind, low vision, and neurodivergent people
* People who physically cannot use a pointing device
* People with dexterity impairment using voice control

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
limitations under the License.
*****************************************************************************/

import { Rule, RuleResult, RuleFail, RuleContext, RulePotential, RuleManual, RulePass, RuleContextHierarchy } from "../api/IRule";
import { Rule, RuleResult, RuleContext, RulePotential, RulePass, RuleContextHierarchy } from "../api/IRule";
import { eRulePolicy, eToolkitLevel } from "../api/IRule";
import { RPTUtil } from "../../v2/checker/accessibility/util/legacy";
import { DOMWalker } from "../../v2/dom/DOMWalker";
import { VisUtil } from "../../v2/dom/VisUtil";
import { ARIADefinitions } from "../../v2/aria/ARIADefinitions";
import { ARIAMapper } from "../../v2/aria/ARIAMapper";

export let element_accesskey_labelled: Rule = {
id: "element_accesskey_labelled",
Expand All @@ -34,8 +36,8 @@ export let element_accesskey_labelled: Rule = {
messages: {
"en-US": {
"Pass_0": "Rule Passed",
"Potential_1": "The HTML element with an assigned 'accesskey' attribute does not have an associated label",
"group": "An HTML element with an assigned 'accesskey' attribute must have an associated label"
"Potential_1": "The element with an assigned 'accesskey' attribute does not have an associated label",
"group": "An element with an assigned 'accesskey' attribute must have an associated label"
}
},
rulesets: [{
Expand All @@ -46,25 +48,36 @@ export let element_accesskey_labelled: Rule = {
}],
act: [],
run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => {
const ruleContext = context["dom"].node as Element;
let passed = false;
if (RPTUtil.attributeNonEmpty(ruleContext, "title")) {
passed = true;
} else if (RPTUtil.attributeNonEmpty(ruleContext, "aria-label")) {
passed = true;
} else if (RPTUtil.getLabelForElementHidden(ruleContext, true)) { // ignore hidden
passed = true;
} else if (RPTUtil.attributeNonEmpty(ruleContext, "aria-labelledby")) {
// assume the validity of the id (of aria-labelledby) is checked by a different rule
passed = true;
} else if (ruleContext.nodeName.toLowerCase() === "input"
&& DOMWalker.parentNode(ruleContext).nodeName.toLowerCase() === "label") {
// assume the validity of the label, e.g. empty label, is checked by a different rule
passed = true;
}
const ruleContext = context["dom"].node as HTMLElement;
//skip the check if the element is hidden or disabled
if (!VisUtil.isNodeVisible(ruleContext) || RPTUtil.isNodeDisabled(ruleContext))
return;

//skip if the element is tabbable, it's covered by other rules
if (RPTUtil.isTabbable(ruleContext))
return;

let roles = RPTUtil.getRoles(ruleContext, true);
//skip the native element, mostly text elements
if (!roles || roles.length === 0) return;

let patterns = ARIADefinitions.designPatterns[roles[0]]
if (!patterns.nameFrom)
return;

// ignore if accessble name is required (checked in other rules) or prohibited (text element)
if (patterns.nameRequired || !patterns.nameFrom || patterns.nameFrom.includes("prohibited"))
return;

//special case: legend, as a child of a fieldset, delegate the accesskey command to the field of the fieldset which is covered by other rules
if (ruleContext.parentElement && ruleContext.parentElement.nodeName.toLowerCase() === 'fieldset')
return;

// check if accessible name exists
if (ARIAMapper.computeName(ruleContext).trim().length > 0)
return RulePass("Pass_0");

if (passed) return RulePass("Pass_0");
if (!passed) return RulePotential("Potential_1");
return RulePotential("Potential_1");

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
passedXpaths: [
],
failedXpaths: [
"/html/body/p"

]
}
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en-us" xml:lang="en-us">
<head>
<title>accessble key</title>
</head>
<body role="main">

<button accesskey="s">Stress reliever</button>

<a href="https://www.w3schools.com/html/" accesskey="h">HTML</a><br>
<a href="https://www.w3schools.com/css/" accesskey="c">CSS</a>


<script type="text/javascript">
UnitTest = {
ruleIds: ["element_accesskey_labelled"],
results: [

]
}
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en-us" xml:lang="en-us">
<head>
<title>word spacing</title>
</head>
<body role="main" id="ko2ru00253">
<fieldset>
<legend accesskey=p>
<label>I want <input name=pizza type=number step=1 value=1 min=0>
pizza(s) with these toppings</label>
</legend>
<label><input name=pizza-cheese type=checkbox checked> Cheese</label>
<label><input name=pizza-ham type=checkbox checked> Ham</label>
<label><input name=pizza-pineapple type=checkbox> Pineapple</label>
</fieldset>

<script type="text/javascript">
UnitTest = {
ruleIds: ["element_accesskey_labelled"],
results: [

]
}
</script>
</body>
</html>

0 comments on commit 4be30c1

Please sign in to comment.