abledom 0.6.0 → 0.6.2

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/dist/index.js CHANGED
@@ -31,6 +31,9 @@ __export(index_exports, {
31
31
  FindElementRule: () => FindElementRule,
32
32
  FocusLostRule: () => FocusLostRule,
33
33
  FocusableElementLabelRule: () => FocusableElementLabelRule,
34
+ NestedInteractiveElementRule: () => NestedInteractiveElementRule,
35
+ RequiredParentRule: () => RequiredParentRule,
36
+ TabIndexRule: () => TabIndexRule,
34
37
  ValidationRule: () => ValidationRule,
35
38
  ValidationRuleType: () => ValidationRuleType,
36
39
  hasAccessibilityAttribute: () => hasAccessibilityAttribute,
@@ -96,10 +99,10 @@ var ValidationRule = class {
96
99
  }
97
100
  };
98
101
 
99
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/ui.css
102
+ // inline-file:/Users/marata/tmp/abledom/src/ui/ui.css
100
103
  var ui_default = "#abledom-report {\n bottom: 20px;\n display: flex;\n flex-direction: column;\n left: 10px;\n max-height: 80%;\n max-width: 60%;\n padding: 4px 8px;\n position: fixed;\n z-index: 100500;\n}\n\n#abledom-report :focus-visible {\n outline: 3px solid red;\n mix-blend-mode: difference;\n}\n\n#abledom-report.abledom-align-left {\n left: 10px;\n right: auto;\n}\n\n#abledom-report.abledom-align-right {\n left: auto;\n right: 10px;\n}\n\n#abledom-report.abledom-align-bottom {\n bottom: 20px;\n top: auto;\n}\n\n#abledom-report.abledom-align-top {\n /* flex-direction: column-reverse; */\n bottom: auto;\n top: 10px;\n}\n\n.abledom-menu-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-block;\n margin: 2px auto 2px 0;\n}\n\n#abledom-report.abledom-align-right .abledom-menu-container {\n margin: 2px 0 2px auto;\n}\n\n.abledom-menu {\n background-color: rgba(140, 10, 121, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n\n.abledom-menu .issues-count {\n margin: 0 8px;\n display: inline-block;\n}\n\n.abledom-menu .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-menu .align-button {\n border-right-color: rgba(0, 0, 0, 0.4);\n border-radius: 0;\n margin: 0;\n}\n\n.abledom-menu .align-button:active,\n#abledom-report .pressed {\n background: linear-gradient(\n 180deg,\n rgba(130, 130, 130, 1) 0%,\n rgba(180, 180, 180, 1) 100%\n );\n}\n\n.abledom-menu .align-button-first {\n border-top-left-radius: 6px;\n border-bottom-left-radius: 6px;\n margin-left: 8px;\n}\n.abledom-menu .align-button-last {\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n border-right-color: rgba(255, 255, 255, 0.4);\n}\n\n.abledom-issues-container {\n overflow: auto;\n max-height: calc(100vh - 100px);\n}\n\n#abledom-report.abledom-align-right .abledom-issues-container {\n text-align: right;\n}\n\n.abledom-issue-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-flex;\n margin: 2px 0;\n}\n\n.abledom-issue {\n background-color: rgba(164, 2, 2, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n.abledom-issue_warning {\n background-color: rgba(163, 82, 1, 0.7);\n}\n.abledom-issue_info {\n background-color: rgba(0, 0, 255, 0.7);\n}\n\n.abledom-issue .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-issue .button:hover {\n opacity: 0.7;\n}\n\n.abledom-issue .button.close {\n background: none;\n border-color: transparent;\n color: #fff;\n margin: 0;\n}\n";
101
104
 
102
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/highlighter.css
105
+ // inline-file:/Users/marata/tmp/abledom/src/ui/highlighter.css
103
106
  var highlighter_default = ".abledom-highlight {\n background-color: yellow;\n box-sizing: border-box;\n display: none;\n opacity: 0.6;\n position: fixed;\n z-index: 100499;\n}\n\n.abledom-highlight-border1 {\n border-top: 2px solid red;\n border-bottom: 2px solid red;\n box-sizing: border-box;\n position: absolute;\n top: -2px;\n width: calc(100% + 20px);\n height: calc(100% + 4px);\n margin: 0 -10px;\n}\n\n.abledom-highlight-border2 {\n border-left: 2px solid red;\n border-right: 2px solid red;\n box-sizing: border-box;\n position: absolute;\n width: calc(100% + 4px);\n left: -2px;\n height: calc(100% + 20px);\n margin: -10px 0;\n}\n";
104
107
 
105
108
  // src/ui/domBuilder.ts
@@ -157,7 +160,7 @@ var DOMBuilder = class {
157
160
  }
158
161
  };
159
162
 
160
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/close.svg
163
+ // inline-file:/Users/marata/tmp/abledom/src/ui/close.svg
161
164
  var close_default = (function buildSVG(parent) {
162
165
  const builder = new DOMBuilder(parent);
163
166
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 24 24", "fill": "none", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "xmlns": "http://www.w3.org/2000/svg" }, void 0, "http://www.w3.org/2000/svg");
@@ -171,7 +174,7 @@ var close_default = (function buildSVG(parent) {
171
174
  return parent.firstElementChild;
172
175
  });
173
176
 
174
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/help.svg
177
+ // inline-file:/Users/marata/tmp/abledom/src/ui/help.svg
175
178
  var help_default = (function buildSVG2(parent) {
176
179
  const builder = new DOMBuilder(parent);
177
180
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 24 24", "fill": "none", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "xmlns": "http://www.w3.org/2000/svg" }, void 0, "http://www.w3.org/2000/svg");
@@ -185,7 +188,7 @@ var help_default = (function buildSVG2(parent) {
185
188
  return parent.firstElementChild;
186
189
  });
187
190
 
188
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/log.svg
191
+ // inline-file:/Users/marata/tmp/abledom/src/ui/log.svg
189
192
  var log_default = (function buildSVG3(parent) {
190
193
  const builder = new DOMBuilder(parent);
191
194
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 24 24", "fill": "none", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "xmlns": "http://www.w3.org/2000/svg" }, void 0, "http://www.w3.org/2000/svg");
@@ -197,7 +200,7 @@ var log_default = (function buildSVG3(parent) {
197
200
  return parent.firstElementChild;
198
201
  });
199
202
 
200
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/reveal.svg
203
+ // inline-file:/Users/marata/tmp/abledom/src/ui/reveal.svg
201
204
  var reveal_default = (function buildSVG4(parent) {
202
205
  const builder = new DOMBuilder(parent);
203
206
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 24 24", "fill": "none", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "xmlns": "http://www.w3.org/2000/svg" }, void 0, "http://www.w3.org/2000/svg");
@@ -215,7 +218,7 @@ var reveal_default = (function buildSVG4(parent) {
215
218
  return parent.firstElementChild;
216
219
  });
217
220
 
218
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/bug.svg
221
+ // inline-file:/Users/marata/tmp/abledom/src/ui/bug.svg
219
222
  var bug_default = (function buildSVG5(parent) {
220
223
  const builder = new DOMBuilder(parent);
221
224
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -243,7 +246,7 @@ var bug_default = (function buildSVG5(parent) {
243
246
  return parent.firstElementChild;
244
247
  });
245
248
 
246
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/hideall.svg
249
+ // inline-file:/Users/marata/tmp/abledom/src/ui/hideall.svg
247
250
  var hideall_default = (function buildSVG6(parent) {
248
251
  const builder = new DOMBuilder(parent);
249
252
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -257,7 +260,7 @@ var hideall_default = (function buildSVG6(parent) {
257
260
  return parent.firstElementChild;
258
261
  });
259
262
 
260
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/muteall.svg
263
+ // inline-file:/Users/marata/tmp/abledom/src/ui/muteall.svg
261
264
  var muteall_default = (function buildSVG7(parent) {
262
265
  const builder = new DOMBuilder(parent);
263
266
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -273,7 +276,7 @@ var muteall_default = (function buildSVG7(parent) {
273
276
  return parent.firstElementChild;
274
277
  });
275
278
 
276
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/showall.svg
279
+ // inline-file:/Users/marata/tmp/abledom/src/ui/showall.svg
277
280
  var showall_default = (function buildSVG8(parent) {
278
281
  const builder = new DOMBuilder(parent);
279
282
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -285,7 +288,7 @@ var showall_default = (function buildSVG8(parent) {
285
288
  return parent.firstElementChild;
286
289
  });
287
290
 
288
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/aligntopleft.svg
291
+ // inline-file:/Users/marata/tmp/abledom/src/ui/aligntopleft.svg
289
292
  var aligntopleft_default = (function buildSVG9(parent) {
290
293
  const builder = new DOMBuilder(parent);
291
294
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -295,7 +298,7 @@ var aligntopleft_default = (function buildSVG9(parent) {
295
298
  return parent.firstElementChild;
296
299
  });
297
300
 
298
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/aligntopright.svg
301
+ // inline-file:/Users/marata/tmp/abledom/src/ui/aligntopright.svg
299
302
  var aligntopright_default = (function buildSVG10(parent) {
300
303
  const builder = new DOMBuilder(parent);
301
304
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -305,7 +308,7 @@ var aligntopright_default = (function buildSVG10(parent) {
305
308
  return parent.firstElementChild;
306
309
  });
307
310
 
308
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/alignbottomright.svg
311
+ // inline-file:/Users/marata/tmp/abledom/src/ui/alignbottomright.svg
309
312
  var alignbottomright_default = (function buildSVG11(parent) {
310
313
  const builder = new DOMBuilder(parent);
311
314
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -315,7 +318,7 @@ var alignbottomright_default = (function buildSVG11(parent) {
315
318
  return parent.firstElementChild;
316
319
  });
317
320
 
318
- // inline-file:/Users/marata/Documents/Work/abledom/src/ui/alignbottomleft.svg
321
+ // inline-file:/Users/marata/tmp/abledom/src/ui/alignbottomleft.svg
319
322
  var alignbottomleft_default = (function buildSVG12(parent) {
320
323
  const builder = new DOMBuilder(parent);
321
324
  builder.openTag("svg", { "width": "20", "height": "20", "viewBox": "0 0 20 20", "fill": "none", "stroke": "currentColor" }, void 0, "http://www.w3.org/2000/svg");
@@ -899,10 +902,17 @@ var AccessibilityAffectingElements = {
899
902
  aside: true,
900
903
  body: true,
901
904
  button: true,
905
+ caption: true,
906
+ col: true,
907
+ colgroup: true,
902
908
  datalist: true,
909
+ dd: true,
903
910
  details: true,
904
911
  dialog: true,
905
912
  dl: true,
913
+ dt: true,
914
+ figcaption: true,
915
+ figure: true,
906
916
  form: true,
907
917
  h1: true,
908
918
  h2: true,
@@ -914,6 +924,7 @@ var AccessibilityAffectingElements = {
914
924
  iframe: true,
915
925
  img: true,
916
926
  input: true,
927
+ legend: true,
917
928
  li: true,
918
929
  link: true,
919
930
  main: true,
@@ -923,15 +934,22 @@ var AccessibilityAffectingElements = {
923
934
  nav: true,
924
935
  object: true,
925
936
  ol: true,
937
+ optgroup: true,
926
938
  option: true,
927
939
  progress: true,
928
940
  section: true,
929
941
  select: true,
942
+ source: true,
943
+ summary: true,
944
+ table: true,
930
945
  tbody: true,
946
+ td: true,
931
947
  textarea: true,
932
948
  tfoot: true,
933
949
  th: true,
934
950
  thead: true,
951
+ tr: true,
952
+ track: true,
935
953
  ul: true
936
954
  };
937
955
  var AccessibilityAttributes = {
@@ -1123,6 +1141,9 @@ var AbleDOM = class {
1123
1141
  ((_c = (_b = this._win) == null ? void 0 : _b.console) == null ? void 0 : _c.error)) == null ? void 0 : _d.apply(null, args);
1124
1142
  });
1125
1143
  this._win = win;
1144
+ if (props.exposeInstanceForTesting) {
1145
+ this._win.ableDOMInstanceForTesting = this;
1146
+ }
1126
1147
  this._props = props;
1127
1148
  const _elementsToValidate = /* @__PURE__ */ new Set();
1128
1149
  const _elementsToRemove = /* @__PURE__ */ new Set();
@@ -2101,6 +2122,475 @@ var CustomNotifyRule = class extends ValidationRule {
2101
2122
  });
2102
2123
  }
2103
2124
  };
2125
+
2126
+ // src/rules/requiredparent.ts
2127
+ var RequiredParentRule = class extends ValidationRule {
2128
+ constructor() {
2129
+ super(...arguments);
2130
+ __publicField(this, "type", 1 /* Error */);
2131
+ __publicField(this, "name", "aria-required-parent");
2132
+ __publicField(this, "anchored", true);
2133
+ __publicField(this, "parentRequirements", /* @__PURE__ */ new Map([
2134
+ [
2135
+ "LI",
2136
+ {
2137
+ allowedParents: ["UL", "OL"],
2138
+ allowedParentRoles: ["list"]
2139
+ }
2140
+ ],
2141
+ [
2142
+ "DT",
2143
+ {
2144
+ allowedParents: ["DL"],
2145
+ allowIntermediateWrappers: true,
2146
+ allowedWrappers: ["DIV"]
2147
+ }
2148
+ ],
2149
+ [
2150
+ "DD",
2151
+ {
2152
+ allowedParents: ["DL"],
2153
+ allowIntermediateWrappers: true,
2154
+ allowedWrappers: ["DIV"]
2155
+ }
2156
+ ],
2157
+ [
2158
+ "TR",
2159
+ {
2160
+ allowedParents: ["TABLE", "THEAD", "TBODY", "TFOOT"],
2161
+ allowedParentRoles: ["table", "grid", "treegrid"]
2162
+ }
2163
+ ],
2164
+ [
2165
+ "TH",
2166
+ {
2167
+ allowedParents: ["TR"],
2168
+ allowedParentRoles: ["row"]
2169
+ }
2170
+ ],
2171
+ [
2172
+ "TD",
2173
+ {
2174
+ allowedParents: ["TR"],
2175
+ allowedParentRoles: ["row"]
2176
+ }
2177
+ ],
2178
+ [
2179
+ "THEAD",
2180
+ {
2181
+ allowedParents: ["TABLE"],
2182
+ allowedParentRoles: ["table", "grid", "treegrid"]
2183
+ }
2184
+ ],
2185
+ [
2186
+ "TBODY",
2187
+ {
2188
+ allowedParents: ["TABLE"],
2189
+ allowedParentRoles: ["table", "grid", "treegrid"]
2190
+ }
2191
+ ],
2192
+ [
2193
+ "TFOOT",
2194
+ {
2195
+ allowedParents: ["TABLE"],
2196
+ allowedParentRoles: ["table", "grid", "treegrid"]
2197
+ }
2198
+ ],
2199
+ [
2200
+ "CAPTION",
2201
+ {
2202
+ allowedParents: ["TABLE"]
2203
+ }
2204
+ ],
2205
+ [
2206
+ "COLGROUP",
2207
+ {
2208
+ allowedParents: ["TABLE"]
2209
+ }
2210
+ ],
2211
+ [
2212
+ "COL",
2213
+ {
2214
+ allowedParents: ["COLGROUP"]
2215
+ }
2216
+ ],
2217
+ [
2218
+ "FIGCAPTION",
2219
+ {
2220
+ allowedParents: ["FIGURE"]
2221
+ }
2222
+ ],
2223
+ [
2224
+ "OPTION",
2225
+ {
2226
+ allowedParents: ["SELECT", "OPTGROUP", "DATALIST"]
2227
+ }
2228
+ ],
2229
+ [
2230
+ "OPTGROUP",
2231
+ {
2232
+ allowedParents: ["SELECT"]
2233
+ }
2234
+ ],
2235
+ [
2236
+ "LEGEND",
2237
+ {
2238
+ allowedParents: ["FIELDSET"]
2239
+ }
2240
+ ],
2241
+ [
2242
+ "SUMMARY",
2243
+ {
2244
+ allowedParents: ["DETAILS"]
2245
+ }
2246
+ ],
2247
+ [
2248
+ "SOURCE",
2249
+ {
2250
+ allowedParents: ["AUDIO", "VIDEO", "PICTURE"]
2251
+ }
2252
+ ],
2253
+ [
2254
+ "TRACK",
2255
+ {
2256
+ allowedParents: ["AUDIO", "VIDEO"]
2257
+ }
2258
+ ],
2259
+ [
2260
+ "role=menuitem",
2261
+ {
2262
+ allowedParentRoles: ["menu", "menubar", "group"]
2263
+ }
2264
+ ],
2265
+ [
2266
+ "role=menuitemcheckbox",
2267
+ {
2268
+ allowedParentRoles: ["menu", "menubar", "group"]
2269
+ }
2270
+ ],
2271
+ [
2272
+ "role=menuitemradio",
2273
+ {
2274
+ allowedParentRoles: ["menu", "menubar", "group"]
2275
+ }
2276
+ ],
2277
+ [
2278
+ "role=listitem",
2279
+ {
2280
+ allowedParentRoles: ["list", "group"]
2281
+ }
2282
+ ],
2283
+ [
2284
+ "role=treeitem",
2285
+ {
2286
+ allowedParentRoles: ["tree", "group"]
2287
+ }
2288
+ ],
2289
+ [
2290
+ "role=tab",
2291
+ {
2292
+ allowedParentRoles: ["tablist"]
2293
+ }
2294
+ ],
2295
+ [
2296
+ "role=row",
2297
+ {
2298
+ allowedParentRoles: ["table", "grid", "treegrid", "rowgroup"]
2299
+ }
2300
+ ],
2301
+ [
2302
+ "role=cell",
2303
+ {
2304
+ allowedParentRoles: ["row"]
2305
+ }
2306
+ ],
2307
+ [
2308
+ "role=gridcell",
2309
+ {
2310
+ allowedParentRoles: ["row"]
2311
+ }
2312
+ ],
2313
+ [
2314
+ "role=columnheader",
2315
+ {
2316
+ allowedParentRoles: ["row"]
2317
+ }
2318
+ ],
2319
+ [
2320
+ "role=rowheader",
2321
+ {
2322
+ allowedParentRoles: ["row"]
2323
+ }
2324
+ ],
2325
+ [
2326
+ "role=rowgroup",
2327
+ {
2328
+ allowedParentRoles: ["table", "grid", "treegrid"]
2329
+ }
2330
+ ],
2331
+ [
2332
+ "role=option",
2333
+ {
2334
+ allowedParentRoles: ["listbox", "group"]
2335
+ }
2336
+ ]
2337
+ ]));
2338
+ }
2339
+ accept(element) {
2340
+ const tagName = element.tagName;
2341
+ const role = element.getAttribute("role");
2342
+ if (this.parentRequirements.has(tagName)) {
2343
+ return true;
2344
+ }
2345
+ if (role && this.parentRequirements.has(`role=${role}`)) {
2346
+ return true;
2347
+ }
2348
+ return false;
2349
+ }
2350
+ validate(element) {
2351
+ const tagName = element.tagName;
2352
+ const role = element.getAttribute("role");
2353
+ let requirement;
2354
+ let identifier = "";
2355
+ if (role && this.parentRequirements.has(`role=${role}`)) {
2356
+ requirement = this.parentRequirements.get(`role=${role}`);
2357
+ identifier = `role="${role}"`;
2358
+ } else if (this.parentRequirements.has(tagName)) {
2359
+ requirement = this.parentRequirements.get(tagName);
2360
+ identifier = `<${tagName.toLowerCase()}>`;
2361
+ }
2362
+ if (!requirement) {
2363
+ return null;
2364
+ }
2365
+ if (requirement.customValidator) {
2366
+ if (requirement.customValidator(element)) {
2367
+ return null;
2368
+ } else {
2369
+ return this.createIssue(element, identifier, requirement);
2370
+ }
2371
+ }
2372
+ if (this.hasValidParent(element, requirement)) {
2373
+ return null;
2374
+ }
2375
+ return this.createIssue(element, identifier, requirement);
2376
+ }
2377
+ hasValidParent(element, requirement) {
2378
+ var _a, _b, _c;
2379
+ let parent = element.parentElement;
2380
+ let depth = 0;
2381
+ const maxDepth = requirement.allowIntermediateWrappers ? 2 : 1;
2382
+ while (parent && depth < maxDepth) {
2383
+ if ((_a = requirement.allowedParents) == null ? void 0 : _a.includes(parent.tagName)) {
2384
+ return true;
2385
+ }
2386
+ const parentRole = parent.getAttribute("role");
2387
+ if (parentRole && ((_b = requirement.allowedParentRoles) == null ? void 0 : _b.includes(parentRole))) {
2388
+ return true;
2389
+ }
2390
+ if (depth === 0 && !requirement.allowIntermediateWrappers) {
2391
+ break;
2392
+ }
2393
+ if (depth === 0 && requirement.allowIntermediateWrappers) {
2394
+ if (!((_c = requirement.allowedWrappers) == null ? void 0 : _c.includes(parent.tagName))) {
2395
+ break;
2396
+ }
2397
+ }
2398
+ parent = parent.parentElement;
2399
+ depth++;
2400
+ }
2401
+ return false;
2402
+ }
2403
+ createIssue(element, identifier, requirement) {
2404
+ var _a, _b;
2405
+ const allowedParentsText = [
2406
+ ...((_a = requirement.allowedParents) == null ? void 0 : _a.map((p) => `<${p.toLowerCase()}>`)) || [],
2407
+ ...((_b = requirement.allowedParentRoles) == null ? void 0 : _b.map((r) => `role="${r}"`)) || []
2408
+ ].join(", ");
2409
+ const message = `${identifier} must be contained by ${allowedParentsText}`;
2410
+ return {
2411
+ issue: {
2412
+ id: "aria-required-parent",
2413
+ message,
2414
+ element,
2415
+ help: "https://dequeuniversity.com/rules/axe/4.2/aria-required-parent"
2416
+ }
2417
+ };
2418
+ }
2419
+ };
2420
+
2421
+ // src/rules/nestedInteractive.ts
2422
+ var interactiveElementSelector = [
2423
+ "a[href]",
2424
+ "button",
2425
+ "input:not([type='hidden'])",
2426
+ "select",
2427
+ "textarea",
2428
+ "details",
2429
+ "audio[controls]",
2430
+ "video[controls]",
2431
+ "*[role='button']",
2432
+ "*[role='link']",
2433
+ "*[role='checkbox']",
2434
+ "*[role='radio']",
2435
+ "*[role='switch']",
2436
+ "*[role='tab']",
2437
+ "*[role='menuitem']",
2438
+ "*[role='menuitemcheckbox']",
2439
+ "*[role='menuitemradio']",
2440
+ "*[role='option']",
2441
+ "*[role='treeitem']"
2442
+ ].join(", ");
2443
+ var NestedInteractiveElementRule = class extends ValidationRule {
2444
+ constructor() {
2445
+ super(...arguments);
2446
+ __publicField(this, "type", 1 /* Error */);
2447
+ __publicField(this, "name", "NestedInteractiveElementRule");
2448
+ __publicField(this, "anchored", true);
2449
+ }
2450
+ _isAriaHidden(element) {
2451
+ return element.ownerDocument.evaluate(
2452
+ `ancestor-or-self::*[@aria-hidden = 'true' or @hidden]`,
2453
+ element,
2454
+ null,
2455
+ XPathResult.BOOLEAN_TYPE,
2456
+ null
2457
+ ).booleanValue;
2458
+ }
2459
+ _isInteractive(element) {
2460
+ return matchesSelector(element, interactiveElementSelector);
2461
+ }
2462
+ _findNestedInteractive(element) {
2463
+ const descendants = element.querySelectorAll(interactiveElementSelector);
2464
+ for (let i = 0; i < descendants.length; i++) {
2465
+ const descendant = descendants[i];
2466
+ if (this._isAriaHidden(descendant)) {
2467
+ continue;
2468
+ }
2469
+ return descendant;
2470
+ }
2471
+ return null;
2472
+ }
2473
+ accept(element) {
2474
+ return this._isInteractive(element);
2475
+ }
2476
+ validate(element) {
2477
+ if (this._isAriaHidden(element)) {
2478
+ return null;
2479
+ }
2480
+ const nestedElement = this._findNestedInteractive(element);
2481
+ if (nestedElement) {
2482
+ const elementTag = element.tagName.toLowerCase();
2483
+ const elementRole = element.getAttribute("role");
2484
+ const nestedTag = nestedElement.tagName.toLowerCase();
2485
+ const nestedRole = nestedElement.getAttribute("role");
2486
+ const elementDesc = elementRole ? `${elementTag}[role="${elementRole}"]` : elementTag;
2487
+ const nestedDesc = nestedRole ? `${nestedTag}[role="${nestedRole}"]` : nestedTag;
2488
+ return {
2489
+ issue: isElementVisible(element) ? {
2490
+ id: "nested-interactive",
2491
+ message: `Interactive element <${elementDesc}> contains a nested interactive element <${nestedDesc}>. This can confuse users and assistive technologies.`,
2492
+ element,
2493
+ rel: nestedElement,
2494
+ help: "https://dequeuniversity.com/rules/axe/4.4/nested-interactive"
2495
+ } : void 0
2496
+ };
2497
+ }
2498
+ return null;
2499
+ }
2500
+ };
2501
+
2502
+ // src/rules/tabindex.ts
2503
+ var INTERACTIVE_ELEMENTS = /* @__PURE__ */ new Set([
2504
+ "A",
2505
+ "BUTTON",
2506
+ "INPUT",
2507
+ "SELECT",
2508
+ "TEXTAREA",
2509
+ "DETAILS",
2510
+ "SUMMARY",
2511
+ "AUDIO",
2512
+ "VIDEO"
2513
+ ]);
2514
+ var INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
2515
+ "button",
2516
+ "link",
2517
+ "checkbox",
2518
+ "radio",
2519
+ "textbox",
2520
+ "combobox",
2521
+ "listbox",
2522
+ "menu",
2523
+ "menubar",
2524
+ "menuitem",
2525
+ "menuitemcheckbox",
2526
+ "menuitemradio",
2527
+ "option",
2528
+ "searchbox",
2529
+ "slider",
2530
+ "spinbutton",
2531
+ "switch",
2532
+ "tab",
2533
+ "tablist",
2534
+ "tree",
2535
+ "treegrid",
2536
+ "treeitem",
2537
+ "grid",
2538
+ "gridcell"
2539
+ ]);
2540
+ var TabIndexRule = class extends ValidationRule {
2541
+ constructor() {
2542
+ super(...arguments);
2543
+ __publicField(this, "type", 2 /* Warning */);
2544
+ __publicField(this, "name", "tabindex");
2545
+ __publicField(this, "anchored", true);
2546
+ }
2547
+ accept(element) {
2548
+ return element.hasAttribute("tabindex");
2549
+ }
2550
+ isInteractiveElement(element) {
2551
+ if (INTERACTIVE_ELEMENTS.has(element.tagName)) {
2552
+ if (element.hasAttribute("disabled")) {
2553
+ return false;
2554
+ }
2555
+ if (element.tagName === "A" && !element.hasAttribute("href")) {
2556
+ return false;
2557
+ }
2558
+ return true;
2559
+ }
2560
+ const role = element.getAttribute("role");
2561
+ if (role && INTERACTIVE_ROLES.has(role)) {
2562
+ return true;
2563
+ }
2564
+ if (element.isContentEditable) {
2565
+ return true;
2566
+ }
2567
+ return false;
2568
+ }
2569
+ validate(element) {
2570
+ const tabindex = parseInt(element.getAttribute("tabindex") || "0", 10);
2571
+ if (tabindex > 0 && this.isInteractiveElement(element)) {
2572
+ return {
2573
+ issue: {
2574
+ id: "tabindex",
2575
+ message: `Avoid positive tabindex values (found: ${tabindex})`,
2576
+ element,
2577
+ help: "https://dequeuniversity.com/rules/axe/4.2/tabindex"
2578
+ }
2579
+ };
2580
+ }
2581
+ if (!this.isInteractiveElement(element)) {
2582
+ return {
2583
+ issue: {
2584
+ id: "tabindex-non-interactive",
2585
+ message: `Avoid using tabindex on non-interactive elements (<${element.tagName.toLowerCase()}>). Consider adding an interactive role or making the element naturally interactive.`,
2586
+ element,
2587
+ help: "https://dequeuniversity.com/rules/axe/4.2/tabindex"
2588
+ }
2589
+ };
2590
+ }
2591
+ return null;
2592
+ }
2593
+ };
2104
2594
  // Annotate the CommonJS export names for ESM import in node:
2105
2595
  0 && (module.exports = {
2106
2596
  AbleDOM,
@@ -2112,6 +2602,9 @@ var CustomNotifyRule = class extends ValidationRule {
2112
2602
  FindElementRule,
2113
2603
  FocusLostRule,
2114
2604
  FocusableElementLabelRule,
2605
+ NestedInteractiveElementRule,
2606
+ RequiredParentRule,
2607
+ TabIndexRule,
2115
2608
  ValidationRule,
2116
2609
  ValidationRuleType,
2117
2610
  hasAccessibilityAttribute,