accented 0.0.0-20250424114613 → 0.0.0-20250618181418
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/README.md +0 -209
- package/dist/accented.d.ts +2 -2
- package/dist/accented.d.ts.map +1 -1
- package/dist/accented.js +24 -20
- package/dist/accented.js.map +1 -1
- package/dist/common/tokens.d.ts +2 -0
- package/dist/common/tokens.d.ts.map +1 -0
- package/dist/common/tokens.js +2 -0
- package/dist/common/tokens.js.map +1 -0
- package/dist/dom-updater.d.ts +1 -1
- package/dist/dom-updater.d.ts.map +1 -1
- package/dist/dom-updater.js +14 -13
- package/dist/dom-updater.js.map +1 -1
- package/dist/elements/accented-dialog.d.ts +2 -3
- package/dist/elements/accented-dialog.d.ts.map +1 -1
- package/dist/elements/accented-dialog.js +14 -8
- package/dist/elements/accented-dialog.js.map +1 -1
- package/dist/elements/accented-trigger.d.ts +3 -4
- package/dist/elements/accented-trigger.d.ts.map +1 -1
- package/dist/elements/accented-trigger.js +8 -10
- package/dist/elements/accented-trigger.js.map +1 -1
- package/dist/fullscreen-listener.d.ts +1 -1
- package/dist/fullscreen-listener.d.ts.map +1 -1
- package/dist/fullscreen-listener.js +3 -4
- package/dist/fullscreen-listener.js.map +1 -1
- package/dist/intersection-observer.d.ts +1 -1
- package/dist/intersection-observer.d.ts.map +1 -1
- package/dist/intersection-observer.js +12 -6
- package/dist/intersection-observer.js.map +1 -1
- package/dist/log-and-rethrow.d.ts +1 -1
- package/dist/log-and-rethrow.d.ts.map +1 -1
- package/dist/log-and-rethrow.js +2 -3
- package/dist/log-and-rethrow.js.map +1 -1
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -2
- package/dist/logger.js.map +1 -1
- package/dist/register-elements.d.ts +1 -1
- package/dist/register-elements.d.ts.map +1 -1
- package/dist/register-elements.js +6 -7
- package/dist/register-elements.js.map +1 -1
- package/dist/resize-listener.d.ts +1 -1
- package/dist/resize-listener.d.ts.map +1 -1
- package/dist/resize-listener.js +3 -4
- package/dist/resize-listener.js.map +1 -1
- package/dist/scanner.d.ts +2 -2
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +25 -27
- package/dist/scanner.js.map +1 -1
- package/dist/scroll-listeners.d.ts +1 -1
- package/dist/scroll-listeners.d.ts.map +1 -1
- package/dist/scroll-listeners.js +3 -4
- package/dist/scroll-listeners.js.map +1 -1
- package/dist/state.d.ts +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +4 -5
- package/dist/state.js.map +1 -1
- package/dist/task-queue.d.ts +2 -2
- package/dist/task-queue.d.ts.map +1 -1
- package/dist/task-queue.js +1 -1
- package/dist/task-queue.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/are-elements-with-issues-equal.d.ts +2 -2
- package/dist/utils/are-elements-with-issues-equal.d.ts.map +1 -1
- package/dist/utils/are-elements-with-issues-equal.js +3 -3
- package/dist/utils/are-elements-with-issues-equal.js.map +1 -1
- package/dist/utils/are-issue-sets-equal.d.ts +2 -2
- package/dist/utils/are-issue-sets-equal.d.ts.map +1 -1
- package/dist/utils/are-issue-sets-equal.js +3 -3
- package/dist/utils/are-issue-sets-equal.js.map +1 -1
- package/dist/utils/containing-blocks.d.ts.map +1 -1
- package/dist/utils/containing-blocks.js +1 -1
- package/dist/utils/containing-blocks.js.map +1 -1
- package/dist/utils/contains.d.ts +1 -1
- package/dist/utils/contains.d.ts.map +1 -1
- package/dist/utils/contains.js +1 -1
- package/dist/utils/contains.js.map +1 -1
- package/dist/utils/deduplicate-nodes.js +0 -1
- package/dist/utils/deduplicate-nodes.js.map +1 -1
- package/dist/utils/deep-merge.d.ts +1 -1
- package/dist/utils/deep-merge.d.ts.map +1 -1
- package/dist/utils/deep-merge.js +6 -5
- package/dist/utils/deep-merge.js.map +1 -1
- package/dist/utils/dom-helpers.d.ts.map +1 -1
- package/dist/utils/dom-helpers.js +4 -2
- package/dist/utils/dom-helpers.js.map +1 -1
- package/dist/utils/ensure-non-empty.d.ts +1 -1
- package/dist/utils/ensure-non-empty.d.ts.map +1 -1
- package/dist/utils/ensure-non-empty.js +2 -2
- package/dist/utils/ensure-non-empty.js.map +1 -1
- package/dist/utils/get-element-html.d.ts +1 -1
- package/dist/utils/get-element-html.d.ts.map +1 -1
- package/dist/utils/get-element-html.js +4 -2
- package/dist/utils/get-element-html.js.map +1 -1
- package/dist/utils/get-element-position.d.ts +2 -2
- package/dist/utils/get-element-position.d.ts.map +1 -1
- package/dist/utils/get-element-position.js +21 -25
- package/dist/utils/get-element-position.js.map +1 -1
- package/dist/utils/get-parent.d.ts +1 -1
- package/dist/utils/get-parent.d.ts.map +1 -1
- package/dist/utils/get-parent.js +1 -1
- package/dist/utils/get-parent.js.map +1 -1
- package/dist/utils/get-scan-context.d.ts +2 -2
- package/dist/utils/get-scan-context.d.ts.map +1 -1
- package/dist/utils/get-scan-context.js +9 -9
- package/dist/utils/get-scan-context.js.map +1 -1
- package/dist/utils/get-scrollable-ancestors.d.ts +1 -1
- package/dist/utils/get-scrollable-ancestors.d.ts.map +1 -1
- package/dist/utils/get-scrollable-ancestors.js +5 -5
- package/dist/utils/get-scrollable-ancestors.js.map +1 -1
- package/dist/utils/is-node-in-scan-context.d.ts +2 -2
- package/dist/utils/is-node-in-scan-context.d.ts.map +1 -1
- package/dist/utils/is-node-in-scan-context.js +5 -5
- package/dist/utils/is-node-in-scan-context.js.map +1 -1
- package/dist/utils/is-non-empty.d.ts +2 -0
- package/dist/utils/is-non-empty.d.ts.map +1 -0
- package/dist/utils/is-non-empty.js +4 -0
- package/dist/utils/is-non-empty.js.map +1 -0
- package/dist/utils/normalize-context.d.ts +2 -2
- package/dist/utils/normalize-context.d.ts.map +1 -1
- package/dist/utils/normalize-context.js +10 -8
- package/dist/utils/normalize-context.js.map +1 -1
- package/dist/utils/recalculate-positions.d.ts +1 -1
- package/dist/utils/recalculate-positions.d.ts.map +1 -1
- package/dist/utils/recalculate-positions.js +5 -5
- package/dist/utils/recalculate-positions.js.map +1 -1
- package/dist/utils/recalculate-scrollable-ancestors.d.ts +1 -1
- package/dist/utils/recalculate-scrollable-ancestors.d.ts.map +1 -1
- package/dist/utils/recalculate-scrollable-ancestors.js +4 -4
- package/dist/utils/recalculate-scrollable-ancestors.js.map +1 -1
- package/dist/utils/shadow-dom-aware-mutation-observer.d.ts +1 -1
- package/dist/utils/shadow-dom-aware-mutation-observer.d.ts.map +1 -1
- package/dist/utils/shadow-dom-aware-mutation-observer.js +19 -22
- package/dist/utils/shadow-dom-aware-mutation-observer.js.map +1 -1
- package/dist/utils/supports-anchor-positioning.d.ts +1 -1
- package/dist/utils/supports-anchor-positioning.d.ts.map +1 -1
- package/dist/utils/supports-anchor-positioning.js +1 -1
- package/dist/utils/supports-anchor-positioning.js.map +1 -1
- package/dist/utils/transform-violations.d.ts +2 -2
- package/dist/utils/transform-violations.d.ts.map +1 -1
- package/dist/utils/transform-violations.js +9 -9
- package/dist/utils/transform-violations.js.map +1 -1
- package/dist/utils/update-elements-with-issues.d.ts +3 -3
- package/dist/utils/update-elements-with-issues.d.ts.map +1 -1
- package/dist/utils/update-elements-with-issues.js +34 -29
- package/dist/utils/update-elements-with-issues.js.map +1 -1
- package/dist/validate-options.d.ts +2 -2
- package/dist/validate-options.d.ts.map +1 -1
- package/dist/validate-options.js +24 -23
- package/dist/validate-options.js.map +1 -1
- package/package.json +5 -3
- package/src/accented.test.ts +2 -2
- package/src/accented.ts +34 -26
- package/src/common/tokens.ts +1 -0
- package/src/dom-updater.ts +26 -19
- package/src/elements/accented-dialog.ts +69 -43
- package/src/elements/accented-trigger.ts +52 -43
- package/src/fullscreen-listener.ts +15 -11
- package/src/intersection-observer.ts +27 -16
- package/src/log-and-rethrow.ts +2 -3
- package/src/logger.ts +8 -6
- package/src/register-elements.ts +7 -7
- package/src/resize-listener.ts +15 -11
- package/src/scanner.ts +55 -41
- package/src/scroll-listeners.ts +27 -19
- package/src/state.ts +24 -21
- package/src/task-queue.test.ts +5 -4
- package/src/task-queue.ts +2 -2
- package/src/types.ts +52 -53
- package/src/utils/are-elements-with-issues-equal.ts +7 -5
- package/src/utils/are-issue-sets-equal.test.ts +10 -6
- package/src/utils/are-issue-sets-equal.ts +8 -6
- package/src/utils/containing-blocks.ts +6 -3
- package/src/utils/contains.test.ts +2 -2
- package/src/utils/contains.ts +1 -1
- package/src/utils/deduplicate-nodes.ts +1 -1
- package/src/utils/deep-merge.test.ts +8 -1
- package/src/utils/deep-merge.ts +11 -8
- package/src/utils/dom-helpers.ts +6 -2
- package/src/utils/ensure-non-empty.ts +2 -2
- package/src/utils/get-element-html.ts +4 -2
- package/src/utils/get-element-position.ts +37 -24
- package/src/utils/get-parent.ts +1 -1
- package/src/utils/get-scan-context.test.ts +14 -8
- package/src/utils/get-scan-context.ts +12 -15
- package/src/utils/get-scrollable-ancestors.ts +8 -5
- package/src/utils/is-node-in-scan-context.test.ts +3 -3
- package/src/utils/is-node-in-scan-context.ts +6 -6
- package/src/utils/is-non-empty.ts +3 -0
- package/src/utils/normalize-context.test.ts +9 -9
- package/src/utils/normalize-context.ts +17 -10
- package/src/utils/recalculate-positions.ts +5 -5
- package/src/utils/recalculate-scrollable-ancestors.ts +4 -4
- package/src/utils/shadow-dom-aware-mutation-observer.ts +21 -24
- package/src/utils/supports-anchor-positioning.ts +3 -3
- package/src/utils/transform-violations.test.ts +22 -20
- package/src/utils/transform-violations.ts +14 -10
- package/src/utils/update-elements-with-issues.test.ts +49 -49
- package/src/utils/update-elements-with-issues.ts +96 -71
- package/src/validate-options.ts +91 -38
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import type { Context, ScanContext } from '../types';
|
|
2
|
-
import contains from './contains.js';
|
|
1
|
+
import type { Context, ScanContext } from '../types.ts';
|
|
2
|
+
import { contains } from './contains.js';
|
|
3
3
|
import { deduplicateNodes } from './deduplicate-nodes.js';
|
|
4
|
-
import isNodeInScanContext from './is-node-in-scan-context.js';
|
|
5
|
-
import normalizeContext from './normalize-context.js';
|
|
4
|
+
import { isNodeInScanContext } from './is-node-in-scan-context.js';
|
|
5
|
+
import { normalizeContext } from './normalize-context.js';
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
const {
|
|
9
|
-
include: contextInclude,
|
|
10
|
-
exclude: contextExclude
|
|
11
|
-
} = normalizeContext(context);
|
|
7
|
+
export function getScanContext(nodes: Array<Node>, context: Context): ScanContext {
|
|
8
|
+
const { include: contextInclude, exclude: contextExclude } = normalizeContext(context);
|
|
12
9
|
|
|
13
10
|
// Filter only nodes that are included by context (see isNodeInContext above).
|
|
14
|
-
const nodesInContext = nodes.filter(node =>
|
|
11
|
+
const nodesInContext = nodes.filter((node) =>
|
|
15
12
|
isNodeInScanContext(node, {
|
|
16
13
|
include: contextInclude,
|
|
17
|
-
exclude: contextExclude
|
|
18
|
-
})
|
|
14
|
+
exclude: contextExclude,
|
|
15
|
+
}),
|
|
19
16
|
);
|
|
20
17
|
|
|
21
18
|
const include: Array<Node> = [];
|
|
@@ -26,14 +23,14 @@ export default function getScanContext(nodes: Array<Node>, context: Context): Sc
|
|
|
26
23
|
|
|
27
24
|
// Now add any included and excluded context nodes that are contained by any of the original nodes.
|
|
28
25
|
for (const node of nodes) {
|
|
29
|
-
const includeDescendants = contextInclude.filter(item => contains(node, item));
|
|
26
|
+
const includeDescendants = contextInclude.filter((item) => contains(node, item));
|
|
30
27
|
include.push(...includeDescendants);
|
|
31
|
-
const excludeDescendants = contextExclude.filter(item => contains(node, item));
|
|
28
|
+
const excludeDescendants = contextExclude.filter((item) => contains(node, item));
|
|
32
29
|
exclude.push(...excludeDescendants);
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
return {
|
|
36
33
|
include: deduplicateNodes(include),
|
|
37
|
-
exclude: deduplicateNodes(exclude)
|
|
34
|
+
exclude: deduplicateNodes(exclude),
|
|
38
35
|
};
|
|
39
36
|
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import getParent from './get-parent.js';
|
|
1
|
+
import { getParent } from './get-parent.js';
|
|
2
2
|
|
|
3
3
|
const scrollableOverflowValues = new Set(['auto', 'scroll', 'hidden']);
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function getScrollableAncestors(element: Element, win: Window) {
|
|
6
6
|
let currentElement: Element | null = element;
|
|
7
|
-
|
|
7
|
+
const scrollableAncestors = new Set<Element>();
|
|
8
8
|
while (true) {
|
|
9
9
|
currentElement = getParent(currentElement);
|
|
10
10
|
if (!currentElement) {
|
|
11
11
|
break;
|
|
12
12
|
}
|
|
13
13
|
const computedStyle = win.getComputedStyle(currentElement);
|
|
14
|
-
if (
|
|
14
|
+
if (
|
|
15
|
+
scrollableOverflowValues.has(computedStyle.overflowX) ||
|
|
16
|
+
scrollableOverflowValues.has(computedStyle.overflowY)
|
|
17
|
+
) {
|
|
15
18
|
scrollableAncestors.add(currentElement);
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
return scrollableAncestors;
|
|
19
|
-
}
|
|
22
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { JSDOM } from 'jsdom';
|
|
2
1
|
import assert from 'node:assert/strict';
|
|
3
|
-
import {suite, test} from 'node:test';
|
|
4
|
-
import
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { JSDOM } from 'jsdom';
|
|
4
|
+
import { isNodeInScanContext } from './is-node-in-scan-context';
|
|
5
5
|
|
|
6
6
|
suite('isNodeInScanContext', () => {
|
|
7
7
|
test('doesn’t include an element if scan context is empty', () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* Adapted from https://github.com/dequelabs/axe-core/blob/fd6239bfc97ebc904044f93f68d7e49137f744ad/lib/core/utils/is-node-in-context.js */
|
|
2
2
|
|
|
3
|
-
import type { ScanContext } from '../types';
|
|
4
|
-
import contains from './contains.js';
|
|
5
|
-
import ensureNonEmpty from './ensure-non-empty.js';
|
|
3
|
+
import type { ScanContext } from '../types.ts';
|
|
4
|
+
import { contains } from './contains.js';
|
|
5
|
+
import { ensureNonEmpty } from './ensure-non-empty.js';
|
|
6
6
|
|
|
7
7
|
function getDeepest(nodes: [Node, ...Node[]]): Node {
|
|
8
8
|
let deepest = nodes[0];
|
|
@@ -14,12 +14,12 @@ function getDeepest(nodes: [Node, ...Node[]]): Node {
|
|
|
14
14
|
return deepest;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export
|
|
18
|
-
const filteredInclude = include.filter(includeNode => contains(includeNode, node));
|
|
17
|
+
export function isNodeInScanContext(node: Node, { include, exclude }: ScanContext): boolean {
|
|
18
|
+
const filteredInclude = include.filter((includeNode) => contains(includeNode, node));
|
|
19
19
|
if (filteredInclude.length === 0) {
|
|
20
20
|
return false;
|
|
21
21
|
}
|
|
22
|
-
const filteredExclude = exclude.filter(excludeNode => contains(excludeNode, node));
|
|
22
|
+
const filteredExclude = exclude.filter((excludeNode) => contains(excludeNode, node));
|
|
23
23
|
if (filteredExclude.length === 0) {
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { JSDOM } from 'jsdom';
|
|
2
1
|
import assert from 'node:assert/strict';
|
|
3
2
|
import { suite, test } from 'node:test';
|
|
4
|
-
import
|
|
3
|
+
import { JSDOM } from 'jsdom';
|
|
4
|
+
import { normalizeContext } from './normalize-context';
|
|
5
5
|
|
|
6
6
|
suite('normalizeContext', () => {
|
|
7
7
|
test('when document is passed, only document is returned in include', () => {
|
|
@@ -11,7 +11,7 @@ suite('normalizeContext', () => {
|
|
|
11
11
|
|
|
12
12
|
assert.deepEqual(normalizedContext, {
|
|
13
13
|
include: [document],
|
|
14
|
-
exclude: []
|
|
14
|
+
exclude: [],
|
|
15
15
|
});
|
|
16
16
|
});
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ suite('normalizeContext', () => {
|
|
|
23
23
|
|
|
24
24
|
assert.deepEqual(normalizedContext, {
|
|
25
25
|
include: [element],
|
|
26
|
-
exclude: []
|
|
26
|
+
exclude: [],
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -41,7 +41,7 @@ suite('normalizeContext', () => {
|
|
|
41
41
|
assert.equal(matchingElements.length, 2);
|
|
42
42
|
assert.deepEqual(normalizedContext, {
|
|
43
43
|
include: matchingElements,
|
|
44
|
-
exclude: []
|
|
44
|
+
exclude: [],
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -57,7 +57,7 @@ suite('normalizeContext', () => {
|
|
|
57
57
|
|
|
58
58
|
assert.deepEqual(normalizedContext, {
|
|
59
59
|
include: [],
|
|
60
|
-
exclude: []
|
|
60
|
+
exclude: [],
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -74,7 +74,7 @@ suite('normalizeContext', () => {
|
|
|
74
74
|
assert.equal(matchingElements.length, 2);
|
|
75
75
|
assert.deepEqual(normalizedContext, {
|
|
76
76
|
include: Array.from(matchingElements),
|
|
77
|
-
exclude: []
|
|
77
|
+
exclude: [],
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -94,12 +94,12 @@ suite('normalizeContext', () => {
|
|
|
94
94
|
shadowRoot.appendChild(matchingElement);
|
|
95
95
|
matchingElements.push(matchingElement);
|
|
96
96
|
}
|
|
97
|
-
const normalizedContext = normalizeContext({fromShadowDom: ['.host', '.matches']});
|
|
97
|
+
const normalizedContext = normalizeContext({ fromShadowDom: ['.host', '.matches'] });
|
|
98
98
|
|
|
99
99
|
assert.equal(matchingElements.length, 2);
|
|
100
100
|
assert.deepEqual(normalizedContext, {
|
|
101
101
|
include: matchingElements,
|
|
102
|
-
exclude: []
|
|
102
|
+
exclude: [],
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
});
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import type { Context, ContextProp,
|
|
2
|
-
import { isNode, isNodeList } from './dom-helpers.js';
|
|
1
|
+
import type { Context, ContextProp, ScanContext, Selector } from '../types.ts';
|
|
3
2
|
import { deduplicateNodes } from './deduplicate-nodes.js';
|
|
3
|
+
import { isNode, isNodeList } from './dom-helpers.js';
|
|
4
|
+
import { isNonEmpty } from './is-non-empty.js';
|
|
4
5
|
|
|
5
|
-
function recursiveSelectAll(
|
|
6
|
-
|
|
6
|
+
function recursiveSelectAll(
|
|
7
|
+
selectors: [string, ...string[]],
|
|
8
|
+
root: Document | ShadowRoot,
|
|
9
|
+
): Array<Node> {
|
|
10
|
+
const nodesOnCurrentLevel = root.querySelectorAll(selectors[0]);
|
|
7
11
|
if (selectors.length === 1) {
|
|
8
12
|
return Array.from(nodesOnCurrentLevel);
|
|
9
13
|
}
|
|
10
14
|
const restSelectors: Array<string> = selectors.slice(1);
|
|
15
|
+
if (!isNonEmpty(restSelectors)) {
|
|
16
|
+
throw new Error('Error: the restSelectors array must not be empty.');
|
|
17
|
+
}
|
|
11
18
|
const selected = [];
|
|
12
19
|
for (const node of nodesOnCurrentLevel) {
|
|
13
20
|
if (node.shadowRoot) {
|
|
@@ -20,24 +27,24 @@ function recursiveSelectAll(selectors: Array<string>, root: Document | ShadowRoo
|
|
|
20
27
|
function selectorToNodes(selector: Selector): Array<Node> {
|
|
21
28
|
if (typeof selector === 'string') {
|
|
22
29
|
return recursiveSelectAll([selector], document);
|
|
23
|
-
}
|
|
30
|
+
}
|
|
31
|
+
if (isNode(selector)) {
|
|
24
32
|
return [selector];
|
|
25
|
-
} else {
|
|
26
|
-
return recursiveSelectAll(selector.fromShadowDom, document);
|
|
27
33
|
}
|
|
34
|
+
return recursiveSelectAll(selector.fromShadowDom, document);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
function contextPropToNodes(contextProp: ContextProp): Array<Node> {
|
|
31
38
|
let nodes: Array<Node> = [];
|
|
32
39
|
if (typeof contextProp === 'object' && (Array.isArray(contextProp) || isNodeList(contextProp))) {
|
|
33
|
-
nodes = Array.from(contextProp).
|
|
40
|
+
nodes = Array.from(contextProp).flatMap((item) => selectorToNodes(item));
|
|
34
41
|
} else {
|
|
35
42
|
nodes = selectorToNodes(contextProp);
|
|
36
43
|
}
|
|
37
44
|
return deduplicateNodes(nodes);
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
export
|
|
47
|
+
export function normalizeContext(context: Context): ScanContext {
|
|
41
48
|
let contextInclude: Array<Node> = [];
|
|
42
49
|
let contextExclude: Array<Node> = [];
|
|
43
50
|
if (typeof context === 'object' && ('include' in context || 'exclude' in context)) {
|
|
@@ -53,6 +60,6 @@ export default function normalizeContext(context: Context): ScanContext {
|
|
|
53
60
|
|
|
54
61
|
return {
|
|
55
62
|
include: contextInclude,
|
|
56
|
-
exclude: contextExclude
|
|
63
|
+
exclude: contextExclude,
|
|
57
64
|
};
|
|
58
65
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { batch } from '@preact/signals-core';
|
|
2
|
+
import { logAndRethrow } from '../log-and-rethrow.js';
|
|
2
3
|
import { extendedElementsWithIssues } from '../state.js';
|
|
3
|
-
import getElementPosition from './get-element-position.js';
|
|
4
|
-
import logAndRethrow from '../log-and-rethrow.js';
|
|
4
|
+
import { getElementPosition } from './get-element-position.js';
|
|
5
5
|
|
|
6
6
|
let frameRequested = false;
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export function recalculatePositions() {
|
|
9
9
|
if (frameRequested) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
@@ -14,11 +14,11 @@ export default function recalculatePositions() {
|
|
|
14
14
|
try {
|
|
15
15
|
frameRequested = false;
|
|
16
16
|
batch(() => {
|
|
17
|
-
|
|
17
|
+
for (const { element, position, visible } of extendedElementsWithIssues.value) {
|
|
18
18
|
if (visible.value && element.isConnected) {
|
|
19
19
|
position.value = getElementPosition(element, window);
|
|
20
20
|
}
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
22
|
});
|
|
23
23
|
} catch (error) {
|
|
24
24
|
logAndRethrow(error);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { batch } from '@preact/signals-core';
|
|
2
2
|
import { extendedElementsWithIssues } from '../state.js';
|
|
3
|
-
import getScrollableAncestors from './get-scrollable-ancestors.js';
|
|
3
|
+
import { getScrollableAncestors } from './get-scrollable-ancestors.js';
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function recalculateScrollableAncestors() {
|
|
6
6
|
batch(() => {
|
|
7
|
-
|
|
7
|
+
for (const { element, scrollableAncestors } of extendedElementsWithIssues.value) {
|
|
8
8
|
if (element.isConnected) {
|
|
9
9
|
scrollableAncestors.value = getScrollableAncestors(element, window);
|
|
10
10
|
}
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
12
|
});
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { isElement, isDocument, isDocumentFragment } from './dom-helpers.js';
|
|
2
1
|
import { getAccentedElementNames } from '../constants.js';
|
|
2
|
+
import { isDocument, isDocumentFragment, isElement } from './dom-helpers.js';
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export function createShadowDOMAwareMutationObserver(name: string, callback: MutationCallback) {
|
|
5
5
|
class ShadowDOMAwareMutationObserver extends MutationObserver {
|
|
6
6
|
#shadowRoots = new Set();
|
|
7
7
|
|
|
@@ -10,22 +10,19 @@ export default function createShadowDOMAwareMutationObserver (name: string, call
|
|
|
10
10
|
constructor(callback: MutationCallback) {
|
|
11
11
|
super((mutations, observer) => {
|
|
12
12
|
const accentedElementNames = getAccentedElementNames(name);
|
|
13
|
-
const childListMutations = mutations
|
|
14
|
-
.filter(mutation => mutation.type === 'childList')
|
|
13
|
+
const childListMutations = mutations.filter((mutation) => mutation.type === 'childList');
|
|
15
14
|
|
|
16
15
|
const newElements = childListMutations
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
.filter(node =>
|
|
20
|
-
.filter(node => !accentedElementNames.includes(node.nodeName.toLowerCase()));
|
|
16
|
+
.flatMap((mutation) => [...mutation.addedNodes])
|
|
17
|
+
.filter((node) => isElement(node))
|
|
18
|
+
.filter((node) => !accentedElementNames.includes(node.nodeName.toLowerCase()));
|
|
21
19
|
|
|
22
20
|
this.#observeShadowRoots(newElements);
|
|
23
21
|
|
|
24
22
|
const removedElements = childListMutations
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
.filter(node =>
|
|
28
|
-
.filter(node => !accentedElementNames.includes(node.nodeName.toLowerCase()));
|
|
23
|
+
.flatMap((mutation) => [...mutation.removedNodes])
|
|
24
|
+
.filter((node) => isElement(node))
|
|
25
|
+
.filter((node) => !accentedElementNames.includes(node.nodeName.toLowerCase()));
|
|
29
26
|
|
|
30
27
|
// Mutation observer has no "unobserve" method, so we're simply deleting
|
|
31
28
|
// the elements from the set of shadow roots.
|
|
@@ -50,28 +47,28 @@ export default function createShadowDOMAwareMutationObserver (name: string, call
|
|
|
50
47
|
|
|
51
48
|
#observeShadowRoots = (elements: Array<Element | Document | DocumentFragment>) => {
|
|
52
49
|
const shadowRoots = elements
|
|
53
|
-
.
|
|
54
|
-
.
|
|
55
|
-
.
|
|
56
|
-
.map(element => element.shadowRoot!);
|
|
50
|
+
.flatMap((element) => [...element.querySelectorAll('*')])
|
|
51
|
+
.filter((element) => element.shadowRoot)
|
|
52
|
+
.map((element) => element.shadowRoot);
|
|
57
53
|
|
|
58
54
|
for (const shadowRoot of shadowRoots) {
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
if (shadowRoot) {
|
|
56
|
+
this.#shadowRoots.add(shadowRoot);
|
|
57
|
+
this.observe(shadowRoot, this.#options);
|
|
58
|
+
}
|
|
61
59
|
}
|
|
62
|
-
}
|
|
60
|
+
};
|
|
63
61
|
|
|
64
62
|
#deleteShadowRoots = (elements: Array<Element | Document | DocumentFragment>) => {
|
|
65
63
|
const shadowRoots = elements
|
|
66
|
-
.
|
|
67
|
-
.
|
|
68
|
-
.
|
|
69
|
-
.map(element => element.shadowRoot!);
|
|
64
|
+
.flatMap((element) => [...element.querySelectorAll('*')])
|
|
65
|
+
.filter((element) => element.shadowRoot)
|
|
66
|
+
.map((element) => element.shadowRoot);
|
|
70
67
|
|
|
71
68
|
for (const shadowRoot of shadowRoots) {
|
|
72
69
|
this.#shadowRoots.delete(shadowRoot);
|
|
73
70
|
}
|
|
74
|
-
}
|
|
71
|
+
};
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
return new ShadowDOMAwareMutationObserver(callback);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type WindowWithCSS = Window & {
|
|
2
|
-
CSS: typeof CSS
|
|
3
|
-
}
|
|
2
|
+
CSS: typeof CSS;
|
|
3
|
+
};
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function supportsAnchorPositioning(win: WindowWithCSS) {
|
|
6
6
|
return win.CSS.supports('anchor-name: --foo') && win.CSS.supports('position-anchor: --foo');
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import {suite, test} from 'node:test';
|
|
3
|
-
import transformViolations from './transform-violations';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { transformViolations } from './transform-violations';
|
|
4
4
|
|
|
5
5
|
import type { AxeResults } from 'axe-core';
|
|
6
6
|
type Violation = AxeResults['violations'][number];
|
|
@@ -12,7 +12,7 @@ const commonViolationProps1: Omit<Violation, 'nodes'> = {
|
|
|
12
12
|
helpUrl: 'http://example.com',
|
|
13
13
|
description: 'description1',
|
|
14
14
|
tags: [],
|
|
15
|
-
impact: 'serious'
|
|
15
|
+
impact: 'serious',
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const commonViolationProps2: Omit<Violation, 'nodes'> = {
|
|
@@ -21,51 +21,51 @@ const commonViolationProps2: Omit<Violation, 'nodes'> = {
|
|
|
21
21
|
helpUrl: 'http://example.com',
|
|
22
22
|
description: 'description2',
|
|
23
23
|
tags: [],
|
|
24
|
-
impact: 'serious'
|
|
24
|
+
impact: 'serious',
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const getRootNode = (): Node => ({} as Node
|
|
27
|
+
const getRootNode = (): Node => ({}) as Node;
|
|
28
28
|
|
|
29
29
|
// @ts-expect-error element is not HTMLElement
|
|
30
|
-
const element1: HTMLElement = {getRootNode};
|
|
30
|
+
const element1: HTMLElement = { getRootNode };
|
|
31
31
|
// @ts-expect-error element is not HTMLElement
|
|
32
|
-
const element2: HTMLElement = {getRootNode};
|
|
32
|
+
const element2: HTMLElement = { getRootNode };
|
|
33
33
|
// @ts-expect-error element is not HTMLElement
|
|
34
|
-
const element3: HTMLElement = {getRootNode};
|
|
34
|
+
const element3: HTMLElement = { getRootNode };
|
|
35
35
|
|
|
36
36
|
const commonNodeProps = {
|
|
37
37
|
html: '<div></div>',
|
|
38
38
|
any: [],
|
|
39
39
|
all: [],
|
|
40
|
-
none: []
|
|
40
|
+
none: [],
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
const node1: Node = {
|
|
44
44
|
...commonNodeProps,
|
|
45
45
|
element: element1,
|
|
46
46
|
target: ['div'],
|
|
47
|
-
failureSummary: 'summary1'
|
|
47
|
+
failureSummary: 'summary1',
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const node2: Node = {
|
|
51
51
|
...commonNodeProps,
|
|
52
52
|
element: element2,
|
|
53
53
|
target: ['div'],
|
|
54
|
-
failureSummary: 'summary2'
|
|
54
|
+
failureSummary: 'summary2',
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
const node3: Node = {
|
|
58
58
|
...commonNodeProps,
|
|
59
59
|
element: element3,
|
|
60
60
|
target: ['div'],
|
|
61
|
-
failureSummary: 'summary3'
|
|
61
|
+
failureSummary: 'summary3',
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
suite('transformViolations', () => {
|
|
65
65
|
test('one violation, one element', () => {
|
|
66
66
|
const violation: Violation = {
|
|
67
67
|
...commonViolationProps1,
|
|
68
|
-
nodes: [node1]
|
|
68
|
+
nodes: [node1],
|
|
69
69
|
};
|
|
70
70
|
const elementsWithIssues = transformViolations([violation], 'accented');
|
|
71
71
|
assert.equal(elementsWithIssues.length, 1);
|
|
@@ -78,15 +78,17 @@ suite('transformViolations', () => {
|
|
|
78
78
|
test('two violations, two elements each', () => {
|
|
79
79
|
const violation1: Violation = {
|
|
80
80
|
...commonViolationProps1,
|
|
81
|
-
nodes: [node1, node2]
|
|
81
|
+
nodes: [node1, node2],
|
|
82
82
|
};
|
|
83
83
|
const violation2: Violation = {
|
|
84
84
|
...commonViolationProps2,
|
|
85
|
-
nodes: [node1, node3]
|
|
85
|
+
nodes: [node1, node3],
|
|
86
86
|
};
|
|
87
87
|
const elementsWithIssues = transformViolations([violation1, violation2], 'accented');
|
|
88
88
|
assert.equal(elementsWithIssues.length, 3);
|
|
89
|
-
const elementWithTwoIssues = elementsWithIssues.find(
|
|
89
|
+
const elementWithTwoIssues = elementsWithIssues.find(
|
|
90
|
+
(elementWithIssues) => elementWithIssues.element === element1,
|
|
91
|
+
);
|
|
90
92
|
assert.equal(elementWithTwoIssues?.issues.length, 2);
|
|
91
93
|
});
|
|
92
94
|
|
|
@@ -96,11 +98,11 @@ suite('transformViolations', () => {
|
|
|
96
98
|
element: element1,
|
|
97
99
|
// A target array whose length is > 1 signifies an element in an iframe
|
|
98
100
|
target: ['iframe', 'div'],
|
|
99
|
-
failureSummary: 'summary1'
|
|
101
|
+
failureSummary: 'summary1',
|
|
100
102
|
};
|
|
101
103
|
const violation: Violation = {
|
|
102
104
|
...commonViolationProps1,
|
|
103
|
-
nodes: [node]
|
|
105
|
+
nodes: [node],
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
const elementsWithIssues = transformViolations([violation], 'accented');
|
|
@@ -113,11 +115,11 @@ suite('transformViolations', () => {
|
|
|
113
115
|
element: element1,
|
|
114
116
|
// A target that contains an array within the outer array signifies an element in shadow DOM
|
|
115
117
|
target: [['div', 'div']],
|
|
116
|
-
failureSummary: 'summary1'
|
|
118
|
+
failureSummary: 'summary1',
|
|
117
119
|
};
|
|
118
120
|
const violation: Violation = {
|
|
119
121
|
...commonViolationProps1,
|
|
120
|
-
nodes: [node]
|
|
122
|
+
nodes: [node],
|
|
121
123
|
};
|
|
122
124
|
|
|
123
125
|
const elementsWithIssues = transformViolations([violation], 'accented');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AxeResults, ImpactValue } from 'axe-core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ElementWithIssues, Issue } from '../types.ts';
|
|
3
3
|
|
|
4
4
|
// This is a list of axe-core violations (their ids) that may be flagged by axe-core
|
|
5
5
|
// as false positives if an Accented trigger is a descendant of the element with the issue.
|
|
@@ -10,12 +10,14 @@ const violationsAffectedByAccentedTriggers = [
|
|
|
10
10
|
'label-content-name-mismatch',
|
|
11
11
|
'list',
|
|
12
12
|
'nested-interactive',
|
|
13
|
-
'scrollable-region-focusable' // The Accented trigger might make the content grow such that scrolling is required.
|
|
13
|
+
'scrollable-region-focusable', // The Accented trigger might make the content grow such that scrolling is required.
|
|
14
14
|
];
|
|
15
15
|
|
|
16
16
|
function maybeCausedByAccented(violationId: string, element: HTMLElement, name: string) {
|
|
17
|
-
return
|
|
18
|
-
|
|
17
|
+
return (
|
|
18
|
+
violationsAffectedByAccentedTriggers.includes(violationId) &&
|
|
19
|
+
Boolean(element.querySelector(`${name}-trigger`))
|
|
20
|
+
);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
function impactCompare(a: ImpactValue, b: ImpactValue) {
|
|
@@ -23,7 +25,7 @@ function impactCompare(a: ImpactValue, b: ImpactValue) {
|
|
|
23
25
|
return impactOrder.indexOf(a) - impactOrder.indexOf(b);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
export
|
|
28
|
+
export function transformViolations(violations: typeof AxeResults.violations, name: string) {
|
|
27
29
|
const elementsWithIssues: Array<ElementWithIssues> = [];
|
|
28
30
|
|
|
29
31
|
for (const violation of violations) {
|
|
@@ -44,17 +46,19 @@ export default function transformViolations(violations: typeof AxeResults.violat
|
|
|
44
46
|
title: violation.help,
|
|
45
47
|
description: node.failureSummary ?? violation.description,
|
|
46
48
|
url: violation.helpUrl,
|
|
47
|
-
impact: violation.impact ?? null
|
|
49
|
+
impact: violation.impact ?? null,
|
|
48
50
|
};
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
+
const existingElement = elementsWithIssues.find(
|
|
52
|
+
(elementWithIssues) => elementWithIssues.element === element,
|
|
53
|
+
);
|
|
54
|
+
if (existingElement === undefined) {
|
|
51
55
|
elementsWithIssues.push({
|
|
52
56
|
element,
|
|
53
57
|
rootNode: element.getRootNode(),
|
|
54
|
-
issues: [issue]
|
|
58
|
+
issues: [issue],
|
|
55
59
|
});
|
|
56
60
|
} else {
|
|
57
|
-
|
|
61
|
+
existingElement.issues.push(issue);
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|