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.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/component_guide/accessibility-test.js +0 -6
  3. data/app/views/govuk_publishing_components/components/docs/machine_readable_metadata.yml +7 -0
  4. data/lib/govuk_publishing_components/presenters/machine_readable/dataset_schema.rb +34 -0
  5. data/lib/govuk_publishing_components/presenters/schema_org.rb +3 -0
  6. data/lib/govuk_publishing_components/version.rb +1 -1
  7. data/node_modules/axe-core/CHANGELOG.md +166 -2
  8. data/node_modules/axe-core/CONTRIBUTING.md +5 -5
  9. data/node_modules/axe-core/README.md +4 -4
  10. data/node_modules/axe-core/axe.d.ts +27 -11
  11. data/node_modules/axe-core/axe.js +9597 -2431
  12. data/node_modules/axe-core/axe.min.js +2 -2
  13. data/node_modules/axe-core/bower.json +1 -1
  14. data/node_modules/axe-core/doc/API.md +211 -128
  15. data/node_modules/axe-core/doc/accessibility-supported.md +1 -1
  16. data/node_modules/axe-core/doc/aria-supported.md +4 -13
  17. data/node_modules/axe-core/doc/backwards-compatibility-doc.md +93 -0
  18. data/node_modules/axe-core/doc/code-submission-guidelines.md +4 -4
  19. data/node_modules/axe-core/doc/developer-guide.md +27 -13
  20. data/node_modules/axe-core/doc/examples/chrome-debugging-protocol/package.json +5 -2
  21. data/node_modules/axe-core/doc/examples/jasmine/README.md +3 -5
  22. data/node_modules/axe-core/doc/examples/jasmine/karma.conf.js +29 -0
  23. data/node_modules/axe-core/doc/examples/jasmine/package.json +6 -5
  24. data/node_modules/axe-core/doc/examples/jest_react/README.md +1 -1
  25. data/node_modules/axe-core/doc/examples/jest_react/link.test.js +3 -3
  26. data/node_modules/axe-core/doc/examples/jest_react/package.json +9 -9
  27. data/node_modules/axe-core/doc/examples/jsdom/package.json +15 -0
  28. data/node_modules/axe-core/doc/examples/mocha/README.md +5 -7
  29. data/node_modules/axe-core/doc/examples/mocha/karma.conf.js +29 -0
  30. data/node_modules/axe-core/doc/examples/mocha/package.json +7 -6
  31. data/node_modules/axe-core/doc/examples/phantomjs/README.md +3 -3
  32. data/node_modules/axe-core/doc/examples/phantomjs/axe-phantom.js +4 -2
  33. data/node_modules/axe-core/doc/examples/phantomjs/package.json +3 -3
  34. data/node_modules/axe-core/doc/examples/puppeteer/package.json +5 -2
  35. data/node_modules/axe-core/doc/examples/qunit/README.md +2 -2
  36. data/node_modules/axe-core/doc/examples/qunit/package.json +2 -2
  37. data/node_modules/axe-core/doc/examples/test-examples.js +32 -0
  38. data/node_modules/axe-core/doc/plugins.md +10 -10
  39. data/node_modules/axe-core/doc/projects.md +12 -8
  40. data/node_modules/axe-core/doc/rule-descriptions.md +87 -79
  41. data/node_modules/axe-core/doc/rule-development.md +30 -2
  42. data/node_modules/axe-core/lib/checks/aria/allowed-attr.js +1 -1
  43. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.js +14 -0
  44. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.json +23 -0
  45. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.js +21 -0
  46. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.json +11 -0
  47. data/node_modules/axe-core/lib/checks/aria/required-attr.js +32 -7
  48. data/node_modules/axe-core/lib/checks/aria/required-children.js +40 -14
  49. data/node_modules/axe-core/lib/checks/aria/required-children.json +12 -1
  50. data/node_modules/axe-core/lib/checks/aria/required-parent.js +1 -1
  51. data/node_modules/axe-core/lib/checks/aria/unsupportedattr.js +1 -1
  52. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +50 -17
  53. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -1
  54. data/node_modules/axe-core/lib/checks/aria/valid-attr.js +1 -1
  55. data/node_modules/axe-core/lib/checks/color/color-contrast.js +2 -2
  56. data/node_modules/axe-core/lib/checks/forms/autocomplete-appropriate.js +4 -4
  57. data/node_modules/axe-core/lib/checks/forms/autocomplete-valid.js +1 -1
  58. data/node_modules/axe-core/lib/checks/forms/fieldset.json +1 -0
  59. data/node_modules/axe-core/lib/checks/forms/group-labelledby.json +1 -0
  60. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.js +16 -0
  61. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.json +11 -0
  62. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.js +12 -0
  63. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.json +11 -0
  64. data/node_modules/axe-core/lib/checks/keyboard/tabindex.js +6 -1
  65. data/node_modules/axe-core/lib/checks/label/alt-space-value.js +3 -2
  66. data/node_modules/axe-core/lib/checks/label/duplicate-img-label.js +18 -15
  67. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.js +13 -3
  68. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.json +4 -0
  69. data/node_modules/axe-core/lib/checks/label/multiple-label.js +22 -12
  70. data/node_modules/axe-core/lib/checks/label/multiple-label.json +1 -1
  71. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique-after.js +23 -0
  72. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.js +7 -0
  73. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.json +12 -0
  74. data/node_modules/axe-core/lib/checks/lists/listitem.js +1 -0
  75. data/node_modules/axe-core/lib/checks/lists/listitem.json +1 -1
  76. data/node_modules/axe-core/lib/checks/lists/only-listitems.js +0 -4
  77. data/node_modules/axe-core/lib/checks/mobile/css-orientation-lock.js +8 -6
  78. data/node_modules/axe-core/lib/checks/navigation/region.js +2 -19
  79. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.js +18 -0
  80. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.json +11 -0
  81. data/node_modules/axe-core/lib/checks/shared/exists.js +1 -1
  82. data/node_modules/axe-core/lib/checks/shared/exists.json +1 -1
  83. data/node_modules/axe-core/lib/checks/shared/has-alt.js +6 -4
  84. data/node_modules/axe-core/lib/checks/shared/non-empty-alt.js +1 -1
  85. data/node_modules/axe-core/lib/checks/tables/caption-faked.json +1 -1
  86. data/node_modules/axe-core/lib/checks/tables/td-has-header.js +5 -4
  87. data/node_modules/axe-core/lib/checks/tables/th-has-data-cells.js +19 -29
  88. data/node_modules/axe-core/lib/commons/aria/get-owned-virtual.js +1 -1
  89. data/node_modules/axe-core/lib/commons/aria/index.js +50 -46
  90. data/node_modules/axe-core/lib/commons/aria/is-accessible-ref.js +41 -37
  91. data/node_modules/axe-core/lib/commons/aria/label-virtual.js +2 -2
  92. data/node_modules/axe-core/lib/commons/aria/roles.js +1 -1
  93. data/node_modules/axe-core/lib/commons/aria/validate-attr-value.js +0 -1
  94. data/node_modules/axe-core/lib/commons/color/center-point-of-rect.js +30 -0
  95. data/node_modules/axe-core/lib/commons/color/contrast.js +7 -1
  96. data/node_modules/axe-core/lib/commons/color/element-has-image.js +36 -0
  97. data/node_modules/axe-core/lib/commons/color/get-background-color.js +332 -306
  98. data/node_modules/axe-core/lib/commons/color/get-foreground-color.js +35 -6
  99. data/node_modules/axe-core/lib/commons/color/get-own-background-color.js +22 -0
  100. data/node_modules/axe-core/lib/commons/dom/find-up.js +5 -5
  101. data/node_modules/axe-core/lib/commons/dom/get-scroll-offset.js +0 -1
  102. data/node_modules/axe-core/lib/commons/dom/get-tabbable-elements.js +1 -1
  103. data/node_modules/axe-core/lib/commons/dom/has-content-virtual.js +7 -5
  104. data/node_modules/axe-core/lib/commons/dom/is-focusable.js +8 -5
  105. data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +15 -2
  106. data/node_modules/axe-core/lib/commons/dom/is-in-text-block.js +1 -2
  107. data/node_modules/axe-core/lib/commons/dom/is-skip-link.js +45 -0
  108. data/node_modules/axe-core/lib/commons/dom/is-visible.js +43 -17
  109. data/node_modules/axe-core/lib/commons/dom/is-visual-content.js +0 -1
  110. data/node_modules/axe-core/lib/commons/dom/visually-contains.js +0 -1
  111. data/node_modules/axe-core/lib/commons/dom/visually-overlaps.js +0 -1
  112. data/node_modules/axe-core/lib/commons/forms/index.js +8 -0
  113. data/node_modules/axe-core/lib/commons/forms/is-aria-combobox.js +13 -0
  114. data/node_modules/axe-core/lib/commons/forms/is-aria-listbox.js +13 -0
  115. data/node_modules/axe-core/lib/commons/forms/is-aria-range.js +14 -0
  116. data/node_modules/axe-core/lib/commons/forms/is-aria-textbox.js +13 -0
  117. data/node_modules/axe-core/lib/commons/forms/is-native-select.js +13 -0
  118. data/node_modules/axe-core/lib/commons/forms/is-native-textbox.js +28 -0
  119. data/node_modules/axe-core/lib/commons/table/get-cell-position.js +2 -2
  120. data/node_modules/axe-core/lib/commons/table/get-headers.js +55 -11
  121. data/node_modules/axe-core/lib/commons/table/get-scope.js +1 -1
  122. data/node_modules/axe-core/lib/commons/table/is-data-table.js +0 -1
  123. data/node_modules/axe-core/lib/commons/table/to-grid.js +2 -2
  124. data/node_modules/axe-core/lib/commons/text/accessible-text-virtual.js +5 -5
  125. data/node_modules/axe-core/lib/commons/text/form-control-value.js +18 -30
  126. data/node_modules/axe-core/lib/commons/text/is-icon-ligature.js +210 -0
  127. data/node_modules/axe-core/lib/commons/text/is-valid-autocomplete.js +0 -1
  128. data/node_modules/axe-core/lib/commons/text/label-text.js +1 -1
  129. data/node_modules/axe-core/lib/commons/text/label-virtual.js +1 -1
  130. data/node_modules/axe-core/lib/commons/text/native-text-methods.js +1 -1
  131. data/node_modules/axe-core/lib/commons/text/subtree-text.js +3 -3
  132. data/node_modules/axe-core/lib/commons/text/unicode.js +15 -0
  133. data/node_modules/axe-core/lib/commons/text/visible-text-nodes.js +25 -0
  134. data/node_modules/axe-core/lib/commons/text/visible-virtual.js +1 -1
  135. data/node_modules/axe-core/lib/core/base/audit.js +90 -15
  136. data/node_modules/axe-core/lib/core/base/cache.js +33 -0
  137. data/node_modules/axe-core/lib/core/base/check.js +48 -1
  138. data/node_modules/axe-core/lib/core/base/context.js +15 -14
  139. data/node_modules/axe-core/lib/core/base/rule.js +223 -46
  140. data/node_modules/axe-core/lib/core/base/virtual-node/abstract-virtual-node.js +40 -0
  141. data/node_modules/axe-core/lib/core/base/virtual-node/serial-virtual-node.js +86 -0
  142. data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +85 -0
  143. data/node_modules/axe-core/lib/core/constants.js +10 -2
  144. data/node_modules/axe-core/lib/core/imports/index.js +28 -3
  145. data/node_modules/axe-core/lib/core/index.js +2 -4
  146. data/node_modules/axe-core/lib/core/public/configure.js +28 -1
  147. data/node_modules/axe-core/lib/core/public/run-rules.js +2 -0
  148. data/node_modules/axe-core/lib/core/public/run-virtual-rule.js +50 -0
  149. data/node_modules/axe-core/lib/core/public/run.js +13 -2
  150. data/node_modules/axe-core/lib/core/reporters/helpers/process-aggregate.js +1 -1
  151. data/node_modules/axe-core/lib/core/reporters/na.js +4 -0
  152. data/node_modules/axe-core/lib/core/reporters/raw-env.js +12 -0
  153. data/node_modules/axe-core/lib/core/reporters/raw.js +25 -1
  154. data/node_modules/axe-core/lib/core/utils/are-styles-set.js +4 -7
  155. data/node_modules/axe-core/lib/core/utils/assert.js +12 -0
  156. data/node_modules/axe-core/lib/core/utils/collect-results-from-frames.js +2 -2
  157. data/node_modules/axe-core/lib/core/utils/contains.js +27 -13
  158. data/node_modules/axe-core/lib/core/utils/css-parser.js +2 -0
  159. data/node_modules/axe-core/lib/core/utils/element-matches.js +5 -1
  160. data/node_modules/axe-core/lib/core/utils/escape-selector.js +1 -2
  161. data/node_modules/axe-core/lib/core/utils/flattened-tree.js +47 -61
  162. data/node_modules/axe-core/lib/core/utils/get-check-option.js +0 -1
  163. data/node_modules/axe-core/lib/core/utils/get-friendly-uri-end.js +1 -1
  164. data/node_modules/axe-core/lib/core/utils/get-node-attributes.js +21 -0
  165. data/node_modules/axe-core/lib/core/utils/get-scroll.js +39 -0
  166. data/node_modules/axe-core/lib/core/utils/get-selector.js +9 -6
  167. data/node_modules/axe-core/lib/core/utils/get-stylesheet-factory.js +51 -0
  168. data/node_modules/axe-core/lib/core/utils/get-xpath.js +0 -1
  169. data/node_modules/axe-core/lib/core/utils/is-hidden.js +16 -4
  170. data/node_modules/axe-core/lib/core/utils/is-html-element.js +5 -5
  171. data/node_modules/axe-core/lib/core/utils/is-shadow-root.js +3 -3
  172. data/node_modules/axe-core/lib/core/utils/memoize.js +17 -0
  173. data/node_modules/axe-core/lib/core/utils/parse-crossorigin-stylesheet.js +53 -0
  174. data/node_modules/axe-core/lib/core/utils/parse-sameorigin-stylesheet.js +96 -0
  175. data/node_modules/axe-core/lib/core/utils/parse-stylesheet.js +70 -0
  176. data/node_modules/axe-core/lib/core/utils/performance-timer.js +7 -2
  177. data/node_modules/axe-core/lib/core/utils/preload-cssom.js +77 -281
  178. data/node_modules/axe-core/lib/core/utils/preload.js +49 -23
  179. data/node_modules/axe-core/lib/core/utils/qsa.js +39 -50
  180. data/node_modules/axe-core/lib/core/utils/respondable.js +20 -3
  181. data/node_modules/axe-core/lib/core/utils/rule-should-run.js +0 -1
  182. data/node_modules/axe-core/lib/core/utils/scroll-state.js +12 -25
  183. data/node_modules/axe-core/lib/core/utils/select.js +12 -23
  184. data/node_modules/axe-core/lib/core/utils/uuid.js +1 -2
  185. data/node_modules/axe-core/lib/intro.stub +1 -1
  186. data/node_modules/axe-core/lib/misc/incomplete-fallback.json +1 -1
  187. data/node_modules/axe-core/lib/rules/aria-allowed-attr-matches.js +1 -1
  188. data/node_modules/axe-core/lib/rules/aria-form-field-name-matches.js +50 -0
  189. data/node_modules/axe-core/lib/rules/aria-has-attr-matches.js +1 -1
  190. data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +1 -1
  191. data/node_modules/axe-core/lib/rules/aria-input-field-name.json +13 -0
  192. data/node_modules/axe-core/lib/rules/aria-roledescription.json +12 -0
  193. data/node_modules/axe-core/lib/rules/aria-toggle-field-name.json +18 -0
  194. data/node_modules/axe-core/lib/rules/autocomplete-matches.js +14 -10
  195. data/node_modules/axe-core/lib/rules/avoid-inline-spacing.json +12 -0
  196. data/node_modules/axe-core/lib/rules/button-name.json +3 -5
  197. data/node_modules/axe-core/lib/rules/checkboxgroup.json +2 -1
  198. data/node_modules/axe-core/lib/rules/color-contrast-matches.js +37 -22
  199. data/node_modules/axe-core/lib/rules/duplicate-id-active-matches.js +1 -1
  200. data/node_modules/axe-core/lib/rules/duplicate-id-misc-matches.js +2 -2
  201. data/node_modules/axe-core/lib/rules/form-field-multiple-labels.json +1 -1
  202. data/node_modules/axe-core/lib/rules/html-has-lang.json +1 -0
  203. data/node_modules/axe-core/lib/rules/image-alt.json +1 -1
  204. data/node_modules/axe-core/lib/rules/img-redundant-alt.json +3 -3
  205. data/node_modules/axe-core/lib/rules/input-button-name.json +26 -0
  206. data/node_modules/axe-core/lib/rules/landmark-unique-matches.js +41 -0
  207. data/node_modules/axe-core/lib/rules/landmark-unique.json +13 -0
  208. data/node_modules/axe-core/lib/rules/link-name.json +5 -4
  209. data/node_modules/axe-core/lib/rules/meta-refresh.json +8 -1
  210. data/node_modules/axe-core/lib/rules/radiogroup.json +2 -1
  211. data/node_modules/axe-core/lib/rules/role-img-alt.json +18 -0
  212. data/node_modules/axe-core/lib/rules/scrollable-region-focusable-matches.js +30 -0
  213. data/node_modules/axe-core/lib/rules/scrollable-region-focusable.json +12 -0
  214. data/node_modules/axe-core/lib/rules/skip-link-matches.js +1 -1
  215. data/node_modules/axe-core/lib/rules/skip-link.json +1 -1
  216. data/node_modules/axe-core/lib/rules/video-description.json +3 -1
  217. data/node_modules/axe-core/locales/de.json +1 -5
  218. data/node_modules/axe-core/locales/es.json +773 -0
  219. data/node_modules/axe-core/locales/fr.json +15 -19
  220. data/node_modules/axe-core/locales/ja.json +65 -11
  221. data/node_modules/axe-core/locales/ko.json +777 -0
  222. data/node_modules/axe-core/locales/nl.json +35 -35
  223. data/node_modules/axe-core/locales/pt_BR.json +773 -0
  224. data/node_modules/axe-core/package.json +56 -52
  225. data/node_modules/axe-core/sri-history.json +20 -0
  226. data/node_modules/axe-core/typings/axe-core/axe-core-tests.ts +5 -15
  227. data/node_modules/govuk-frontend/package.json +10 -10
  228. metadata +62 -4
  229. data/node_modules/axe-core/doc/axelogo2018.png +0 -0
  230. 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 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.
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 aXe extension sidebar |
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.
@@ -6,7 +6,7 @@ var attr,
6
6
  attrName,
7
7
  allowed,
8
8
  role = node.getAttribute('role'),
9
- attrs = node.attributes;
9
+ attrs = axe.utils.getNodeAttributes(node);
10
10
 
11
11
  if (!role) {
12
12
  role = axe.commons.aria.implicitRole(node);
@@ -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
- var missing = [];
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
- var attr,
7
- role = node.getAttribute('role'),
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 (var i = 0, l = required.length; i < l; i++) {
15
- attr = required[i];
16
- if (!node.getAttribute(attr)) {
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(implicit);
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
- let virtualTree = axe.utils.getNodeFromTree(axe._tree[0], nodes[index]);
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
- /* eslint max-statements: ["error", 22], complexity: ["error", 17] */
43
- var i,
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 (i = 0; i < l; i++) {
49
- var r = childRoles[i];
50
- if (owns(node, virtualNode, r) || ariaOwns(ownedElements, r)) {
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(r);
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
- node.nodeName.toUpperCase() === 'INPUT' &&
69
- textTypeInputs.includes(node.type)
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
- if (reviewEmpty.includes(role)) {
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": ["listbox"]
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(axe._tree[0], owners[i]),
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.attributes)
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
- var invalid = [],
4
- aria = /^aria-/;
3
+ const needsReview = [];
4
+ const invalid = [];
5
+ const aria = /^aria-/;
6
+ const attrs = axe.utils.getNodeAttributes(node);
5
7
 
6
- var attr,
7
- attrName,
8
- attrs = node.attributes;
8
+ const skipAttrs = ['aria-errormessage'];
9
9
 
10
- var skipAttrs = ['aria-errormessage'];
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 (var i = 0, l = attrs.length; i < l; i++) {
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 (!skipAttrs.includes(attrName)) {
17
- if (
18
- options.indexOf(attrName) === -1 &&
19
- aria.test(attrName) &&
20
- !axe.commons.aria.validateAttrValue(node, attrName)
21
- ) {
22
- invalid.push(attrName + '="' + attr.nodeValue + '"');
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
  }
@@ -4,7 +4,7 @@ var invalid = [],
4
4
  aria = /^aria-/;
5
5
 
6
6
  var attr,
7
- attrs = node.attributes;
7
+ attrs = axe.utils.getNodeAttributes(node);
8
8
 
9
9
  for (var i = 0, l = attrs.length; i < l; i++) {
10
10
  attr = attrs[i].name;
@@ -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) + 'pt',
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'