govuk_publishing_components 21.16.3 → 21.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/component_guide/accessibility-test.js +0 -6
- data/app/views/govuk_publishing_components/components/docs/machine_readable_metadata.yml +7 -0
- data/lib/govuk_publishing_components/presenters/machine_readable/dataset_schema.rb +34 -0
- data/lib/govuk_publishing_components/presenters/schema_org.rb +3 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/axe-core/CHANGELOG.md +166 -2
- data/node_modules/axe-core/CONTRIBUTING.md +5 -5
- data/node_modules/axe-core/README.md +4 -4
- data/node_modules/axe-core/axe.d.ts +27 -11
- data/node_modules/axe-core/axe.js +9597 -2431
- data/node_modules/axe-core/axe.min.js +2 -2
- data/node_modules/axe-core/bower.json +1 -1
- data/node_modules/axe-core/doc/API.md +211 -128
- data/node_modules/axe-core/doc/accessibility-supported.md +1 -1
- data/node_modules/axe-core/doc/aria-supported.md +4 -13
- data/node_modules/axe-core/doc/backwards-compatibility-doc.md +93 -0
- data/node_modules/axe-core/doc/code-submission-guidelines.md +4 -4
- data/node_modules/axe-core/doc/developer-guide.md +27 -13
- data/node_modules/axe-core/doc/examples/chrome-debugging-protocol/package.json +5 -2
- data/node_modules/axe-core/doc/examples/jasmine/README.md +3 -5
- data/node_modules/axe-core/doc/examples/jasmine/karma.conf.js +29 -0
- data/node_modules/axe-core/doc/examples/jasmine/package.json +6 -5
- data/node_modules/axe-core/doc/examples/jest_react/README.md +1 -1
- data/node_modules/axe-core/doc/examples/jest_react/link.test.js +3 -3
- data/node_modules/axe-core/doc/examples/jest_react/package.json +9 -9
- data/node_modules/axe-core/doc/examples/jsdom/package.json +15 -0
- data/node_modules/axe-core/doc/examples/mocha/README.md +5 -7
- data/node_modules/axe-core/doc/examples/mocha/karma.conf.js +29 -0
- data/node_modules/axe-core/doc/examples/mocha/package.json +7 -6
- data/node_modules/axe-core/doc/examples/phantomjs/README.md +3 -3
- data/node_modules/axe-core/doc/examples/phantomjs/axe-phantom.js +4 -2
- data/node_modules/axe-core/doc/examples/phantomjs/package.json +3 -3
- data/node_modules/axe-core/doc/examples/puppeteer/package.json +5 -2
- data/node_modules/axe-core/doc/examples/qunit/README.md +2 -2
- data/node_modules/axe-core/doc/examples/qunit/package.json +2 -2
- data/node_modules/axe-core/doc/examples/test-examples.js +32 -0
- data/node_modules/axe-core/doc/plugins.md +10 -10
- data/node_modules/axe-core/doc/projects.md +12 -8
- data/node_modules/axe-core/doc/rule-descriptions.md +87 -79
- data/node_modules/axe-core/doc/rule-development.md +30 -2
- data/node_modules/axe-core/lib/checks/aria/allowed-attr.js +1 -1
- data/node_modules/axe-core/lib/checks/aria/aria-roledescription.js +14 -0
- data/node_modules/axe-core/lib/checks/aria/aria-roledescription.json +23 -0
- data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.js +21 -0
- data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.json +11 -0
- data/node_modules/axe-core/lib/checks/aria/required-attr.js +32 -7
- data/node_modules/axe-core/lib/checks/aria/required-children.js +40 -14
- data/node_modules/axe-core/lib/checks/aria/required-children.json +12 -1
- data/node_modules/axe-core/lib/checks/aria/required-parent.js +1 -1
- data/node_modules/axe-core/lib/checks/aria/unsupportedattr.js +1 -1
- data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +50 -17
- data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -1
- data/node_modules/axe-core/lib/checks/aria/valid-attr.js +1 -1
- data/node_modules/axe-core/lib/checks/color/color-contrast.js +2 -2
- data/node_modules/axe-core/lib/checks/forms/autocomplete-appropriate.js +4 -4
- data/node_modules/axe-core/lib/checks/forms/autocomplete-valid.js +1 -1
- data/node_modules/axe-core/lib/checks/forms/fieldset.json +1 -0
- data/node_modules/axe-core/lib/checks/forms/group-labelledby.json +1 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-content.js +16 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-content.json +11 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-element.js +12 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-element.json +11 -0
- data/node_modules/axe-core/lib/checks/keyboard/tabindex.js +6 -1
- data/node_modules/axe-core/lib/checks/label/alt-space-value.js +3 -2
- data/node_modules/axe-core/lib/checks/label/duplicate-img-label.js +18 -15
- data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.js +13 -3
- data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.json +4 -0
- data/node_modules/axe-core/lib/checks/label/multiple-label.js +22 -12
- data/node_modules/axe-core/lib/checks/label/multiple-label.json +1 -1
- data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique-after.js +23 -0
- data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.js +7 -0
- data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.json +12 -0
- data/node_modules/axe-core/lib/checks/lists/listitem.js +1 -0
- data/node_modules/axe-core/lib/checks/lists/listitem.json +1 -1
- data/node_modules/axe-core/lib/checks/lists/only-listitems.js +0 -4
- data/node_modules/axe-core/lib/checks/mobile/css-orientation-lock.js +8 -6
- data/node_modules/axe-core/lib/checks/navigation/region.js +2 -19
- data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.js +18 -0
- data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.json +11 -0
- data/node_modules/axe-core/lib/checks/shared/exists.js +1 -1
- data/node_modules/axe-core/lib/checks/shared/exists.json +1 -1
- data/node_modules/axe-core/lib/checks/shared/has-alt.js +6 -4
- data/node_modules/axe-core/lib/checks/shared/non-empty-alt.js +1 -1
- data/node_modules/axe-core/lib/checks/tables/caption-faked.json +1 -1
- data/node_modules/axe-core/lib/checks/tables/td-has-header.js +5 -4
- data/node_modules/axe-core/lib/checks/tables/th-has-data-cells.js +19 -29
- data/node_modules/axe-core/lib/commons/aria/get-owned-virtual.js +1 -1
- data/node_modules/axe-core/lib/commons/aria/index.js +50 -46
- data/node_modules/axe-core/lib/commons/aria/is-accessible-ref.js +41 -37
- data/node_modules/axe-core/lib/commons/aria/label-virtual.js +2 -2
- data/node_modules/axe-core/lib/commons/aria/roles.js +1 -1
- data/node_modules/axe-core/lib/commons/aria/validate-attr-value.js +0 -1
- data/node_modules/axe-core/lib/commons/color/center-point-of-rect.js +30 -0
- data/node_modules/axe-core/lib/commons/color/contrast.js +7 -1
- data/node_modules/axe-core/lib/commons/color/element-has-image.js +36 -0
- data/node_modules/axe-core/lib/commons/color/get-background-color.js +332 -306
- data/node_modules/axe-core/lib/commons/color/get-foreground-color.js +35 -6
- data/node_modules/axe-core/lib/commons/color/get-own-background-color.js +22 -0
- data/node_modules/axe-core/lib/commons/dom/find-up.js +5 -5
- data/node_modules/axe-core/lib/commons/dom/get-scroll-offset.js +0 -1
- data/node_modules/axe-core/lib/commons/dom/get-tabbable-elements.js +1 -1
- data/node_modules/axe-core/lib/commons/dom/has-content-virtual.js +7 -5
- data/node_modules/axe-core/lib/commons/dom/is-focusable.js +8 -5
- data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +15 -2
- data/node_modules/axe-core/lib/commons/dom/is-in-text-block.js +1 -2
- data/node_modules/axe-core/lib/commons/dom/is-skip-link.js +45 -0
- data/node_modules/axe-core/lib/commons/dom/is-visible.js +43 -17
- data/node_modules/axe-core/lib/commons/dom/is-visual-content.js +0 -1
- data/node_modules/axe-core/lib/commons/dom/visually-contains.js +0 -1
- data/node_modules/axe-core/lib/commons/dom/visually-overlaps.js +0 -1
- data/node_modules/axe-core/lib/commons/forms/index.js +8 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-combobox.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-listbox.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-range.js +14 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-textbox.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-native-select.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-native-textbox.js +28 -0
- data/node_modules/axe-core/lib/commons/table/get-cell-position.js +2 -2
- data/node_modules/axe-core/lib/commons/table/get-headers.js +55 -11
- data/node_modules/axe-core/lib/commons/table/get-scope.js +1 -1
- data/node_modules/axe-core/lib/commons/table/is-data-table.js +0 -1
- data/node_modules/axe-core/lib/commons/table/to-grid.js +2 -2
- data/node_modules/axe-core/lib/commons/text/accessible-text-virtual.js +5 -5
- data/node_modules/axe-core/lib/commons/text/form-control-value.js +18 -30
- data/node_modules/axe-core/lib/commons/text/is-icon-ligature.js +210 -0
- data/node_modules/axe-core/lib/commons/text/is-valid-autocomplete.js +0 -1
- data/node_modules/axe-core/lib/commons/text/label-text.js +1 -1
- data/node_modules/axe-core/lib/commons/text/label-virtual.js +1 -1
- data/node_modules/axe-core/lib/commons/text/native-text-methods.js +1 -1
- data/node_modules/axe-core/lib/commons/text/subtree-text.js +3 -3
- data/node_modules/axe-core/lib/commons/text/unicode.js +15 -0
- data/node_modules/axe-core/lib/commons/text/visible-text-nodes.js +25 -0
- data/node_modules/axe-core/lib/commons/text/visible-virtual.js +1 -1
- data/node_modules/axe-core/lib/core/base/audit.js +90 -15
- data/node_modules/axe-core/lib/core/base/cache.js +33 -0
- data/node_modules/axe-core/lib/core/base/check.js +48 -1
- data/node_modules/axe-core/lib/core/base/context.js +15 -14
- data/node_modules/axe-core/lib/core/base/rule.js +223 -46
- data/node_modules/axe-core/lib/core/base/virtual-node/abstract-virtual-node.js +40 -0
- data/node_modules/axe-core/lib/core/base/virtual-node/serial-virtual-node.js +86 -0
- data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +85 -0
- data/node_modules/axe-core/lib/core/constants.js +10 -2
- data/node_modules/axe-core/lib/core/imports/index.js +28 -3
- data/node_modules/axe-core/lib/core/index.js +2 -4
- data/node_modules/axe-core/lib/core/public/configure.js +28 -1
- data/node_modules/axe-core/lib/core/public/run-rules.js +2 -0
- data/node_modules/axe-core/lib/core/public/run-virtual-rule.js +50 -0
- data/node_modules/axe-core/lib/core/public/run.js +13 -2
- data/node_modules/axe-core/lib/core/reporters/helpers/process-aggregate.js +1 -1
- data/node_modules/axe-core/lib/core/reporters/na.js +4 -0
- data/node_modules/axe-core/lib/core/reporters/raw-env.js +12 -0
- data/node_modules/axe-core/lib/core/reporters/raw.js +25 -1
- data/node_modules/axe-core/lib/core/utils/are-styles-set.js +4 -7
- data/node_modules/axe-core/lib/core/utils/assert.js +12 -0
- data/node_modules/axe-core/lib/core/utils/collect-results-from-frames.js +2 -2
- data/node_modules/axe-core/lib/core/utils/contains.js +27 -13
- data/node_modules/axe-core/lib/core/utils/css-parser.js +2 -0
- data/node_modules/axe-core/lib/core/utils/element-matches.js +5 -1
- data/node_modules/axe-core/lib/core/utils/escape-selector.js +1 -2
- data/node_modules/axe-core/lib/core/utils/flattened-tree.js +47 -61
- data/node_modules/axe-core/lib/core/utils/get-check-option.js +0 -1
- data/node_modules/axe-core/lib/core/utils/get-friendly-uri-end.js +1 -1
- data/node_modules/axe-core/lib/core/utils/get-node-attributes.js +21 -0
- data/node_modules/axe-core/lib/core/utils/get-scroll.js +39 -0
- data/node_modules/axe-core/lib/core/utils/get-selector.js +9 -6
- data/node_modules/axe-core/lib/core/utils/get-stylesheet-factory.js +51 -0
- data/node_modules/axe-core/lib/core/utils/get-xpath.js +0 -1
- data/node_modules/axe-core/lib/core/utils/is-hidden.js +16 -4
- data/node_modules/axe-core/lib/core/utils/is-html-element.js +5 -5
- data/node_modules/axe-core/lib/core/utils/is-shadow-root.js +3 -3
- data/node_modules/axe-core/lib/core/utils/memoize.js +17 -0
- data/node_modules/axe-core/lib/core/utils/parse-crossorigin-stylesheet.js +53 -0
- data/node_modules/axe-core/lib/core/utils/parse-sameorigin-stylesheet.js +96 -0
- data/node_modules/axe-core/lib/core/utils/parse-stylesheet.js +70 -0
- data/node_modules/axe-core/lib/core/utils/performance-timer.js +7 -2
- data/node_modules/axe-core/lib/core/utils/preload-cssom.js +77 -281
- data/node_modules/axe-core/lib/core/utils/preload.js +49 -23
- data/node_modules/axe-core/lib/core/utils/qsa.js +39 -50
- data/node_modules/axe-core/lib/core/utils/respondable.js +20 -3
- data/node_modules/axe-core/lib/core/utils/rule-should-run.js +0 -1
- data/node_modules/axe-core/lib/core/utils/scroll-state.js +12 -25
- data/node_modules/axe-core/lib/core/utils/select.js +12 -23
- data/node_modules/axe-core/lib/core/utils/uuid.js +1 -2
- data/node_modules/axe-core/lib/intro.stub +1 -1
- data/node_modules/axe-core/lib/misc/incomplete-fallback.json +1 -1
- data/node_modules/axe-core/lib/rules/aria-allowed-attr-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/aria-form-field-name-matches.js +50 -0
- data/node_modules/axe-core/lib/rules/aria-has-attr-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +1 -1
- data/node_modules/axe-core/lib/rules/aria-input-field-name.json +13 -0
- data/node_modules/axe-core/lib/rules/aria-roledescription.json +12 -0
- data/node_modules/axe-core/lib/rules/aria-toggle-field-name.json +18 -0
- data/node_modules/axe-core/lib/rules/autocomplete-matches.js +14 -10
- data/node_modules/axe-core/lib/rules/avoid-inline-spacing.json +12 -0
- data/node_modules/axe-core/lib/rules/button-name.json +3 -5
- data/node_modules/axe-core/lib/rules/checkboxgroup.json +2 -1
- data/node_modules/axe-core/lib/rules/color-contrast-matches.js +37 -22
- data/node_modules/axe-core/lib/rules/duplicate-id-active-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/duplicate-id-misc-matches.js +2 -2
- data/node_modules/axe-core/lib/rules/form-field-multiple-labels.json +1 -1
- data/node_modules/axe-core/lib/rules/html-has-lang.json +1 -0
- data/node_modules/axe-core/lib/rules/image-alt.json +1 -1
- data/node_modules/axe-core/lib/rules/img-redundant-alt.json +3 -3
- data/node_modules/axe-core/lib/rules/input-button-name.json +26 -0
- data/node_modules/axe-core/lib/rules/landmark-unique-matches.js +41 -0
- data/node_modules/axe-core/lib/rules/landmark-unique.json +13 -0
- data/node_modules/axe-core/lib/rules/link-name.json +5 -4
- data/node_modules/axe-core/lib/rules/meta-refresh.json +8 -1
- data/node_modules/axe-core/lib/rules/radiogroup.json +2 -1
- data/node_modules/axe-core/lib/rules/role-img-alt.json +18 -0
- data/node_modules/axe-core/lib/rules/scrollable-region-focusable-matches.js +30 -0
- data/node_modules/axe-core/lib/rules/scrollable-region-focusable.json +12 -0
- data/node_modules/axe-core/lib/rules/skip-link-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/skip-link.json +1 -1
- data/node_modules/axe-core/lib/rules/video-description.json +3 -1
- data/node_modules/axe-core/locales/de.json +1 -5
- data/node_modules/axe-core/locales/es.json +773 -0
- data/node_modules/axe-core/locales/fr.json +15 -19
- data/node_modules/axe-core/locales/ja.json +65 -11
- data/node_modules/axe-core/locales/ko.json +777 -0
- data/node_modules/axe-core/locales/nl.json +35 -35
- data/node_modules/axe-core/locales/pt_BR.json +773 -0
- data/node_modules/axe-core/package.json +56 -52
- data/node_modules/axe-core/sri-history.json +20 -0
- data/node_modules/axe-core/typings/axe-core/axe-core-tests.ts +5 -15
- data/node_modules/govuk-frontend/package.json +10 -10
- metadata +62 -4
- data/node_modules/axe-core/doc/axelogo2018.png +0 -0
- data/node_modules/axe-core/lib/rules/role-not-button-matches.js +0 -1
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Before you start writing axe-core rules, be sure to create a proposal for them in a github issue. Read [Proposing Axe-core rules](./rule-proposal.md) for details.
|
4
4
|
|
5
|
-
A rule is a JSON Object that defines a test for
|
5
|
+
A rule is a JSON Object that defines a test for axe-core to run. At a high level, think of a rule as doing two things. First it finds all elements that it should test, and after that it runs a number of checks to see if those selected elements pass or fail the rule.
|
6
6
|
|
7
7
|
## Rule Select and Matches
|
8
8
|
|
@@ -33,7 +33,7 @@ The actual testing of elements in axe-core is done by checks. A rule has one or
|
|
33
33
|
| enabled | Does the rule run by default |
|
34
34
|
| tags | Grouping for the rule, such as wcag2a, best-practice |
|
35
35
|
| metadata.description | Description of what a rule does |
|
36
|
-
| metadata.help | Short description of a violation, used in the
|
36
|
+
| metadata.help | Short description of a violation, used in the axe extension sidebar |
|
37
37
|
|
38
38
|
## Check Properties
|
39
39
|
|
@@ -93,3 +93,31 @@ this.data({
|
|
93
93
|
missingData: 'bgImage'
|
94
94
|
});
|
95
95
|
```
|
96
|
+
|
97
|
+
# Hierarchical rules
|
98
|
+
|
99
|
+
Axe-core handles shadow DOM and cross-domain iframe rules very well - as long as you take care in the design and implementation of your rule. Any rule that evaluates the DOM hierarchy needs to think about what happens across shadow DOM boundaries and iframe boundaries.
|
100
|
+
|
101
|
+
The rule callbacks all receive both a `node` and a `virtualNode` argument (in addition to the `options` argument). `node` points to the DOM Node that is to be evaluated, whereas `virtualNode` points to the node in the flattened tree (the hierarchy that shadow DOM creates that is used for parent child relationships across shadow DOM boundaries).
|
102
|
+
|
103
|
+
If your rule looks at any hierarchical context (parents or children) then you need to operate on the `virtualNode` for those operations. Calls to `isHidden` and the accessible name calculation calls will all evaluate the hierarchy. The commons and utils functions will all fetch the `virtualNode` from the flattened tree if you use the `node` implementation (and then simply call the virtualNode one) and are there for backwards compatibility. This backwards compatibility comes at a performance cost that can be avoided by simply using the `virtualNode` to start with. We will ask you to change this during PR review, so you might as well just start out by using the `virtualNode`.
|
104
|
+
|
105
|
+
## iframes
|
106
|
+
|
107
|
+
Rules that evaluate the structure and/or the number of elements on the entire page (for example the heading nesting rule, or the landmark rules) will need to do this evaluation across iframe boundaries. What this means is that the check function instead of determining pass/fail/incomplete, performs a data gathering function. This data is then passed up the iframe hierarchy to the top window where it is passed into the `after` function, which does the evaluation of the gathered data and determines pass/fail.
|
108
|
+
|
109
|
+
## Rules of Thumb for Rule Code Reviewers
|
110
|
+
|
111
|
+
Rules of thumb for determining whether `virtualNode` should be used - if any of the answers to the following questions is yes, then it should use shadow DOM and `virtualNode`:
|
112
|
+
|
113
|
+
1. Is it using the non-virtualNode version of a function that wraps the flattened tree lookup?
|
114
|
+
2. Is it evaluating the DOM hierarchy?
|
115
|
+
|
116
|
+
Rules that do this MUST have shadow DOM test cases that show passes and fails across the shadow DOM boundary.
|
117
|
+
|
118
|
+
Rules of thumb for determining whether an `after` function is required - if any of the answers to the following questions is yes:
|
119
|
+
|
120
|
+
1. Does the rule evaluate number of things on a page?
|
121
|
+
2. Does the rule evaluate hierarchy of things across a whole page?
|
122
|
+
|
123
|
+
Rules that use an `after` function MUST have iframe test cases that assert correct data passing between iframes and handle all the relevant cases across iframes.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
options = options || {};
|
2
|
+
|
3
|
+
const role = axe.commons.aria.getRole(node);
|
4
|
+
const supportedRoles = options.supportedRoles || [];
|
5
|
+
|
6
|
+
if (supportedRoles.includes(role)) {
|
7
|
+
return true;
|
8
|
+
}
|
9
|
+
|
10
|
+
if (role && role !== 'presentation' && role !== 'none') {
|
11
|
+
return undefined;
|
12
|
+
}
|
13
|
+
|
14
|
+
return false;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{
|
2
|
+
"id": "aria-roledescription",
|
3
|
+
"evaluate": "aria-roledescription.js",
|
4
|
+
"options": {
|
5
|
+
"supportedRoles": [
|
6
|
+
"button",
|
7
|
+
"img",
|
8
|
+
"checkbox",
|
9
|
+
"radio",
|
10
|
+
"combobox",
|
11
|
+
"menuitemcheckbox",
|
12
|
+
"menuitemradio"
|
13
|
+
]
|
14
|
+
},
|
15
|
+
"metadata": {
|
16
|
+
"impact": "serious",
|
17
|
+
"messages": {
|
18
|
+
"pass": "aria-roledescription used on a supported semantic role",
|
19
|
+
"incomplete": "Check that the aria-roledescription is announced by supported screen readers",
|
20
|
+
"fail": "Give the element a role that supports aria-roledescription"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
const { aria, text } = axe.commons;
|
2
|
+
|
3
|
+
const role = aria.getRole(node, { noImplicit: true });
|
4
|
+
this.data(role);
|
5
|
+
|
6
|
+
const labelText = text.sanitize(text.labelText(virtualNode)).toLowerCase();
|
7
|
+
const accText = text.sanitize(text.accessibleText(node)).toLowerCase();
|
8
|
+
|
9
|
+
if (!accText && !labelText) {
|
10
|
+
return false;
|
11
|
+
}
|
12
|
+
|
13
|
+
if (!accText && labelText) {
|
14
|
+
return undefined;
|
15
|
+
}
|
16
|
+
|
17
|
+
if (!accText.includes(labelText)) {
|
18
|
+
return undefined;
|
19
|
+
}
|
20
|
+
|
21
|
+
return false;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
{
|
2
|
+
"id": "no-implicit-explicit-label",
|
3
|
+
"evaluate": "no-implicit-explicit-label.js",
|
4
|
+
"metadata": {
|
5
|
+
"impact": "moderate",
|
6
|
+
"messages": {
|
7
|
+
"pass": "There is no mismatch between a <label> and accessible name",
|
8
|
+
"incomplete": "Check that the <label> does not need be part of the ARIA {{=it.data}} field's name"
|
9
|
+
}
|
10
|
+
}
|
11
|
+
}
|
@@ -1,19 +1,44 @@
|
|
1
1
|
options = options || {};
|
2
2
|
|
3
|
-
|
3
|
+
const missing = [];
|
4
|
+
const {
|
5
|
+
isNativeTextbox,
|
6
|
+
isNativeSelect,
|
7
|
+
isAriaTextbox,
|
8
|
+
isAriaListbox,
|
9
|
+
isAriaCombobox,
|
10
|
+
isAriaRange
|
11
|
+
} = axe.commons.forms;
|
12
|
+
|
13
|
+
// aria-valuenow should fail if element does not have a value property
|
14
|
+
// @see https://github.com/dequelabs/axe-core/issues/1501
|
15
|
+
const preChecks = {
|
16
|
+
'aria-valuenow': function() {
|
17
|
+
return !(
|
18
|
+
isNativeTextbox(node) ||
|
19
|
+
isNativeSelect(node) ||
|
20
|
+
isAriaTextbox(node) ||
|
21
|
+
isAriaListbox(node) ||
|
22
|
+
isAriaCombobox(node) ||
|
23
|
+
(isAriaRange(node) && node.hasAttribute('aria-valuenow'))
|
24
|
+
);
|
25
|
+
}
|
26
|
+
};
|
4
27
|
|
5
28
|
if (node.hasAttributes()) {
|
6
|
-
|
7
|
-
|
8
|
-
required = axe.commons.aria.requiredAttr(role);
|
29
|
+
const role = node.getAttribute('role');
|
30
|
+
let required = axe.commons.aria.requiredAttr(role);
|
9
31
|
|
10
32
|
if (Array.isArray(options[role])) {
|
11
33
|
required = axe.utils.uniqueArray(options[role], required);
|
12
34
|
}
|
13
35
|
if (role && required) {
|
14
|
-
for (
|
15
|
-
attr = required[i];
|
16
|
-
if (
|
36
|
+
for (let i = 0, l = required.length; i < l; i++) {
|
37
|
+
const attr = required[i];
|
38
|
+
if (
|
39
|
+
!node.getAttribute(attr) &&
|
40
|
+
(preChecks[attr] ? preChecks[attr]() : true)
|
41
|
+
) {
|
17
42
|
missing.push(attr);
|
18
43
|
}
|
19
44
|
}
|
@@ -2,6 +2,7 @@ const requiredOwned = axe.commons.aria.requiredOwned;
|
|
2
2
|
const implicitNodes = axe.commons.aria.implicitNodes;
|
3
3
|
const matchesSelector = axe.utils.matchesSelector;
|
4
4
|
const idrefs = axe.commons.dom.idrefs;
|
5
|
+
const hasContentVirtual = axe.commons.dom.hasContentVirtual;
|
5
6
|
const reviewEmpty =
|
6
7
|
options && Array.isArray(options.reviewEmpty) ? options.reviewEmpty : [];
|
7
8
|
|
@@ -13,7 +14,9 @@ function owns(node, virtualTree, role, ariaOwned) {
|
|
13
14
|
selector = ['[role="' + role + '"]'];
|
14
15
|
|
15
16
|
if (implicit) {
|
16
|
-
selector = selector.concat(
|
17
|
+
selector = selector.concat(
|
18
|
+
implicit.map(implicitSelector => implicitSelector + ':not([role])')
|
19
|
+
);
|
17
20
|
}
|
18
21
|
|
19
22
|
selector = selector.join(',');
|
@@ -30,7 +33,7 @@ function ariaOwns(nodes, role) {
|
|
30
33
|
if (nodes[index] === null) {
|
31
34
|
continue;
|
32
35
|
}
|
33
|
-
|
36
|
+
const virtualTree = axe.utils.getNodeFromTree(nodes[index]);
|
34
37
|
if (owns(nodes[index], virtualTree, role, true)) {
|
35
38
|
return true;
|
36
39
|
}
|
@@ -39,34 +42,38 @@ function ariaOwns(nodes, role) {
|
|
39
42
|
}
|
40
43
|
|
41
44
|
function missingRequiredChildren(node, childRoles, all, role) {
|
42
|
-
|
43
|
-
|
44
|
-
l = childRoles.length,
|
45
|
+
var index,
|
46
|
+
length = childRoles.length,
|
45
47
|
missing = [],
|
46
48
|
ownedElements = idrefs(node, 'aria-owns');
|
47
49
|
|
48
|
-
for (
|
49
|
-
var
|
50
|
-
if (
|
50
|
+
for (index = 0; index < length; index++) {
|
51
|
+
var childRole = childRoles[index];
|
52
|
+
if (
|
53
|
+
owns(node, virtualNode, childRole) ||
|
54
|
+
ariaOwns(ownedElements, childRole)
|
55
|
+
) {
|
51
56
|
if (!all) {
|
52
57
|
return null;
|
53
58
|
}
|
54
59
|
} else {
|
55
60
|
if (all) {
|
56
|
-
missing.push(
|
61
|
+
missing.push(childRole);
|
57
62
|
}
|
58
63
|
}
|
59
64
|
}
|
60
65
|
|
61
66
|
// combobox exceptions
|
62
67
|
if (role === 'combobox') {
|
63
|
-
// remove 'textbox' from missing roles if combobox is a native text-type input
|
68
|
+
// remove 'textbox' from missing roles if combobox is a native text-type input or owns a 'searchbox'
|
64
69
|
var textboxIndex = missing.indexOf('textbox');
|
65
70
|
var textTypeInputs = ['text', 'search', 'email', 'url', 'tel'];
|
66
71
|
if (
|
67
|
-
textboxIndex >= 0 &&
|
68
|
-
|
69
|
-
|
72
|
+
(textboxIndex >= 0 &&
|
73
|
+
(node.nodeName.toUpperCase() === 'INPUT' &&
|
74
|
+
textTypeInputs.includes(node.type))) ||
|
75
|
+
(owns(node, virtualNode, 'searchbox') ||
|
76
|
+
ariaOwns(ownedElements, 'searchbox'))
|
70
77
|
) {
|
71
78
|
missing.splice(textboxIndex, 1);
|
72
79
|
}
|
@@ -88,6 +95,19 @@ function missingRequiredChildren(node, childRoles, all, role) {
|
|
88
95
|
return null;
|
89
96
|
}
|
90
97
|
|
98
|
+
function hasDecendantWithRole(node) {
|
99
|
+
return (
|
100
|
+
node.children &&
|
101
|
+
node.children.some(child => {
|
102
|
+
const role = axe.commons.aria.getRole(child);
|
103
|
+
return (
|
104
|
+
!['presentation', 'none', null].includes(role) ||
|
105
|
+
hasDecendantWithRole(child)
|
106
|
+
);
|
107
|
+
})
|
108
|
+
);
|
109
|
+
}
|
110
|
+
|
91
111
|
var role = node.getAttribute('role');
|
92
112
|
var required = requiredOwned(role);
|
93
113
|
|
@@ -110,7 +130,13 @@ if (!missing) {
|
|
110
130
|
|
111
131
|
this.data(missing);
|
112
132
|
|
113
|
-
|
133
|
+
// Only review empty nodes when a node is both empty and does not have an aria-owns relationship
|
134
|
+
if (
|
135
|
+
reviewEmpty.includes(role) &&
|
136
|
+
!hasContentVirtual(virtualNode, false, true) &&
|
137
|
+
!hasDecendantWithRole(virtualNode) &&
|
138
|
+
idrefs(node, 'aria-owns').length === 0
|
139
|
+
) {
|
114
140
|
return undefined;
|
115
141
|
} else {
|
116
142
|
return false;
|
@@ -2,7 +2,18 @@
|
|
2
2
|
"id": "aria-required-children",
|
3
3
|
"evaluate": "required-children.js",
|
4
4
|
"options": {
|
5
|
-
"reviewEmpty": [
|
5
|
+
"reviewEmpty": [
|
6
|
+
"doc-bibliography",
|
7
|
+
"doc-endnotes",
|
8
|
+
"grid",
|
9
|
+
"list",
|
10
|
+
"listbox",
|
11
|
+
"table",
|
12
|
+
"tablist",
|
13
|
+
"tree",
|
14
|
+
"treegrid",
|
15
|
+
"rowgroup"
|
16
|
+
]
|
6
17
|
},
|
7
18
|
"metadata": {
|
8
19
|
"impact": "critical",
|
@@ -73,7 +73,7 @@ var owners = getAriaOwners(node);
|
|
73
73
|
if (owners) {
|
74
74
|
for (var i = 0, l = owners.length; i < l; i++) {
|
75
75
|
missingParents = getMissingContext(
|
76
|
-
axe.utils.getNodeFromTree(
|
76
|
+
axe.utils.getNodeFromTree(owners[i]),
|
77
77
|
missingParents,
|
78
78
|
true
|
79
79
|
);
|
@@ -2,7 +2,7 @@ const nodeName = node.nodeName.toUpperCase();
|
|
2
2
|
const lookupTable = axe.commons.aria.lookupTable;
|
3
3
|
const role = axe.commons.aria.getRole(node);
|
4
4
|
|
5
|
-
const unsupportedAttrs = Array.from(node
|
5
|
+
const unsupportedAttrs = Array.from(axe.utils.getNodeAttributes(node))
|
6
6
|
.filter(({ name }) => {
|
7
7
|
const attribute = lookupTable.attributes[name];
|
8
8
|
|
@@ -1,29 +1,62 @@
|
|
1
1
|
options = Array.isArray(options) ? options : [];
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
const needsReview = [];
|
4
|
+
const invalid = [];
|
5
|
+
const aria = /^aria-/;
|
6
|
+
const attrs = axe.utils.getNodeAttributes(node);
|
5
7
|
|
6
|
-
|
7
|
-
attrName,
|
8
|
-
attrs = node.attributes;
|
8
|
+
const skipAttrs = ['aria-errormessage'];
|
9
9
|
|
10
|
-
|
10
|
+
const preChecks = {
|
11
|
+
// aria-controls should only check if element exists if the element
|
12
|
+
// doesn't have aria-expanded=false or aria-selected=false (tabs)
|
13
|
+
// @see https://github.com/dequelabs/axe-core/issues/1463
|
14
|
+
'aria-controls': function() {
|
15
|
+
return (
|
16
|
+
node.getAttribute('aria-expanded') !== 'false' &&
|
17
|
+
node.getAttribute('aria-selected') !== 'false'
|
18
|
+
);
|
19
|
+
},
|
20
|
+
// aria-owns should only check if element exists if the element
|
21
|
+
// doesn't have aria-expanded=false (combobox)
|
22
|
+
// @see https://github.com/dequelabs/axe-core/issues/1524
|
23
|
+
'aria-owns': function() {
|
24
|
+
return node.getAttribute('aria-expanded') !== 'false';
|
25
|
+
},
|
26
|
+
// aria-describedby should not mark missing element as violation but
|
27
|
+
// instead as needs review
|
28
|
+
// @see https://github.com/dequelabs/axe-core/issues/1151
|
29
|
+
'aria-describedby': function() {
|
30
|
+
if (!axe.commons.aria.validateAttrValue(node, 'aria-describedby')) {
|
31
|
+
needsReview.push(
|
32
|
+
`aria-describedby="${node.getAttribute('aria-describedby')}"`
|
33
|
+
);
|
34
|
+
}
|
35
|
+
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
};
|
11
39
|
|
12
|
-
for (
|
13
|
-
attr = attrs[i];
|
14
|
-
attrName = attr.name;
|
40
|
+
for (let i = 0, l = attrs.length; i < l; i++) {
|
41
|
+
const attr = attrs[i];
|
42
|
+
const attrName = attr.name;
|
15
43
|
// skip any attributes handled elsewhere
|
16
|
-
if (
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
)
|
22
|
-
|
23
|
-
}
|
44
|
+
if (
|
45
|
+
!skipAttrs.includes(attrName) &&
|
46
|
+
options.indexOf(attrName) === -1 &&
|
47
|
+
aria.test(attrName) &&
|
48
|
+
(preChecks[attrName] ? preChecks[attrName]() : true) &&
|
49
|
+
!axe.commons.aria.validateAttrValue(node, attrName)
|
50
|
+
) {
|
51
|
+
invalid.push(`${attrName}="${attr.nodeValue}"`);
|
24
52
|
}
|
25
53
|
}
|
26
54
|
|
55
|
+
if (needsReview.length) {
|
56
|
+
this.data(needsReview);
|
57
|
+
return undefined;
|
58
|
+
}
|
59
|
+
|
27
60
|
if (invalid.length) {
|
28
61
|
this.data(invalid);
|
29
62
|
return false;
|
@@ -6,7 +6,8 @@
|
|
6
6
|
"impact": "critical",
|
7
7
|
"messages": {
|
8
8
|
"pass": "ARIA attribute values are valid",
|
9
|
-
"fail": "Invalid ARIA attribute value{{=it.data && it.data.length > 1 ? 's' : ''}}:{{~it.data:value}} {{=value}}{{~}}"
|
9
|
+
"fail": "Invalid ARIA attribute value{{=it.data && it.data.length > 1 ? 's' : ''}}:{{~it.data:value}} {{=value}}{{~}}",
|
10
|
+
"incomplete": "ARIA attribute{{=it.data && it.data.length > 1 ? 's' : ''}} element ID does not exist on the page:{{~it.data:value}} {{=value}}{{~}}"
|
10
11
|
}
|
11
12
|
}
|
12
13
|
}
|
@@ -7,7 +7,7 @@ if (!dom.isVisible(node, false)) {
|
|
7
7
|
const noScroll = !!(options || {}).noScroll;
|
8
8
|
const bgNodes = [];
|
9
9
|
const bgColor = color.getBackgroundColor(node, bgNodes, noScroll);
|
10
|
-
const fgColor = color.getForegroundColor(node, noScroll);
|
10
|
+
const fgColor = color.getForegroundColor(node, noScroll, bgColor);
|
11
11
|
|
12
12
|
const nodeStyle = window.getComputedStyle(node);
|
13
13
|
const fontSize = parseFloat(nodeStyle.getPropertyValue('font-size'));
|
@@ -42,7 +42,7 @@ const data = {
|
|
42
42
|
fgColor: fgColor ? fgColor.toHexString() : undefined,
|
43
43
|
bgColor: bgColor ? bgColor.toHexString() : undefined,
|
44
44
|
contrastRatio: cr ? truncatedResult : undefined,
|
45
|
-
fontSize: ((fontSize * 72) / 96).toFixed(1)
|
45
|
+
fontSize: `${((fontSize * 72) / 96).toFixed(1)}pt (${fontSize}px)`,
|
46
46
|
fontWeight: bold ? 'bold' : 'normal',
|
47
47
|
missingData: missing,
|
48
48
|
expectedContrastRatio: cr.expectedContrastRatio + ':1'
|