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
package/src/task-queue.test.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import {mock, suite, test} from 'node:test';
|
|
2
|
+
import { mock, suite, test } from 'node:test';
|
|
3
3
|
|
|
4
|
-
import TaskQueue from './task-queue
|
|
4
|
+
import { TaskQueue } from './task-queue';
|
|
5
5
|
|
|
6
|
-
const wait = (duration: number) => new Promise(resolve => setTimeout(resolve, duration));
|
|
6
|
+
const wait = (duration: number) => new Promise((resolve) => setTimeout(resolve, duration));
|
|
7
7
|
|
|
8
|
-
const createAsyncCallback = (duration: number) =>
|
|
8
|
+
const createAsyncCallback = (duration: number) =>
|
|
9
|
+
mock.fn(() => new Promise((resolve) => setTimeout(resolve, duration)));
|
|
9
10
|
|
|
10
11
|
suite('TaskQueue', () => {
|
|
11
12
|
test('callback is not called after a TaskQueue is created, even after a timeout', async () => {
|
package/src/task-queue.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Throttle } from './types';
|
|
1
|
+
import type { Throttle } from './types.ts';
|
|
2
2
|
|
|
3
3
|
type TaskCallback<T> = (items: Array<T>) => void;
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export class TaskQueue<T> {
|
|
6
6
|
#throttle: Throttle;
|
|
7
7
|
#asyncCallback: TaskCallback<T> | null = null;
|
|
8
8
|
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type axe from 'axe-core';
|
|
2
1
|
import type { Signal } from '@preact/signals-core';
|
|
3
|
-
import type
|
|
2
|
+
import type axe from 'axe-core';
|
|
3
|
+
import type { AccentedTrigger } from './elements/accented-trigger.ts';
|
|
4
4
|
|
|
5
5
|
export type Throttle = {
|
|
6
6
|
/**
|
|
@@ -8,7 +8,7 @@ export type Throttle = {
|
|
|
8
8
|
*
|
|
9
9
|
* Default: `1000`.
|
|
10
10
|
* */
|
|
11
|
-
wait?: number
|
|
11
|
+
wait?: number;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* When to run the scan on Accented initialization or on a mutation.
|
|
@@ -17,8 +17,8 @@ export type Throttle = {
|
|
|
17
17
|
*
|
|
18
18
|
* Default: `true`.
|
|
19
19
|
* */
|
|
20
|
-
leading?: boolean
|
|
21
|
-
}
|
|
20
|
+
leading?: boolean;
|
|
21
|
+
};
|
|
22
22
|
|
|
23
23
|
export type Output = {
|
|
24
24
|
/**
|
|
@@ -26,8 +26,8 @@ export type Output = {
|
|
|
26
26
|
*
|
|
27
27
|
* Default: `true`.
|
|
28
28
|
* */
|
|
29
|
-
console?: boolean
|
|
30
|
-
}
|
|
29
|
+
console?: boolean;
|
|
30
|
+
};
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Model context type based on axe.ElementContext,
|
|
@@ -43,27 +43,27 @@ export type SelectorList = Array<Selector> | NodeList;
|
|
|
43
43
|
// The rest of the type is structured the same as in axe-core.
|
|
44
44
|
export type ContextProp = Selector | SelectorList;
|
|
45
45
|
|
|
46
|
-
export type ContextObject =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
export type ContextObject =
|
|
47
|
+
| {
|
|
48
|
+
include: ContextProp;
|
|
49
|
+
exclude?: ContextProp;
|
|
50
|
+
}
|
|
51
|
+
| {
|
|
52
|
+
exclude: ContextProp;
|
|
53
|
+
include?: ContextProp;
|
|
54
|
+
};
|
|
53
55
|
|
|
54
56
|
export type Context = ContextProp | ContextObject;
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
58
|
export const allowedAxeOptions = ['rules', 'runOnly'] as const;
|
|
59
59
|
|
|
60
|
-
export type AxeOptions = Pick<axe.RunOptions, typeof allowedAxeOptions[number]>;
|
|
60
|
+
export type AxeOptions = Pick<axe.RunOptions, (typeof allowedAxeOptions)[number]>;
|
|
61
61
|
|
|
62
62
|
type CallbackParams = {
|
|
63
63
|
/**
|
|
64
64
|
* The most current array of elements with issues.
|
|
65
65
|
* */
|
|
66
|
-
elementsWithIssues: Array<ElementWithIssues
|
|
66
|
+
elementsWithIssues: Array<ElementWithIssues>;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* * `performance`: runtime performance of the last scan. An object:
|
|
@@ -75,17 +75,16 @@ type CallbackParams = {
|
|
|
75
75
|
* or an object with `include` and `exclude` properties (if any nodes were excluded).
|
|
76
76
|
* */
|
|
77
77
|
performance: {
|
|
78
|
-
totalBlockingTime: number
|
|
79
|
-
scan: number
|
|
80
|
-
domUpdate: number
|
|
81
|
-
scanContext: ScanContext | Array<Node
|
|
82
|
-
}
|
|
83
|
-
}
|
|
78
|
+
totalBlockingTime: number;
|
|
79
|
+
scan: number;
|
|
80
|
+
domUpdate: number;
|
|
81
|
+
scanContext: ScanContext | Array<Node>;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
84
|
|
|
85
85
|
export type Callback = (params: CallbackParams) => void;
|
|
86
86
|
|
|
87
87
|
export type AccentedOptions = {
|
|
88
|
-
|
|
89
88
|
/**
|
|
90
89
|
* The `context` parameter for `axe.run()`.
|
|
91
90
|
*
|
|
@@ -102,7 +101,7 @@ export type AccentedOptions = {
|
|
|
102
101
|
*
|
|
103
102
|
* Default: `document`.
|
|
104
103
|
*/
|
|
105
|
-
context?: Context
|
|
104
|
+
context?: Context;
|
|
106
105
|
|
|
107
106
|
/**
|
|
108
107
|
* The `options` parameter for `axe.run()`.
|
|
@@ -118,7 +117,7 @@ export type AccentedOptions = {
|
|
|
118
117
|
*
|
|
119
118
|
* Default: `{}`.
|
|
120
119
|
*/
|
|
121
|
-
axeOptions?: AxeOptions
|
|
120
|
+
axeOptions?: AxeOptions;
|
|
122
121
|
|
|
123
122
|
/**
|
|
124
123
|
* The character sequence that’s used in various elements, attributes and stylesheets that Accented adds to the page.
|
|
@@ -135,24 +134,24 @@ export type AccentedOptions = {
|
|
|
135
134
|
*
|
|
136
135
|
* Default: `accented`.
|
|
137
136
|
*/
|
|
138
|
-
name?: string
|
|
137
|
+
name?: string;
|
|
139
138
|
|
|
140
139
|
/**
|
|
141
140
|
* Output options object.
|
|
142
141
|
* */
|
|
143
|
-
output?: Output
|
|
142
|
+
output?: Output;
|
|
144
143
|
|
|
145
144
|
/**
|
|
146
145
|
* Scan throttling options object.
|
|
147
146
|
* */
|
|
148
|
-
throttle?: Throttle
|
|
147
|
+
throttle?: Throttle;
|
|
149
148
|
|
|
150
149
|
/**
|
|
151
150
|
* A callback that will be called after each scan.
|
|
152
151
|
*
|
|
153
152
|
* Default: `() => {}`.
|
|
154
153
|
* */
|
|
155
|
-
callback?: Callback
|
|
154
|
+
callback?: Callback;
|
|
156
155
|
};
|
|
157
156
|
|
|
158
157
|
/**
|
|
@@ -162,41 +161,41 @@ export type AccentedOptions = {
|
|
|
162
161
|
export type DisableAccented = () => void;
|
|
163
162
|
|
|
164
163
|
export type Position = {
|
|
165
|
-
left: number
|
|
166
|
-
top: number
|
|
167
|
-
width: number
|
|
168
|
-
height: number
|
|
164
|
+
left: number;
|
|
165
|
+
top: number;
|
|
166
|
+
width: number;
|
|
167
|
+
height: number;
|
|
169
168
|
};
|
|
170
169
|
|
|
171
170
|
export type Issue = {
|
|
172
|
-
id: string
|
|
173
|
-
title: string
|
|
174
|
-
description: string
|
|
175
|
-
url: string
|
|
176
|
-
impact: axe.ImpactValue
|
|
171
|
+
id: string;
|
|
172
|
+
title: string;
|
|
173
|
+
description: string;
|
|
174
|
+
url: string;
|
|
175
|
+
impact: axe.ImpactValue;
|
|
177
176
|
};
|
|
178
177
|
|
|
179
178
|
export type BaseElementWithIssues = {
|
|
180
|
-
element: HTMLElement | SVGElement
|
|
181
|
-
rootNode: Node
|
|
179
|
+
element: HTMLElement | SVGElement;
|
|
180
|
+
rootNode: Node;
|
|
182
181
|
};
|
|
183
182
|
|
|
184
183
|
export type ElementWithIssues = BaseElementWithIssues & {
|
|
185
|
-
issues: Array<Issue
|
|
184
|
+
issues: Array<Issue>;
|
|
186
185
|
};
|
|
187
186
|
|
|
188
187
|
export type ExtendedElementWithIssues = BaseElementWithIssues & {
|
|
189
|
-
issues: Signal<ElementWithIssues['issues']
|
|
190
|
-
visible: Signal<boolean
|
|
191
|
-
trigger: AccentedTrigger
|
|
192
|
-
position: Signal<Position
|
|
193
|
-
skipRender: boolean
|
|
194
|
-
anchorNameValue: string
|
|
195
|
-
scrollableAncestors: Signal<Set<Element
|
|
196
|
-
id: number
|
|
188
|
+
issues: Signal<ElementWithIssues['issues']>;
|
|
189
|
+
visible: Signal<boolean>;
|
|
190
|
+
trigger: AccentedTrigger;
|
|
191
|
+
position: Signal<Position>;
|
|
192
|
+
skipRender: boolean;
|
|
193
|
+
anchorNameValue: string;
|
|
194
|
+
scrollableAncestors: Signal<Set<Element>>;
|
|
195
|
+
id: number;
|
|
197
196
|
};
|
|
198
197
|
|
|
199
198
|
export type ScanContext = {
|
|
200
|
-
include: Array<Node
|
|
201
|
-
exclude: Array<Node
|
|
199
|
+
include: Array<Node>;
|
|
200
|
+
exclude: Array<Node>;
|
|
202
201
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { BaseElementWithIssues } from
|
|
1
|
+
import type { BaseElementWithIssues } from '../types.ts';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function areElementsWithIssuesEqual(
|
|
4
4
|
elementWithIssues1: BaseElementWithIssues,
|
|
5
|
-
elementWithIssues2: BaseElementWithIssues
|
|
5
|
+
elementWithIssues2: BaseElementWithIssues,
|
|
6
6
|
) {
|
|
7
|
-
return
|
|
8
|
-
|
|
7
|
+
return (
|
|
8
|
+
elementWithIssues1.element === elementWithIssues2.element &&
|
|
9
|
+
elementWithIssues1.rootNode === elementWithIssues2.rootNode
|
|
10
|
+
);
|
|
9
11
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import {suite, test} from 'node:test';
|
|
3
|
-
import areIssueSetsEqual from './are-issue-sets-equal.js';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
4
3
|
import type { Issue } from '../types';
|
|
4
|
+
import { areIssueSetsEqual } from './are-issue-sets-equal';
|
|
5
5
|
|
|
6
6
|
const issue1: Issue = {
|
|
7
7
|
id: 'id1',
|
|
8
8
|
title: 'title1',
|
|
9
9
|
description: 'description1',
|
|
10
10
|
url: 'http://example.com',
|
|
11
|
-
impact: 'serious'
|
|
11
|
+
impact: 'serious',
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const issue2: Issue = {
|
|
@@ -16,18 +16,22 @@ const issue2: Issue = {
|
|
|
16
16
|
title: 'title2',
|
|
17
17
|
description: 'description2',
|
|
18
18
|
url: 'http://example.com',
|
|
19
|
-
impact: 'serious'
|
|
19
|
+
impact: 'serious',
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
// @ts-expect-error
|
|
23
|
-
const issue2Clone: Issue = Object.keys(issue2).reduce((obj, key) => {
|
|
23
|
+
const issue2Clone: Issue = Object.keys(issue2).reduce((obj, key) => {
|
|
24
|
+
// @ts-expect-error
|
|
25
|
+
obj[key] = issue2[key];
|
|
26
|
+
return obj;
|
|
27
|
+
}, {});
|
|
24
28
|
|
|
25
29
|
const issue3: Issue = {
|
|
26
30
|
id: 'id3',
|
|
27
31
|
title: 'title3',
|
|
28
32
|
description: 'description3',
|
|
29
33
|
url: 'http://example.com',
|
|
30
|
-
impact: 'serious'
|
|
34
|
+
impact: 'serious',
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
suite('areIssueSetsEqual', () => {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { Issue } from '../types';
|
|
1
|
+
import type { Issue } from '../types.ts';
|
|
2
2
|
|
|
3
3
|
const issueProps: Array<keyof Issue> = ['id', 'title', 'description', 'url', 'impact'];
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
return
|
|
7
|
-
issues1.
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export function areIssueSetsEqual(issues1: Array<Issue>, issues2: Array<Issue>) {
|
|
6
|
+
return (
|
|
7
|
+
issues1.length === issues2.length &&
|
|
8
|
+
issues1.every((issue1) =>
|
|
9
|
+
Boolean(issues2.find((issue2) => issueProps.every((prop) => issue2[prop] === issue1[prop]))),
|
|
10
|
+
)
|
|
11
|
+
);
|
|
10
12
|
}
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* It's only meant to be used during initialization.
|
|
9
9
|
*/
|
|
10
|
-
function testContainingBlockCreation<T extends keyof CSSStyleDeclaration>(
|
|
10
|
+
function testContainingBlockCreation<T extends keyof CSSStyleDeclaration>(
|
|
11
|
+
prop: T,
|
|
12
|
+
value: CSSStyleDeclaration[T],
|
|
13
|
+
) {
|
|
11
14
|
const container = document.createElement('div');
|
|
12
15
|
container.style[prop] = value;
|
|
13
16
|
container.style.position = 'fixed';
|
|
@@ -40,13 +43,13 @@ export function createsContainingBlock(prop: keyof CSSStyleDeclaration) {
|
|
|
40
43
|
|
|
41
44
|
export function initializeContainingBlockSupportSet() {
|
|
42
45
|
type StyleEntry<T extends keyof CSSStyleDeclaration> = {
|
|
43
|
-
[K in T]: { prop: K; value: CSSStyleDeclaration[K] }
|
|
46
|
+
[K in T]: { prop: K; value: CSSStyleDeclaration[K] };
|
|
44
47
|
}[T];
|
|
45
48
|
|
|
46
49
|
const propsToTest: Array<StyleEntry<'filter' | 'backdropFilter' | 'containerType'>> = [
|
|
47
50
|
{ prop: 'filter', value: 'blur(1px)' },
|
|
48
51
|
{ prop: 'backdropFilter', value: 'blur(1px)' },
|
|
49
|
-
{ prop: 'containerType', value: 'size' }
|
|
52
|
+
{ prop: 'containerType', value: 'size' },
|
|
50
53
|
];
|
|
51
54
|
|
|
52
55
|
for (const { prop, value } of propsToTest) {
|
|
@@ -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 { contains } from './contains';
|
|
5
5
|
|
|
6
6
|
suite('contains', () => {
|
|
7
7
|
test('an element contains itself', () => {
|
package/src/utils/contains.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isDocumentFragment, isShadowRoot } from './dom-helpers.js';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function contains(ancestor: Node, descendant: Node): boolean {
|
|
4
4
|
if (ancestor.contains(descendant)) {
|
|
5
5
|
return true;
|
|
6
6
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
3
|
|
|
4
|
-
import deepMerge from './deep-merge';
|
|
4
|
+
import { deepMerge } from './deep-merge';
|
|
5
5
|
|
|
6
6
|
suite('deepMerge', () => {
|
|
7
7
|
test('merges two objects with overlapping keys', () => {
|
|
@@ -31,4 +31,11 @@ suite('deepMerge', () => {
|
|
|
31
31
|
const result = deepMerge(target, source);
|
|
32
32
|
assert.deepEqual(result, { a: [4, 5] });
|
|
33
33
|
});
|
|
34
|
+
|
|
35
|
+
test('handles merging an object into a string in a logical way', () => {
|
|
36
|
+
const target = { a: 'hello' };
|
|
37
|
+
const source = { a: { b: 'bye' } };
|
|
38
|
+
const result = deepMerge(target, source);
|
|
39
|
+
assert.deepEqual(result, { a: { b: 'bye' } });
|
|
40
|
+
});
|
|
34
41
|
});
|
package/src/utils/deep-merge.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
// biome-ignore lint/suspicious/noExplicitAny: I'm not sure how to type this properly
|
|
1
2
|
type AnyObject = Record<string, any>;
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
const isObject = (obj: unknown): obj is AnyObject =>
|
|
5
|
+
typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
|
6
|
+
|
|
7
|
+
export function deepMerge(target: AnyObject, source: AnyObject): AnyObject {
|
|
8
|
+
const output = { ...target };
|
|
5
9
|
for (const key of Object.keys(source)) {
|
|
6
|
-
if (
|
|
7
|
-
if (
|
|
8
|
-
output[key] = source[key];
|
|
9
|
-
} else {
|
|
10
|
+
if (isObject(source[key])) {
|
|
11
|
+
if (isObject(target[key])) {
|
|
10
12
|
output[key] = deepMerge(target[key], source[key]);
|
|
13
|
+
} else {
|
|
14
|
+
output[key] = source[key];
|
|
11
15
|
}
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
16
|
+
} else {
|
|
14
17
|
output[key] = source[key];
|
|
15
18
|
}
|
|
16
19
|
}
|
package/src/utils/dom-helpers.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export function isNode(obj: object): obj is Node {
|
|
2
|
-
return
|
|
3
|
-
'
|
|
2
|
+
return (
|
|
3
|
+
'nodeType' in obj &&
|
|
4
|
+
typeof obj.nodeType === 'number' &&
|
|
5
|
+
'nodeName' in obj &&
|
|
6
|
+
typeof obj.nodeName === 'string'
|
|
7
|
+
);
|
|
4
8
|
}
|
|
5
9
|
|
|
6
10
|
export function isNodeList(obj: object): obj is NodeList {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function ensureNonEmpty<T>(arr: T[]): [T, ...T[]] {
|
|
2
2
|
if (arr.length === 0) {
|
|
3
|
-
throw new Error(
|
|
3
|
+
throw new Error('Array must not be empty');
|
|
4
4
|
}
|
|
5
5
|
return arr as [T, ...T[]];
|
|
6
6
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function getElementHtml(element: Element) {
|
|
2
2
|
const outerHtml = element.outerHTML;
|
|
3
3
|
const innerHtml = element.innerHTML;
|
|
4
4
|
if (!innerHtml) {
|
|
@@ -9,5 +9,7 @@ export default function getElementHtml(element: Element) {
|
|
|
9
9
|
// This shouldn't be happening, but if it does, we can just return the outer HTML.
|
|
10
10
|
return outerHtml;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
const openingTag = outerHtml.slice(0, index);
|
|
13
|
+
const closingTag = outerHtml.slice(index + innerHtml.length);
|
|
14
|
+
return `${openingTag}…${closingTag}`;
|
|
13
15
|
}
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
-
import type { Position } from '../types';
|
|
2
|
-
import { isHtmlElement } from './dom-helpers.js';
|
|
3
|
-
import getParent from './get-parent.js';
|
|
1
|
+
import type { Position } from '../types.ts';
|
|
4
2
|
import { createsContainingBlock } from './containing-blocks.js';
|
|
3
|
+
import { isHtmlElement } from './dom-helpers.js';
|
|
4
|
+
import { getParent } from './get-parent.js';
|
|
5
5
|
|
|
6
6
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_display/Containing_block#identifying_the_containing_block
|
|
7
7
|
function isContainingBlock(element: Element, win: Window): boolean {
|
|
8
8
|
const style = win.getComputedStyle(element);
|
|
9
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
transform,
|
|
11
|
+
perspective,
|
|
12
|
+
contain,
|
|
13
|
+
contentVisibility,
|
|
14
|
+
containerType,
|
|
15
|
+
filter,
|
|
16
|
+
backdropFilter,
|
|
17
|
+
willChange,
|
|
18
|
+
} = style;
|
|
10
19
|
const containItems = contain.split(' ');
|
|
11
20
|
const willChangeItems = willChange.split(/\s*,\s*/);
|
|
12
21
|
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
return (
|
|
23
|
+
transform !== 'none' ||
|
|
24
|
+
perspective !== 'none' ||
|
|
25
|
+
containItems.some((item) => ['layout', 'paint', 'strict', 'content'].includes(item)) ||
|
|
26
|
+
contentVisibility === 'auto' ||
|
|
27
|
+
(createsContainingBlock('containerType') && containerType !== 'normal') ||
|
|
28
|
+
(createsContainingBlock('filter') && filter !== 'none') ||
|
|
29
|
+
(createsContainingBlock('backdropFilter') && backdropFilter !== 'none') ||
|
|
30
|
+
willChangeItems.some((item) =>
|
|
31
|
+
['transform', 'perspective', 'contain', 'filter', 'backdrop-filter'].includes(item),
|
|
32
|
+
)
|
|
33
|
+
);
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
function getNonInitialContainingBlock(element: Element, win: Window): Element | null {
|
|
@@ -39,7 +52,7 @@ function getNonInitialContainingBlock(element: Element, win: Window): Element |
|
|
|
39
52
|
* * The element itself, or one of the element's ancestors has a scale or rotate transform.
|
|
40
53
|
* * The browser doesn't support anchor positioning.
|
|
41
54
|
*/
|
|
42
|
-
export
|
|
55
|
+
export function getElementPosition(element: Element, win: Window): Position {
|
|
43
56
|
const nonInitialContainingBlock = getNonInitialContainingBlock(element, win);
|
|
44
57
|
// If an element has a containing block as an ancestor,
|
|
45
58
|
// and that containing block is not the <html> element (the initial containing block),
|
|
@@ -60,17 +73,17 @@ export default function getElementPosition(element: Element, win: Window): Posit
|
|
|
60
73
|
currentElement = currentElement.offsetParent as HTMLElement | null;
|
|
61
74
|
}
|
|
62
75
|
return { top, left, width, height };
|
|
63
|
-
} else {
|
|
64
|
-
const elementRect = element.getBoundingClientRect();
|
|
65
|
-
const nonInitialContainingBlockRect = nonInitialContainingBlock.getBoundingClientRect();
|
|
66
|
-
return {
|
|
67
|
-
top: elementRect.top - nonInitialContainingBlockRect.top,
|
|
68
|
-
height: elementRect.height,
|
|
69
|
-
left: elementRect.left - nonInitialContainingBlockRect.left,
|
|
70
|
-
width: elementRect.width
|
|
71
|
-
};
|
|
72
76
|
}
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
|
|
78
|
+
const elementRect = element.getBoundingClientRect();
|
|
79
|
+
const nonInitialContainingBlockRect = nonInitialContainingBlock.getBoundingClientRect();
|
|
80
|
+
return {
|
|
81
|
+
top: elementRect.top - nonInitialContainingBlockRect.top,
|
|
82
|
+
height: elementRect.height,
|
|
83
|
+
left: elementRect.left - nonInitialContainingBlockRect.left,
|
|
84
|
+
width: elementRect.width,
|
|
85
|
+
};
|
|
75
86
|
}
|
|
87
|
+
|
|
88
|
+
return element.getBoundingClientRect();
|
|
76
89
|
}
|
package/src/utils/get-parent.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isDocumentFragment, isShadowRoot } from './dom-helpers.js';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function getParent(element: Element): Element | null {
|
|
4
4
|
if (element.parentElement) {
|
|
5
5
|
return element.parentElement;
|
|
6
6
|
}
|
|
@@ -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 { getScanContext } from './get-scan-context';
|
|
5
5
|
|
|
6
6
|
suite('getScanContext', () => {
|
|
7
7
|
test('when context doesn’t overlap with nodes, the result is empty', () => {
|
|
@@ -13,7 +13,7 @@ suite('getScanContext', () => {
|
|
|
13
13
|
|
|
14
14
|
assert.deepEqual(scanContext, {
|
|
15
15
|
include: [],
|
|
16
|
-
exclude: []
|
|
16
|
+
exclude: [],
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -27,7 +27,7 @@ suite('getScanContext', () => {
|
|
|
27
27
|
|
|
28
28
|
assert.deepEqual(scanContext, {
|
|
29
29
|
include: [contextNode],
|
|
30
|
-
exclude: []
|
|
30
|
+
exclude: [],
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -44,13 +44,16 @@ suite('getScanContext', () => {
|
|
|
44
44
|
const { document } = dom.window;
|
|
45
45
|
global.document = document;
|
|
46
46
|
const mutatedNode = document.querySelector('#mutated-node')!;
|
|
47
|
-
const scanContext = getScanContext([mutatedNode], {
|
|
47
|
+
const scanContext = getScanContext([mutatedNode], {
|
|
48
|
+
include: ['.include'],
|
|
49
|
+
exclude: ['.exclude'],
|
|
50
|
+
});
|
|
48
51
|
const innerExclude = document.querySelector('#inner-exclude')!;
|
|
49
52
|
const innerInclude = document.querySelector('#inner-include')!;
|
|
50
53
|
|
|
51
54
|
assert.deepEqual(scanContext, {
|
|
52
55
|
include: [mutatedNode, innerInclude],
|
|
53
|
-
exclude: [innerExclude]
|
|
56
|
+
exclude: [innerExclude],
|
|
54
57
|
});
|
|
55
58
|
});
|
|
56
59
|
|
|
@@ -67,13 +70,16 @@ suite('getScanContext', () => {
|
|
|
67
70
|
const { document } = dom.window;
|
|
68
71
|
global.document = document;
|
|
69
72
|
const mutatedNode = document.querySelector('#mutated-node')!;
|
|
70
|
-
const scanContext = getScanContext([mutatedNode], {
|
|
73
|
+
const scanContext = getScanContext([mutatedNode], {
|
|
74
|
+
include: ['.include'],
|
|
75
|
+
exclude: ['.exclude'],
|
|
76
|
+
});
|
|
71
77
|
const innerExclude = document.querySelector('#inner-exclude')!;
|
|
72
78
|
const innerInclude = document.querySelector('#inner-include')!;
|
|
73
79
|
|
|
74
80
|
assert.deepEqual(scanContext, {
|
|
75
81
|
include: [innerInclude],
|
|
76
|
-
exclude: [innerExclude]
|
|
82
|
+
exclude: [innerExclude],
|
|
77
83
|
});
|
|
78
84
|
});
|
|
79
85
|
});
|