@uuv/a11y 1.0.0-beta.7 → 1.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/LICENSE +1 -1
- package/bundle/uuv-a11y.bundle.js +2 -2
- package/dist/CHANGELOG.md +15 -0
- package/dist/lib/engine/engine.js +17 -7
- package/dist/lib/model/index.d.ts +1 -1
- package/dist/lib/model/index.js +1 -1
- package/dist/lib/query/compliant-attributes/attribut-specification.d.ts +15 -2
- package/dist/lib/query/compliant-attributes/attribut-specification.js +38 -1
- package/dist/lib/query/compliant-attributes/attribute-checker.d.ts +12 -0
- package/dist/lib/query/compliant-attributes/attribute-checker.js +18 -0
- package/dist/lib/reference/rgaa/coverage/coverage-statement.json +19 -1
- package/dist/lib/reference/rgaa/rules/1-image.js +2 -2
- package/dist/lib/reference/rgaa/rules/5-table.d.ts +12 -1
- package/dist/lib/reference/rgaa/rules/5-table.js +292 -1
- package/dist/lib/reference/rgaa/selector-helper.d.ts +36 -0
- package/dist/lib/reference/rgaa/selector-helper.js +73 -1
- package/dist/package.json +4 -2
- package/package.json +4 -2
package/dist/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [1.0.0-beta.8](https://github.com/Orange-OpenSource/uuv/compare/a11y-v1.0.0-beta.7...a11y-v1.0.0-beta.8) (2024-02-13)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **a11y:** add rgaa criteria 5.1 to 5.5, [#451](https://github.com/Orange-OpenSource/uuv/issues/451) ([fa3b369](https://github.com/Orange-OpenSource/uuv/commit/fa3b3693e102b54e1d72321aba573e66c864f4e3))
|
|
7
|
+
* **a11y:** add rgaa criteria 5.6 to 5.8, [#451](https://github.com/Orange-OpenSource/uuv/issues/451) ([9cdf4ad](https://github.com/Orange-OpenSource/uuv/commit/9cdf4ad0e5ba16d7b8ddb90e014d96fc741acc17))
|
|
8
|
+
|
|
9
|
+
## [1.0.0-beta.7](https://github.com/Orange-OpenSource/uuv/compare/a11y-v1.0.0-beta.6...a11y-v1.0.0-beta.7) (2024-01-24)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* **a11y:** add rgaa topic 2, [#432](https://github.com/Orange-OpenSource/uuv/issues/432) ([#450](https://github.com/Orange-OpenSource/uuv/issues/450)) ([231d95b](https://github.com/Orange-OpenSource/uuv/commit/231d95b48ecfbeb0c86f44d0de0ca8c68df7b9f1))
|
|
15
|
+
|
|
1
16
|
## [1.0.0-beta.6](https://github.com/Orange-OpenSource/uuv/compare/a11y-v1.0.0-beta.5...a11y-v1.0.0-beta.6) (2024-01-23)
|
|
2
17
|
|
|
3
18
|
|
|
@@ -67,22 +67,32 @@ class Engine {
|
|
|
67
67
|
const a11YRuleResult = new model_1.A11yRuleResult(this.targetUrl, rule);
|
|
68
68
|
const validation = a11YRuleResult.getOrAddValidation(rule.criterion);
|
|
69
69
|
validation.status = model_1.A11yResultStatus.SUCCESS;
|
|
70
|
-
for (
|
|
71
|
-
const domNode =
|
|
70
|
+
for (const element of queryResults) {
|
|
71
|
+
const domNode = element.domNode;
|
|
72
72
|
const selector = this.getSelector(domNode);
|
|
73
73
|
const attributesToCheck = [];
|
|
74
74
|
rule.attributes.forEach((attribute) => {
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
|
|
75
|
+
const childData = attribute.split(":");
|
|
76
|
+
if (childData.length > 1) {
|
|
77
|
+
domNode.childNodes.forEach((childNode) => {
|
|
78
|
+
if (childNode.nodeName === childData[1]) {
|
|
79
|
+
attributesToCheck.push(`${attribute}=${childNode.textContent}`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const attributeFilledWithInformation = domNode.getAttribute(attribute);
|
|
85
|
+
if (attributeFilledWithInformation) {
|
|
86
|
+
attributesToCheck.push(`${attribute}=${attributeFilledWithInformation}`);
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
89
|
});
|
|
80
90
|
const manualValidation = a11YRuleResult.getOrAddValidation(rule.criterion);
|
|
81
91
|
manualValidation.status = model_1.A11yResultStatus.MANUAL;
|
|
82
92
|
manualValidation.nodesToCheckManually.push({
|
|
83
|
-
node:
|
|
93
|
+
node: element,
|
|
84
94
|
selector: selector,
|
|
85
|
-
attributes: attributesToCheck.
|
|
95
|
+
attributes: attributesToCheck.join(", "),
|
|
86
96
|
html: domNode.outerHTML,
|
|
87
97
|
help: rule.id ? `${this.reference.getRuleUrl(rule.id)}` : ""
|
|
88
98
|
});
|
package/dist/lib/model/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./checker"), exports);
|
|
18
17
|
__exportStar(require("./reference"), exports);
|
|
18
|
+
__exportStar(require("./checker"), exports);
|
|
19
19
|
__exportStar(require("./result"), exports);
|
|
20
20
|
__exportStar(require("./rule"), exports);
|
|
@@ -1,12 +1,25 @@
|
|
|
1
|
+
export interface IAttributeSpecification {
|
|
2
|
+
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
3
|
+
}
|
|
1
4
|
export declare class EmptyAttributeSpecification implements IAttributeSpecification {
|
|
2
5
|
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
3
6
|
}
|
|
4
7
|
export declare class NotEmptyAttributeSpecification implements IAttributeSpecification {
|
|
5
8
|
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
6
9
|
}
|
|
7
|
-
export
|
|
10
|
+
export declare class EmptyElementWithIdSpecification implements IAttributeSpecification {
|
|
8
11
|
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
9
12
|
}
|
|
10
|
-
export declare class
|
|
13
|
+
export declare class NotUniqueIdAttributeSpecification implements IAttributeSpecification {
|
|
14
|
+
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare class NotEqualsAttributeSpecification implements IAttributeSpecification {
|
|
17
|
+
private expectedValueList;
|
|
18
|
+
constructor(expectedValueList: string[]);
|
|
19
|
+
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class EqualsAttributeSpecification implements IAttributeSpecification {
|
|
22
|
+
private expectedValueList;
|
|
23
|
+
constructor(expectedValueList: string[]);
|
|
11
24
|
isSatisfiedBy(element: HTMLElement, attributeName: string): boolean;
|
|
12
25
|
}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.EmptyElementWithIdSpecification = exports.NotEmptyAttributeSpecification = exports.EmptyAttributeSpecification = void 0;
|
|
6
|
+
exports.EqualsAttributeSpecification = exports.NotEqualsAttributeSpecification = exports.NotUniqueIdAttributeSpecification = exports.EmptyElementWithIdSpecification = exports.NotEmptyAttributeSpecification = exports.EmptyAttributeSpecification = void 0;
|
|
7
7
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
8
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
9
9
|
const $ = require("jquery/dist/jquery.min");
|
|
@@ -32,3 +32,40 @@ class EmptyElementWithIdSpecification {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
exports.EmptyElementWithIdSpecification = EmptyElementWithIdSpecification;
|
|
35
|
+
class NotUniqueIdAttributeSpecification {
|
|
36
|
+
isSatisfiedBy(element, attributeName) {
|
|
37
|
+
if (lodash_1.default.isNull(element.id) || lodash_1.default.isEmpty(element.id)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return $(`[id=${element.id}]`).length > 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.NotUniqueIdAttributeSpecification = NotUniqueIdAttributeSpecification;
|
|
44
|
+
class NotEqualsAttributeSpecification {
|
|
45
|
+
expectedValueList;
|
|
46
|
+
constructor(expectedValueList) {
|
|
47
|
+
this.expectedValueList = expectedValueList;
|
|
48
|
+
}
|
|
49
|
+
isSatisfiedBy(element, attributeName) {
|
|
50
|
+
const attributeValue = element.getAttribute(attributeName);
|
|
51
|
+
if (lodash_1.default.isNull(attributeValue)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return !this.expectedValueList.includes(attributeValue);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.NotEqualsAttributeSpecification = NotEqualsAttributeSpecification;
|
|
58
|
+
class EqualsAttributeSpecification {
|
|
59
|
+
expectedValueList;
|
|
60
|
+
constructor(expectedValueList) {
|
|
61
|
+
this.expectedValueList = expectedValueList;
|
|
62
|
+
}
|
|
63
|
+
isSatisfiedBy(element, attributeName) {
|
|
64
|
+
const attributeValue = element.getAttribute(attributeName);
|
|
65
|
+
if (lodash_1.default.isNull(attributeValue)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return this.expectedValueList.includes(attributeValue);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.EqualsAttributeSpecification = EqualsAttributeSpecification;
|
|
@@ -20,4 +20,16 @@ export declare class AttributeChecker {
|
|
|
20
20
|
* @param attributeName : HTMLElement attribute name
|
|
21
21
|
*/
|
|
22
22
|
static emptyHtmlNodeTargetedByTheAttribute(attributeName: string): CompliantSpecification;
|
|
23
|
+
/**
|
|
24
|
+
* Check that id attribute is not unique in current page
|
|
25
|
+
*/
|
|
26
|
+
static notUniqueId(): CompliantSpecification;
|
|
27
|
+
/**
|
|
28
|
+
* Check that html element does not have the expected value
|
|
29
|
+
*/
|
|
30
|
+
static notEquals(attributeName: string, expectedValueList: string[]): CompliantSpecification;
|
|
31
|
+
/**
|
|
32
|
+
* Check that html element have the expected value
|
|
33
|
+
*/
|
|
34
|
+
static equals(attributeName: string, expectedValueList: string[]): CompliantSpecification;
|
|
23
35
|
}
|
|
@@ -33,5 +33,23 @@ class AttributeChecker {
|
|
|
33
33
|
static emptyHtmlNodeTargetedByTheAttribute(attributeName) {
|
|
34
34
|
return new CompliantSpecification(attributeName, new attribut_specification_1.EmptyElementWithIdSpecification());
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Check that id attribute is not unique in current page
|
|
38
|
+
*/
|
|
39
|
+
static notUniqueId() {
|
|
40
|
+
return new CompliantSpecification("id", new attribut_specification_1.NotUniqueIdAttributeSpecification());
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check that html element does not have the expected value
|
|
44
|
+
*/
|
|
45
|
+
static notEquals(attributeName, expectedValueList) {
|
|
46
|
+
return new CompliantSpecification(attributeName, new attribut_specification_1.NotEqualsAttributeSpecification(expectedValueList));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check that html element have the expected value
|
|
50
|
+
*/
|
|
51
|
+
static equals(attributeName, expectedValueList) {
|
|
52
|
+
return new CompliantSpecification(attributeName, new attribut_specification_1.EqualsAttributeSpecification(expectedValueList));
|
|
53
|
+
}
|
|
36
54
|
}
|
|
37
55
|
exports.AttributeChecker = AttributeChecker;
|
|
@@ -6,7 +6,22 @@
|
|
|
6
6
|
"1.1.5",
|
|
7
7
|
"1.1.6",
|
|
8
8
|
"2.1.1",
|
|
9
|
-
"2.2.1"
|
|
9
|
+
"2.2.1",
|
|
10
|
+
"5.1.1",
|
|
11
|
+
"5.2.1",
|
|
12
|
+
"5.3.1",
|
|
13
|
+
"5.4.1",
|
|
14
|
+
"5.5.1",
|
|
15
|
+
"5.6.1",
|
|
16
|
+
"5.6.2",
|
|
17
|
+
"5.6.3",
|
|
18
|
+
"5.6.4",
|
|
19
|
+
"5.7.1",
|
|
20
|
+
"5.7.2",
|
|
21
|
+
"5.7.3",
|
|
22
|
+
"5.7.4",
|
|
23
|
+
"5.7.5",
|
|
24
|
+
"5.8.1"
|
|
10
25
|
],
|
|
11
26
|
"inProgress": [
|
|
12
27
|
"1.1.4",
|
|
@@ -87,6 +102,9 @@
|
|
|
87
102
|
},
|
|
88
103
|
"1.2.6": {
|
|
89
104
|
"warning": "Pour les contrôles concernant les alternatives textuelles, nous utilisons la librairie [dom-accessibility-api](https://www.npmjs.com/package/dom-accessibility-api) qui implémente les [règles du W3C](https://w3c.github.io/accname/)"
|
|
105
|
+
},
|
|
106
|
+
"5": {
|
|
107
|
+
"warning": "Nous considérons qu'un *tableau de mise en forme* est une balise *table* possédant le rôle *presentation* \\\n Nous considérons qu'un *tableau avec header* est une balise *table* ou un rôle table possédant une balise *th* ou un élément avec un rôle *rowheader* ou un élément avec un rôle *columnheader*"
|
|
90
108
|
}
|
|
91
109
|
}
|
|
92
110
|
}
|
|
@@ -99,7 +99,7 @@ exports.default = [
|
|
|
99
99
|
wcag: "1.1.1 A",
|
|
100
100
|
id: "1.1.6",
|
|
101
101
|
elementType: "object",
|
|
102
|
-
query: query_1.OperatorQuery.And(new query_1.AccessibleNameQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), true), new by_sibling_query_1.BySiblingQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), false, [
|
|
102
|
+
query: query_1.OperatorQuery.And(new query_1.AccessibleNameQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), true), new by_sibling_query_1.BySiblingQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), false, [selector_helper_1.siblingElement.button.SELECTOR, "a"])),
|
|
103
103
|
description: "object has no alternative text",
|
|
104
104
|
help: "domNode corresponds to the object node and linkedNodes corresponds to the nodes to be analysed"
|
|
105
105
|
}),
|
|
@@ -108,7 +108,7 @@ exports.default = [
|
|
|
108
108
|
wcag: "1.1.1 A",
|
|
109
109
|
id: "1.1.6",
|
|
110
110
|
elementType: "object",
|
|
111
|
-
query: query_1.OperatorQuery.And(new query_1.AccessibleNameQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), true), new by_sibling_query_1.BySiblingQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), true, [
|
|
111
|
+
query: query_1.OperatorQuery.And(new query_1.AccessibleNameQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), true), new by_sibling_query_1.BySiblingQuery(new query_1.ByTagQuery([`${selector_helper_1.informativeContent.object.SELECTOR}[role=img]`]), true, [selector_helper_1.siblingElement.button.SELECTOR, "a"])),
|
|
112
112
|
description: "object has sibling elements to check",
|
|
113
113
|
help: "domNode corresponds to the object node and linkedNodes corresponds to the nodes to be analysed"
|
|
114
114
|
}),
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { AutoCheckA11yRule } from "../../../model";
|
|
2
|
+
declare const _default: (AutoCheckA11yRule & {
|
|
3
|
+
attributes?: string[] | undefined;
|
|
4
|
+
criterion: string;
|
|
5
|
+
wcag: string;
|
|
6
|
+
id: string;
|
|
7
|
+
elementType: string;
|
|
8
|
+
query: import("../../../query").Query;
|
|
9
|
+
description?: string | undefined;
|
|
10
|
+
help?: string | string[] | undefined;
|
|
11
|
+
shouldNotExist?: boolean | undefined;
|
|
12
|
+
})[];
|
|
2
13
|
export default _default;
|
|
@@ -1,3 +1,294 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
3
|
+
const model_1 = require("../../../model");
|
|
4
|
+
const query_1 = require("../../../query");
|
|
5
|
+
const selector_helper_1 = require("../selector-helper");
|
|
6
|
+
exports.default = [
|
|
7
|
+
model_1.AutoCheckA11yRule.from({
|
|
8
|
+
criterion: "5.1",
|
|
9
|
+
wcag: "1.3.1 A",
|
|
10
|
+
id: "5.1.1",
|
|
11
|
+
elementType: "table",
|
|
12
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery(selector_helper_1.table.noCaption.buildSelector(selector_helper_1.table.selector.complex, "")), [
|
|
13
|
+
query_1.AttributeChecker.emptyAttribute("summary"),
|
|
14
|
+
query_1.AttributeChecker.emptyHtmlNodeTargetedByTheAttribute("aria-describedby"),
|
|
15
|
+
]),
|
|
16
|
+
description: "Table tag without summary",
|
|
17
|
+
help: "set a summary to table"
|
|
18
|
+
}),
|
|
19
|
+
model_1.AutoCheckA11yRule.from({
|
|
20
|
+
criterion: "5.1",
|
|
21
|
+
wcag: "1.3.1 A",
|
|
22
|
+
id: "5.1.1",
|
|
23
|
+
elementType: "table",
|
|
24
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery(selector_helper_1.table.noCaption.buildSelector(selector_helper_1.table.hasComplexHeader.buildSelector([], "table"), "")), [
|
|
25
|
+
query_1.AttributeChecker.emptyHtmlNodeTargetedByTheAttribute("aria-describedby"),
|
|
26
|
+
]),
|
|
27
|
+
description: "Element with role table without summary",
|
|
28
|
+
help: "set a summary to table"
|
|
29
|
+
}),
|
|
30
|
+
model_1.ManualCheckA11yRule.from({
|
|
31
|
+
criterion: "5.2",
|
|
32
|
+
wcag: "1.3.1 A",
|
|
33
|
+
id: "5.2.1",
|
|
34
|
+
elementType: "table",
|
|
35
|
+
query: new query_1.ByTagQuery([
|
|
36
|
+
...selector_helper_1.table.caption.buildSelector(selector_helper_1.table.selector.complex, ""),
|
|
37
|
+
...selector_helper_1.table.summary.buildSelector(selector_helper_1.table.selector.complex, ""),
|
|
38
|
+
...selector_helper_1.table.ariaDescribedBy.buildSelector(selector_helper_1.table.selector.complex, ""),
|
|
39
|
+
...selector_helper_1.table.caption.buildSelector(selector_helper_1.table.hasComplexHeader.buildSelector([], "table"), ""),
|
|
40
|
+
...selector_helper_1.table.summary.buildSelector(selector_helper_1.table.hasComplexHeader.buildSelector([], "table"), ""),
|
|
41
|
+
...selector_helper_1.table.ariaDescribedBy.buildSelector(selector_helper_1.table.hasComplexHeader.buildSelector([], "table"), "")
|
|
42
|
+
]),
|
|
43
|
+
description: "if present, attributes summary(before html5), aria-describedby or child tag <caption> must be relevant",
|
|
44
|
+
attributes: [
|
|
45
|
+
"summary",
|
|
46
|
+
"aria-describedby",
|
|
47
|
+
"child:caption"
|
|
48
|
+
],
|
|
49
|
+
help: "adapt these attributes to be relevant"
|
|
50
|
+
}),
|
|
51
|
+
model_1.ManualCheckA11yRule.from({
|
|
52
|
+
criterion: "5.3",
|
|
53
|
+
wcag: "1.3.2 A, 4.1.2 A",
|
|
54
|
+
id: "5.3.1",
|
|
55
|
+
elementType: "table",
|
|
56
|
+
query: new query_1.ByTagQuery([
|
|
57
|
+
selector_helper_1.table.selector.withFormatting,
|
|
58
|
+
]),
|
|
59
|
+
description: "table with presentation role must have revelant content",
|
|
60
|
+
attributes: [
|
|
61
|
+
"child:td"
|
|
62
|
+
],
|
|
63
|
+
help: "adapt these <td> children content to be relevant"
|
|
64
|
+
}),
|
|
65
|
+
model_1.AutoCheckA11yRule.from({
|
|
66
|
+
criterion: "5.4",
|
|
67
|
+
wcag: "1.3.1 A",
|
|
68
|
+
id: "5.4.1",
|
|
69
|
+
elementType: "table",
|
|
70
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
71
|
+
...selector_helper_1.table.noCaption.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
72
|
+
]), [
|
|
73
|
+
query_1.AttributeChecker.emptyAttribute("title"),
|
|
74
|
+
query_1.AttributeChecker.emptyAttribute("aria-label"),
|
|
75
|
+
query_1.AttributeChecker.emptyHtmlNodeTargetedByTheAttribute("aria-labelledby"),
|
|
76
|
+
]),
|
|
77
|
+
description: "table must have title",
|
|
78
|
+
attributes: [
|
|
79
|
+
"child:caption",
|
|
80
|
+
"title",
|
|
81
|
+
"aria-label",
|
|
82
|
+
"aria-labelledby"
|
|
83
|
+
],
|
|
84
|
+
help: "set one of these attributes"
|
|
85
|
+
}),
|
|
86
|
+
model_1.ManualCheckA11yRule.from({
|
|
87
|
+
criterion: "5.5",
|
|
88
|
+
wcag: "1.3.1 A",
|
|
89
|
+
id: "5.5.1",
|
|
90
|
+
elementType: "table",
|
|
91
|
+
query: new query_1.ByTagQuery([
|
|
92
|
+
...selector_helper_1.table.caption.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
93
|
+
...selector_helper_1.table.title.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
94
|
+
...selector_helper_1.table.ariaLabel.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
95
|
+
...selector_helper_1.table.ariaLabelledBy.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
96
|
+
]),
|
|
97
|
+
description: "table must have a revelant title",
|
|
98
|
+
attributes: [
|
|
99
|
+
"child:caption",
|
|
100
|
+
"title",
|
|
101
|
+
"aria-label",
|
|
102
|
+
"aria-labelledby"
|
|
103
|
+
],
|
|
104
|
+
help: "adapt one of these attributes to be revelant"
|
|
105
|
+
}),
|
|
106
|
+
model_1.ManualCheckA11yRule.from({
|
|
107
|
+
criterion: "5.6",
|
|
108
|
+
wcag: "1.3.1 A",
|
|
109
|
+
id: "5.6.1",
|
|
110
|
+
elementType: "table",
|
|
111
|
+
query: new query_1.ByTagQuery([
|
|
112
|
+
selector_helper_1.table.selector.withData, "[role=table]"
|
|
113
|
+
]),
|
|
114
|
+
description: "table column header must be set properly",
|
|
115
|
+
help: "each table column header must be set with tag <th> or role=\"columnheader\""
|
|
116
|
+
}),
|
|
117
|
+
model_1.ManualCheckA11yRule.from({
|
|
118
|
+
criterion: "5.6",
|
|
119
|
+
wcag: "1.3.1 A",
|
|
120
|
+
id: "5.6.2",
|
|
121
|
+
elementType: "table",
|
|
122
|
+
query: new query_1.ByTagQuery([
|
|
123
|
+
selector_helper_1.table.selector.withData, "[role=table]"
|
|
124
|
+
]),
|
|
125
|
+
description: "table line header must be set properly",
|
|
126
|
+
help: "each table line header must be set with tag <th> or role=\"rowheader\""
|
|
127
|
+
}),
|
|
128
|
+
model_1.ManualCheckA11yRule.from({
|
|
129
|
+
criterion: "5.6",
|
|
130
|
+
wcag: "1.3.1 A",
|
|
131
|
+
id: "5.6.3",
|
|
132
|
+
elementType: "table",
|
|
133
|
+
query: new query_1.ByTagQuery([
|
|
134
|
+
selector_helper_1.table.selector.withData, "[role=table]"
|
|
135
|
+
]),
|
|
136
|
+
description: "each header does not apply to the entire row or column must be set properly"
|
|
137
|
+
}),
|
|
138
|
+
model_1.ManualCheckA11yRule.from({
|
|
139
|
+
criterion: "5.6",
|
|
140
|
+
wcag: "1.3.1 A",
|
|
141
|
+
id: "5.6.4",
|
|
142
|
+
elementType: "table",
|
|
143
|
+
query: new query_1.ByTagQuery([
|
|
144
|
+
selector_helper_1.table.selector.withData, "[role=table]"
|
|
145
|
+
]),
|
|
146
|
+
description: "each cell associated with several headers structured using a <th> or <td> tag"
|
|
147
|
+
}),
|
|
148
|
+
model_1.AutoCheckA11yRule.from({
|
|
149
|
+
criterion: "5.7",
|
|
150
|
+
wcag: "1.3.1 A",
|
|
151
|
+
id: "5.7.1",
|
|
152
|
+
elementType: "table",
|
|
153
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
154
|
+
...selector_helper_1.table.thColumnHeader.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
155
|
+
]), [
|
|
156
|
+
query_1.AttributeChecker.notUniqueId(),
|
|
157
|
+
query_1.AttributeChecker.emptyAttribute("scope"),
|
|
158
|
+
query_1.AttributeChecker.notEquals("role", ["rowheader", "columnheader"])
|
|
159
|
+
]),
|
|
160
|
+
description: "Table column header attributes",
|
|
161
|
+
help: "Correctly set table column header attributes"
|
|
162
|
+
}),
|
|
163
|
+
model_1.AutoCheckA11yRule.from({
|
|
164
|
+
criterion: "5.7",
|
|
165
|
+
wcag: "1.3.1 A",
|
|
166
|
+
id: "5.7.1",
|
|
167
|
+
elementType: "table",
|
|
168
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
169
|
+
...selector_helper_1.table.thRowHeader.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
170
|
+
]), [
|
|
171
|
+
query_1.AttributeChecker.notUniqueId(),
|
|
172
|
+
query_1.AttributeChecker.emptyAttribute("scope"),
|
|
173
|
+
query_1.AttributeChecker.notEquals("role", ["rowheader", "columnheader"])
|
|
174
|
+
]),
|
|
175
|
+
description: "Table row header attributes",
|
|
176
|
+
help: "Correctly set table row header attributes"
|
|
177
|
+
}),
|
|
178
|
+
model_1.AutoCheckA11yRule.from({
|
|
179
|
+
criterion: "5.7",
|
|
180
|
+
wcag: "1.3.1 A",
|
|
181
|
+
id: "5.7.2",
|
|
182
|
+
elementType: "table",
|
|
183
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
184
|
+
...selector_helper_1.table.thColumnHeader.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
185
|
+
]), [
|
|
186
|
+
query_1.AttributeChecker.notEmptyAttribute("scope"),
|
|
187
|
+
query_1.AttributeChecker.notEquals("scope", ["col"])
|
|
188
|
+
]),
|
|
189
|
+
description: "Table column header scope attribute",
|
|
190
|
+
help: "Set table column header scope attribute to col"
|
|
191
|
+
}),
|
|
192
|
+
model_1.AutoCheckA11yRule.from({
|
|
193
|
+
criterion: "5.7",
|
|
194
|
+
wcag: "1.3.1 A",
|
|
195
|
+
id: "5.7.2",
|
|
196
|
+
elementType: "table",
|
|
197
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
198
|
+
...selector_helper_1.table.thRowHeader.buildSelector([selector_helper_1.table.selector.withData], "table"),
|
|
199
|
+
]), [
|
|
200
|
+
query_1.AttributeChecker.notEmptyAttribute("scope"),
|
|
201
|
+
query_1.AttributeChecker.notEquals("scope", ["row"])
|
|
202
|
+
]),
|
|
203
|
+
description: "Table row header scope attribute",
|
|
204
|
+
help: "Set table column header scope attribute to row"
|
|
205
|
+
}),
|
|
206
|
+
model_1.AutoCheckA11yRule.from({
|
|
207
|
+
criterion: "5.7",
|
|
208
|
+
wcag: "1.3.1 A",
|
|
209
|
+
id: "5.7.3",
|
|
210
|
+
elementType: "table",
|
|
211
|
+
query: new query_1.OrQuery([
|
|
212
|
+
new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
213
|
+
"table:not([role=presentation]) tr:not(:first-child) th:not(:first-child)"
|
|
214
|
+
]), [
|
|
215
|
+
query_1.AttributeChecker.notUniqueId()
|
|
216
|
+
]),
|
|
217
|
+
new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
218
|
+
"table:not([role=presentation]) tr:not(:first-child) th:not(:first-child)"
|
|
219
|
+
]), [
|
|
220
|
+
query_1.AttributeChecker.notEmptyAttribute("scope")
|
|
221
|
+
]),
|
|
222
|
+
new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
223
|
+
"table:not([role=presentation]) tr:not(:first-child) th:not(:first-child)"
|
|
224
|
+
]), [
|
|
225
|
+
query_1.AttributeChecker.equals("role", ["rowheader", "columnheader"])
|
|
226
|
+
])
|
|
227
|
+
]),
|
|
228
|
+
description: "each header does not apply to the entire row or column must be set properly"
|
|
229
|
+
}),
|
|
230
|
+
model_1.ManualCheckA11yRule.from({
|
|
231
|
+
criterion: "5.7",
|
|
232
|
+
wcag: "1.3.1 A",
|
|
233
|
+
id: "5.7.4",
|
|
234
|
+
elementType: "table",
|
|
235
|
+
query: new query_1.ByTagQuery([
|
|
236
|
+
selector_helper_1.table.selector.withData, "[role=table]"
|
|
237
|
+
]),
|
|
238
|
+
description: "Each cell associated with several headers, the header attribute must be filled in correctly.",
|
|
239
|
+
help: "Each cell associated with several headers, the header attribute must contain the list of ids referring to the target headers."
|
|
240
|
+
}),
|
|
241
|
+
model_1.AutoCheckA11yRule.from({
|
|
242
|
+
criterion: "5.7",
|
|
243
|
+
wcag: "1.3.1 A",
|
|
244
|
+
id: "5.7.5",
|
|
245
|
+
elementType: "table",
|
|
246
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
247
|
+
"table:not([role=presentation]) tr:first-child th",
|
|
248
|
+
"[role=table] tr:first-child th"
|
|
249
|
+
]), [
|
|
250
|
+
query_1.AttributeChecker.notEmptyAttribute("role"),
|
|
251
|
+
query_1.AttributeChecker.notEquals("role", ["columnheader"])
|
|
252
|
+
]),
|
|
253
|
+
description: "Table column header role attribute",
|
|
254
|
+
help: "Set table column header role attribute to 'columnheader'"
|
|
255
|
+
}),
|
|
256
|
+
model_1.AutoCheckA11yRule.from({
|
|
257
|
+
criterion: "5.7",
|
|
258
|
+
wcag: "1.3.1 A",
|
|
259
|
+
id: "5.7.5",
|
|
260
|
+
elementType: "table",
|
|
261
|
+
query: new query_1.CompliantAttributesQuery(new query_1.ByTagQuery([
|
|
262
|
+
"table:not([role=presentation]) tr:not(:first-child) th:first-child",
|
|
263
|
+
"[role=table] tr:not(:first-child) th:first-child"
|
|
264
|
+
]), [
|
|
265
|
+
query_1.AttributeChecker.notEmptyAttribute("role"),
|
|
266
|
+
query_1.AttributeChecker.notEquals("role", ["rowheader"])
|
|
267
|
+
]),
|
|
268
|
+
description: "Table row header role attribute",
|
|
269
|
+
help: "Set table row header role attribute to 'rowheader'"
|
|
270
|
+
}),
|
|
271
|
+
model_1.AutoCheckA11yRule.from({
|
|
272
|
+
criterion: "5.8",
|
|
273
|
+
wcag: "1.3.1 A",
|
|
274
|
+
id: "5.8.1",
|
|
275
|
+
elementType: "table",
|
|
276
|
+
query: new query_1.ByTagQuery([
|
|
277
|
+
selector_helper_1.table.selector.withFormatting + "[summary]",
|
|
278
|
+
selector_helper_1.table.selector.withFormatting + ":has(caption)",
|
|
279
|
+
selector_helper_1.table.selector.withFormatting + ":has(thead)",
|
|
280
|
+
selector_helper_1.table.selector.withFormatting + ":has(th)",
|
|
281
|
+
selector_helper_1.table.selector.withFormatting + ":has(tfoot)",
|
|
282
|
+
selector_helper_1.table.selector.withFormatting + " :has(> [role=rowheader])",
|
|
283
|
+
selector_helper_1.table.selector.withFormatting + " :has(> [role=columnheader])",
|
|
284
|
+
selector_helper_1.table.selector.withFormatting + " td[scope]",
|
|
285
|
+
selector_helper_1.table.selector.withFormatting + " td[headers]",
|
|
286
|
+
selector_helper_1.table.selector.withFormatting + " td[axis]",
|
|
287
|
+
]),
|
|
288
|
+
description: "table with presentation role must have revelant content",
|
|
289
|
+
attributes: [
|
|
290
|
+
"child:td"
|
|
291
|
+
],
|
|
292
|
+
help: "adapt these <td> children content to be relevant"
|
|
293
|
+
})
|
|
294
|
+
];
|
|
@@ -17,4 +17,40 @@ export declare const siblingElement: {
|
|
|
17
17
|
button: SelectorRule;
|
|
18
18
|
a: SelectorRule;
|
|
19
19
|
};
|
|
20
|
+
/** CSS SELECTOR HELPER *******/
|
|
21
|
+
declare class CssSelectorRule {
|
|
22
|
+
readonly attributes: string[];
|
|
23
|
+
readonly selector: string;
|
|
24
|
+
readonly role: string;
|
|
25
|
+
constructor(attributes: string[], selector?: string, role?: string);
|
|
26
|
+
buildSelector(overrideSelector?: string[], overrideRole?: string | undefined): string[];
|
|
27
|
+
}
|
|
28
|
+
export declare const commonCssSelector: {
|
|
29
|
+
ariaDescribedBy: string[];
|
|
30
|
+
title: string[];
|
|
31
|
+
ariaLabelledBy: string[];
|
|
32
|
+
ariaLabel: string[];
|
|
33
|
+
};
|
|
34
|
+
export declare const tableCssSelector: {
|
|
35
|
+
complexTh: string[];
|
|
36
|
+
complexRowHeader: string[];
|
|
37
|
+
complexColumnHeader: string[];
|
|
38
|
+
};
|
|
39
|
+
export declare const table: {
|
|
40
|
+
selector: {
|
|
41
|
+
withFormatting: string;
|
|
42
|
+
complex: string[];
|
|
43
|
+
withData: string;
|
|
44
|
+
};
|
|
45
|
+
noCaption: CssSelectorRule;
|
|
46
|
+
caption: CssSelectorRule;
|
|
47
|
+
summary: CssSelectorRule;
|
|
48
|
+
ariaDescribedBy: CssSelectorRule;
|
|
49
|
+
title: CssSelectorRule;
|
|
50
|
+
ariaLabel: CssSelectorRule;
|
|
51
|
+
ariaLabelledBy: CssSelectorRule;
|
|
52
|
+
thColumnHeader: CssSelectorRule;
|
|
53
|
+
thRowHeader: CssSelectorRule;
|
|
54
|
+
hasComplexHeader: CssSelectorRule;
|
|
55
|
+
};
|
|
20
56
|
export {};
|