@stackoverflow/stacks 2.5.3 → 2.5.5
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/LICENSE.MD +9 -9
- package/README.md +180 -180
- package/dist/css/stacks.css +104 -122
- package/dist/css/stacks.min.css +1 -1
- package/lib/atomic/border.less +139 -139
- package/lib/atomic/color.less +36 -36
- package/lib/atomic/flex.less +426 -426
- package/lib/atomic/gap.less +44 -44
- package/lib/atomic/grid.less +139 -139
- package/lib/atomic/misc.less +374 -374
- package/lib/atomic/spacing.less +98 -98
- package/lib/atomic/typography.less +264 -264
- package/lib/atomic/width-height.less +194 -194
- package/lib/base/body.less +44 -44
- package/lib/base/configuration-static.less +61 -61
- package/lib/base/fieldset.less +5 -5
- package/lib/base/icon.less +11 -11
- package/lib/base/internal.less +220 -220
- package/lib/base/reset-meyer.less +64 -64
- package/lib/base/reset-normalize.less +449 -449
- package/lib/base/reset.less +20 -20
- package/lib/components/activity-indicator/activity-indicator.less +53 -53
- package/lib/components/anchor/anchor.less +78 -68
- package/lib/components/avatar/avatar.less +108 -108
- package/lib/components/award-bling/award-bling.less +31 -31
- package/lib/components/badge/badge.less +258 -258
- package/lib/components/banner/banner.less +44 -44
- package/lib/components/banner/banner.ts +149 -149
- package/lib/components/block-link/block-link.less +82 -82
- package/lib/components/breadcrumbs/breadcrumbs.less +41 -41
- package/lib/components/button/button.less +473 -473
- package/lib/components/button-group/button-group.less +82 -82
- package/lib/components/card/card.less +37 -37
- package/lib/components/check-control/check-control.less +17 -17
- package/lib/components/check-group/check-group.less +19 -19
- package/lib/components/checkbox_radio/checkbox_radio.less +159 -159
- package/lib/components/code-block/code-block.fixtures.ts +88 -88
- package/lib/components/code-block/code-block.less +116 -116
- package/lib/components/description/description.less +9 -9
- package/lib/components/empty-state/empty-state.less +16 -16
- package/lib/components/expandable/expandable.less +118 -118
- package/lib/components/expandable/expandable.ts +238 -238
- package/lib/components/input-fill/input-fill.less +35 -35
- package/lib/components/input-icon/input-icon.less +45 -45
- package/lib/components/input-message/input-message.less +49 -49
- package/lib/components/input_textarea/input_textarea.less +150 -150
- package/lib/components/label/label.less +110 -110
- package/lib/components/link/link.less +135 -120
- package/lib/components/link-preview/link-preview.less +148 -148
- package/lib/components/menu/menu.less +41 -41
- package/lib/components/modal/modal.less +118 -118
- package/lib/components/modal/modal.ts +383 -383
- package/lib/components/navigation/navigation.less +136 -136
- package/lib/components/navigation/navigation.ts +128 -128
- package/lib/components/notice/notice.less +195 -195
- package/lib/components/page-title/page-title.less +51 -51
- package/lib/components/pagination/pagination.less +65 -65
- package/lib/components/popover/popover.less +159 -157
- package/lib/components/popover/popover.ts +651 -651
- package/lib/components/popover/tooltip.ts +343 -343
- package/lib/components/post-summary/post-summary.less +457 -447
- package/lib/components/progress-bar/progress-bar.less +291 -291
- package/lib/components/prose/prose.less +452 -452
- package/lib/components/select/select.less +138 -138
- package/lib/components/sidebar-widget/sidebar-widget.less +257 -257
- package/lib/components/spinner/spinner.less +103 -103
- package/lib/components/table/table.less +307 -307
- package/lib/components/table/table.ts +296 -296
- package/lib/components/table-container/table-container.less +4 -4
- package/lib/components/tag/tag.less +186 -186
- package/lib/components/toast/toast.less +35 -35
- package/lib/components/toast/toast.ts +357 -357
- package/lib/components/toggle-switch/toggle-switch.less +104 -104
- package/lib/components/topbar/topbar.less +553 -553
- package/lib/components/uploader/uploader.less +205 -205
- package/lib/components/uploader/uploader.ts +207 -207
- package/lib/components/user-card/user-card.less +129 -129
- package/lib/controllers.ts +33 -33
- package/lib/exports/color-mixins.less +283 -283
- package/lib/exports/color-sets.less +711 -711
- package/lib/exports/color.less +65 -65
- package/lib/exports/constants-helpers.less +108 -108
- package/lib/exports/constants-type.less +155 -155
- package/lib/exports/exports.less +15 -15
- package/lib/exports/mixins.less +333 -333
- package/lib/exports/spacing-mixins.less +67 -67
- package/lib/index.ts +32 -32
- package/lib/input-utils.less +41 -41
- package/lib/stacks-dynamic.less +24 -24
- package/lib/stacks-static.less +93 -93
- package/lib/stacks.less +13 -13
- package/lib/stacks.ts +113 -113
- package/lib/test/a11y-test-utils.ts +94 -94
- package/lib/test/assertions.ts +36 -36
- package/lib/test/less-test-utils.ts +28 -28
- package/lib/test/open-wc-testing-patch.d.ts +26 -26
- package/lib/test/test-utils.ts +364 -364
- package/lib/test/visual-test-utils.ts +58 -58
- package/lib/tsconfig.build.json +4 -4
- package/lib/tsconfig.json +17 -17
- package/package.json +115 -115
package/lib/stacks.ts
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
import * as Stimulus from "@hotwired/stimulus";
|
|
2
|
-
|
|
3
|
-
export class StacksApplication extends Stimulus.Application {
|
|
4
|
-
static _initializing = true;
|
|
5
|
-
|
|
6
|
-
load(...definitions: Stimulus.Definition[]): void;
|
|
7
|
-
load(definitions: Stimulus.Definition[]): void;
|
|
8
|
-
load(
|
|
9
|
-
head: Stimulus.Definition | Stimulus.Definition[],
|
|
10
|
-
...rest: Stimulus.Definition[]
|
|
11
|
-
) {
|
|
12
|
-
const definitions = Array.isArray(head) ? head : [head, ...rest];
|
|
13
|
-
|
|
14
|
-
for (const definition of definitions) {
|
|
15
|
-
const hasPrefix = /^s-/.test(definition.identifier);
|
|
16
|
-
if (StacksApplication._initializing && !hasPrefix) {
|
|
17
|
-
throw 'Stacks-created Stimulus controller names must start with "s-".';
|
|
18
|
-
}
|
|
19
|
-
if (!StacksApplication._initializing && hasPrefix) {
|
|
20
|
-
throw 'The "s-" prefix on Stimulus controller names is reserved for Stacks-created controllers.';
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
super.load(definitions);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static start(
|
|
28
|
-
element?: Element,
|
|
29
|
-
schema?: Stimulus.Schema
|
|
30
|
-
): StacksApplication {
|
|
31
|
-
const application = new StacksApplication(element, schema);
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
33
|
-
application.start();
|
|
34
|
-
return application;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static finalize() {
|
|
38
|
-
StacksApplication._initializing = false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const application: Stimulus.Application = StacksApplication.start();
|
|
43
|
-
|
|
44
|
-
export class StacksController extends Stimulus.Controller {
|
|
45
|
-
protected getElementData(element: Element, key: string) {
|
|
46
|
-
return element.getAttribute("data-" + this.identifier + "-" + key);
|
|
47
|
-
}
|
|
48
|
-
protected setElementData(element: Element, key: string, value: string) {
|
|
49
|
-
element.setAttribute("data-" + this.identifier + "-" + key, value);
|
|
50
|
-
}
|
|
51
|
-
protected removeElementData(element: Element, key: string) {
|
|
52
|
-
element.removeAttribute("data-" + this.identifier + "-" + key);
|
|
53
|
-
}
|
|
54
|
-
protected triggerEvent<T>(
|
|
55
|
-
eventName: string,
|
|
56
|
-
detail?: T,
|
|
57
|
-
optionalElement?: Element
|
|
58
|
-
) {
|
|
59
|
-
const namespacedName = this.identifier + ":" + eventName;
|
|
60
|
-
let event: CustomEvent<T>;
|
|
61
|
-
try {
|
|
62
|
-
event = new CustomEvent(namespacedName, {
|
|
63
|
-
bubbles: true,
|
|
64
|
-
cancelable: true,
|
|
65
|
-
detail: detail,
|
|
66
|
-
});
|
|
67
|
-
} catch (ex) {
|
|
68
|
-
// Internet Explorer
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
70
|
-
event = document.createEvent("CustomEvent");
|
|
71
|
-
event.initCustomEvent(namespacedName, true, true, detail);
|
|
72
|
-
}
|
|
73
|
-
(optionalElement || this.element).dispatchEvent(event);
|
|
74
|
-
return event;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ControllerDefinition/createController/addController is here to make
|
|
79
|
-
// it easier to consume Stimulus from ES5 files (without classes)
|
|
80
|
-
export interface ControllerDefinition {
|
|
81
|
-
[name: string]: unknown;
|
|
82
|
-
targets?: string[];
|
|
83
|
-
}
|
|
84
|
-
export function createController(
|
|
85
|
-
controllerDefinition: ControllerDefinition
|
|
86
|
-
): typeof StacksController {
|
|
87
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
88
|
-
const Controller = controllerDefinition.hasOwnProperty("targets")
|
|
89
|
-
? class Controller extends StacksController {
|
|
90
|
-
static targets = controllerDefinition.targets ?? [];
|
|
91
|
-
}
|
|
92
|
-
: class Controller extends StacksController {};
|
|
93
|
-
|
|
94
|
-
for (const prop in controllerDefinition) {
|
|
95
|
-
const ownPropDescriptor =
|
|
96
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
97
|
-
controllerDefinition.hasOwnProperty(prop) &&
|
|
98
|
-
Object.getOwnPropertyDescriptor(controllerDefinition, prop);
|
|
99
|
-
if (prop !== "targets" && ownPropDescriptor) {
|
|
100
|
-
Object.defineProperty(
|
|
101
|
-
Controller.prototype,
|
|
102
|
-
prop,
|
|
103
|
-
ownPropDescriptor
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return Controller;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function addController(name: string, controller: ControllerDefinition) {
|
|
112
|
-
application.register(name, createController(controller));
|
|
113
|
-
}
|
|
1
|
+
import * as Stimulus from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export class StacksApplication extends Stimulus.Application {
|
|
4
|
+
static _initializing = true;
|
|
5
|
+
|
|
6
|
+
load(...definitions: Stimulus.Definition[]): void;
|
|
7
|
+
load(definitions: Stimulus.Definition[]): void;
|
|
8
|
+
load(
|
|
9
|
+
head: Stimulus.Definition | Stimulus.Definition[],
|
|
10
|
+
...rest: Stimulus.Definition[]
|
|
11
|
+
) {
|
|
12
|
+
const definitions = Array.isArray(head) ? head : [head, ...rest];
|
|
13
|
+
|
|
14
|
+
for (const definition of definitions) {
|
|
15
|
+
const hasPrefix = /^s-/.test(definition.identifier);
|
|
16
|
+
if (StacksApplication._initializing && !hasPrefix) {
|
|
17
|
+
throw 'Stacks-created Stimulus controller names must start with "s-".';
|
|
18
|
+
}
|
|
19
|
+
if (!StacksApplication._initializing && hasPrefix) {
|
|
20
|
+
throw 'The "s-" prefix on Stimulus controller names is reserved for Stacks-created controllers.';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
super.load(definitions);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static start(
|
|
28
|
+
element?: Element,
|
|
29
|
+
schema?: Stimulus.Schema
|
|
30
|
+
): StacksApplication {
|
|
31
|
+
const application = new StacksApplication(element, schema);
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
33
|
+
application.start();
|
|
34
|
+
return application;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static finalize() {
|
|
38
|
+
StacksApplication._initializing = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const application: Stimulus.Application = StacksApplication.start();
|
|
43
|
+
|
|
44
|
+
export class StacksController extends Stimulus.Controller {
|
|
45
|
+
protected getElementData(element: Element, key: string) {
|
|
46
|
+
return element.getAttribute("data-" + this.identifier + "-" + key);
|
|
47
|
+
}
|
|
48
|
+
protected setElementData(element: Element, key: string, value: string) {
|
|
49
|
+
element.setAttribute("data-" + this.identifier + "-" + key, value);
|
|
50
|
+
}
|
|
51
|
+
protected removeElementData(element: Element, key: string) {
|
|
52
|
+
element.removeAttribute("data-" + this.identifier + "-" + key);
|
|
53
|
+
}
|
|
54
|
+
protected triggerEvent<T>(
|
|
55
|
+
eventName: string,
|
|
56
|
+
detail?: T,
|
|
57
|
+
optionalElement?: Element
|
|
58
|
+
) {
|
|
59
|
+
const namespacedName = this.identifier + ":" + eventName;
|
|
60
|
+
let event: CustomEvent<T>;
|
|
61
|
+
try {
|
|
62
|
+
event = new CustomEvent(namespacedName, {
|
|
63
|
+
bubbles: true,
|
|
64
|
+
cancelable: true,
|
|
65
|
+
detail: detail,
|
|
66
|
+
});
|
|
67
|
+
} catch (ex) {
|
|
68
|
+
// Internet Explorer
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
70
|
+
event = document.createEvent("CustomEvent");
|
|
71
|
+
event.initCustomEvent(namespacedName, true, true, detail);
|
|
72
|
+
}
|
|
73
|
+
(optionalElement || this.element).dispatchEvent(event);
|
|
74
|
+
return event;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ControllerDefinition/createController/addController is here to make
|
|
79
|
+
// it easier to consume Stimulus from ES5 files (without classes)
|
|
80
|
+
export interface ControllerDefinition {
|
|
81
|
+
[name: string]: unknown;
|
|
82
|
+
targets?: string[];
|
|
83
|
+
}
|
|
84
|
+
export function createController(
|
|
85
|
+
controllerDefinition: ControllerDefinition
|
|
86
|
+
): typeof StacksController {
|
|
87
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
88
|
+
const Controller = controllerDefinition.hasOwnProperty("targets")
|
|
89
|
+
? class Controller extends StacksController {
|
|
90
|
+
static targets = controllerDefinition.targets ?? [];
|
|
91
|
+
}
|
|
92
|
+
: class Controller extends StacksController {};
|
|
93
|
+
|
|
94
|
+
for (const prop in controllerDefinition) {
|
|
95
|
+
const ownPropDescriptor =
|
|
96
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
97
|
+
controllerDefinition.hasOwnProperty(prop) &&
|
|
98
|
+
Object.getOwnPropertyDescriptor(controllerDefinition, prop);
|
|
99
|
+
if (prop !== "targets" && ownPropDescriptor) {
|
|
100
|
+
Object.defineProperty(
|
|
101
|
+
Controller.prototype,
|
|
102
|
+
prop,
|
|
103
|
+
ownPropDescriptor
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return Controller;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function addController(name: string, controller: ControllerDefinition) {
|
|
112
|
+
application.register(name, createController(controller));
|
|
113
|
+
}
|
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
import { html, fixture, expect } from "@open-wc/testing";
|
|
2
|
-
import { screen } from "@testing-library/dom";
|
|
3
|
-
import axe from "axe-core";
|
|
4
|
-
import registerAPCACheck from "apca-check";
|
|
5
|
-
import { generateTestVariations, type TestVariationArgs } from "./test-utils";
|
|
6
|
-
import type { AdditionalAssertion } from "./assertions";
|
|
7
|
-
|
|
8
|
-
type A11yTestArgs = TestVariationArgs & {
|
|
9
|
-
/**
|
|
10
|
-
* Additional assertions to run against the test element
|
|
11
|
-
*/
|
|
12
|
-
additionalAssertions?: AdditionalAssertion[];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// register Stack APCA conformance threshold function
|
|
16
|
-
// see also https://stackoverflow.design/product/foundation/accessibility/#visual-accessibility
|
|
17
|
-
const customConformanceThresholdFn = (fontSize: string): number | null => {
|
|
18
|
-
// if the font size is 32px or larger, we use a 45Lc threshold
|
|
19
|
-
// otherwise, we use a 60Lc threshold
|
|
20
|
-
return parseFloat(fontSize) >= 32 ? 45 : 60;
|
|
21
|
-
};
|
|
22
|
-
registerAPCACheck("custom", customConformanceThresholdFn);
|
|
23
|
-
|
|
24
|
-
const scheduleA11yTest = ({
|
|
25
|
-
element,
|
|
26
|
-
testid,
|
|
27
|
-
theme,
|
|
28
|
-
additionalAssertions = [],
|
|
29
|
-
}: {
|
|
30
|
-
element: ReturnType<typeof html>;
|
|
31
|
-
testid: string;
|
|
32
|
-
theme: string[];
|
|
33
|
-
additionalAssertions?: AdditionalAssertion[];
|
|
34
|
-
}) => {
|
|
35
|
-
it(`a11y: ${testid} should be accessible`, async () => {
|
|
36
|
-
await fixture(element);
|
|
37
|
-
const el = screen.getByTestId(testid);
|
|
38
|
-
|
|
39
|
-
document.body.className = "";
|
|
40
|
-
|
|
41
|
-
if (theme?.length) {
|
|
42
|
-
const prefixedThemes = theme.map((t) => `theme-${t}`);
|
|
43
|
-
document.body.classList.add(...prefixedThemes);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const highcontrast = theme?.includes("highcontrast");
|
|
47
|
-
|
|
48
|
-
axe.configure({
|
|
49
|
-
rules: [
|
|
50
|
-
// for non-high contrast, we disable WCAG 2.1 AA (4.5:1)
|
|
51
|
-
// and use a Stacks-specific APCA custom level instead
|
|
52
|
-
{ id: "color-contrast", enabled: false },
|
|
53
|
-
{
|
|
54
|
-
id: "color-contrast-apca-custom",
|
|
55
|
-
enabled: !highcontrast,
|
|
56
|
-
},
|
|
57
|
-
// for high contrast, we check against WCAG 2.1 AAA (7:1)
|
|
58
|
-
{ id: "color-contrast-enhanced", enabled: highcontrast },
|
|
59
|
-
],
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
await expect(el).to.be.accessible();
|
|
63
|
-
el.remove();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
additionalAssertions.forEach((assertion) => {
|
|
67
|
-
it(`a11y: ${testid} ${assertion.description}`, async () => {
|
|
68
|
-
await fixture(element);
|
|
69
|
-
const el = screen.getByTestId(testid);
|
|
70
|
-
await assertion.assertion(el);
|
|
71
|
-
el.remove();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const runA11yTests = (args: A11yTestArgs) => {
|
|
77
|
-
const testVariations = generateTestVariations(args);
|
|
78
|
-
testVariations.forEach((variation) => {
|
|
79
|
-
if (variation.skipped) {
|
|
80
|
-
it.skip(`a11y: ${variation.testid} (skipped)`, () => {
|
|
81
|
-
return;
|
|
82
|
-
});
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
scheduleA11yTest({
|
|
87
|
-
...variation,
|
|
88
|
-
additionalAssertions: args.additionalAssertions,
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export type { AdditionalAssertion };
|
|
94
|
-
export { runA11yTests };
|
|
1
|
+
import { html, fixture, expect } from "@open-wc/testing";
|
|
2
|
+
import { screen } from "@testing-library/dom";
|
|
3
|
+
import axe from "axe-core";
|
|
4
|
+
import registerAPCACheck from "apca-check";
|
|
5
|
+
import { generateTestVariations, type TestVariationArgs } from "./test-utils";
|
|
6
|
+
import type { AdditionalAssertion } from "./assertions";
|
|
7
|
+
|
|
8
|
+
type A11yTestArgs = TestVariationArgs & {
|
|
9
|
+
/**
|
|
10
|
+
* Additional assertions to run against the test element
|
|
11
|
+
*/
|
|
12
|
+
additionalAssertions?: AdditionalAssertion[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// register Stack APCA conformance threshold function
|
|
16
|
+
// see also https://stackoverflow.design/product/foundation/accessibility/#visual-accessibility
|
|
17
|
+
const customConformanceThresholdFn = (fontSize: string): number | null => {
|
|
18
|
+
// if the font size is 32px or larger, we use a 45Lc threshold
|
|
19
|
+
// otherwise, we use a 60Lc threshold
|
|
20
|
+
return parseFloat(fontSize) >= 32 ? 45 : 60;
|
|
21
|
+
};
|
|
22
|
+
registerAPCACheck("custom", customConformanceThresholdFn);
|
|
23
|
+
|
|
24
|
+
const scheduleA11yTest = ({
|
|
25
|
+
element,
|
|
26
|
+
testid,
|
|
27
|
+
theme,
|
|
28
|
+
additionalAssertions = [],
|
|
29
|
+
}: {
|
|
30
|
+
element: ReturnType<typeof html>;
|
|
31
|
+
testid: string;
|
|
32
|
+
theme: string[];
|
|
33
|
+
additionalAssertions?: AdditionalAssertion[];
|
|
34
|
+
}) => {
|
|
35
|
+
it(`a11y: ${testid} should be accessible`, async () => {
|
|
36
|
+
await fixture(element);
|
|
37
|
+
const el = screen.getByTestId(testid);
|
|
38
|
+
|
|
39
|
+
document.body.className = "";
|
|
40
|
+
|
|
41
|
+
if (theme?.length) {
|
|
42
|
+
const prefixedThemes = theme.map((t) => `theme-${t}`);
|
|
43
|
+
document.body.classList.add(...prefixedThemes);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const highcontrast = theme?.includes("highcontrast");
|
|
47
|
+
|
|
48
|
+
axe.configure({
|
|
49
|
+
rules: [
|
|
50
|
+
// for non-high contrast, we disable WCAG 2.1 AA (4.5:1)
|
|
51
|
+
// and use a Stacks-specific APCA custom level instead
|
|
52
|
+
{ id: "color-contrast", enabled: false },
|
|
53
|
+
{
|
|
54
|
+
id: "color-contrast-apca-custom",
|
|
55
|
+
enabled: !highcontrast,
|
|
56
|
+
},
|
|
57
|
+
// for high contrast, we check against WCAG 2.1 AAA (7:1)
|
|
58
|
+
{ id: "color-contrast-enhanced", enabled: highcontrast },
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await expect(el).to.be.accessible();
|
|
63
|
+
el.remove();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
additionalAssertions.forEach((assertion) => {
|
|
67
|
+
it(`a11y: ${testid} ${assertion.description}`, async () => {
|
|
68
|
+
await fixture(element);
|
|
69
|
+
const el = screen.getByTestId(testid);
|
|
70
|
+
await assertion.assertion(el);
|
|
71
|
+
el.remove();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const runA11yTests = (args: A11yTestArgs) => {
|
|
77
|
+
const testVariations = generateTestVariations(args);
|
|
78
|
+
testVariations.forEach((variation) => {
|
|
79
|
+
if (variation.skipped) {
|
|
80
|
+
it.skip(`a11y: ${variation.testid} (skipped)`, () => {
|
|
81
|
+
return;
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
scheduleA11yTest({
|
|
87
|
+
...variation,
|
|
88
|
+
additionalAssertions: args.additionalAssertions,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type { AdditionalAssertion };
|
|
94
|
+
export { runA11yTests };
|
package/lib/test/assertions.ts
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { expect } from "@open-wc/testing";
|
|
2
|
-
import Color from "colorjs.io";
|
|
3
|
-
|
|
4
|
-
type AdditionalAssertion = {
|
|
5
|
-
description: string;
|
|
6
|
-
assertion: (node: HTMLElement) => Promise<void> | void;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
// TODO: evaluate if we can do this check against all the components
|
|
10
|
-
// automatically instead of having to add the assertion manually
|
|
11
|
-
const WCAGNonTextContrast: AdditionalAssertion = {
|
|
12
|
-
description:
|
|
13
|
-
"should pass WCAG22 1.4.11 non-text-contrast success criterion (https://www.w3.org/TR/WCAG22/#non-text-contrast)",
|
|
14
|
-
assertion: (node) => {
|
|
15
|
-
const selectedNode = node.querySelector(".is-selected") as HTMLElement;
|
|
16
|
-
const selectedNodeStyles = window.getComputedStyle(selectedNode);
|
|
17
|
-
const bodyStyles = window.getComputedStyle(document.body);
|
|
18
|
-
const bgBodyColor = new Color(
|
|
19
|
-
bodyStyles.getPropertyValue("background-color")
|
|
20
|
-
);
|
|
21
|
-
const bgSelectedNodeColor = new Color(
|
|
22
|
-
selectedNodeStyles.getPropertyValue("background-color")
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
// we are specifing WCAG21 because of colorjs.io API
|
|
26
|
-
// WCAG21 and WCAG22 algoirthms are the same
|
|
27
|
-
const WCAGcontrast = bgSelectedNodeColor.contrast(
|
|
28
|
-
bgBodyColor,
|
|
29
|
-
"WCAG21"
|
|
30
|
-
);
|
|
31
|
-
expect(WCAGcontrast).to.be.at.least(3);
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type { AdditionalAssertion };
|
|
36
|
-
export { WCAGNonTextContrast };
|
|
1
|
+
import { expect } from "@open-wc/testing";
|
|
2
|
+
import Color from "colorjs.io";
|
|
3
|
+
|
|
4
|
+
type AdditionalAssertion = {
|
|
5
|
+
description: string;
|
|
6
|
+
assertion: (node: HTMLElement) => Promise<void> | void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// TODO: evaluate if we can do this check against all the components
|
|
10
|
+
// automatically instead of having to add the assertion manually
|
|
11
|
+
const WCAGNonTextContrast: AdditionalAssertion = {
|
|
12
|
+
description:
|
|
13
|
+
"should pass WCAG22 1.4.11 non-text-contrast success criterion (https://www.w3.org/TR/WCAG22/#non-text-contrast)",
|
|
14
|
+
assertion: (node) => {
|
|
15
|
+
const selectedNode = node.querySelector(".is-selected") as HTMLElement;
|
|
16
|
+
const selectedNodeStyles = window.getComputedStyle(selectedNode);
|
|
17
|
+
const bodyStyles = window.getComputedStyle(document.body);
|
|
18
|
+
const bgBodyColor = new Color(
|
|
19
|
+
bodyStyles.getPropertyValue("background-color")
|
|
20
|
+
);
|
|
21
|
+
const bgSelectedNodeColor = new Color(
|
|
22
|
+
selectedNodeStyles.getPropertyValue("background-color")
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// we are specifing WCAG21 because of colorjs.io API
|
|
26
|
+
// WCAG21 and WCAG22 algoirthms are the same
|
|
27
|
+
const WCAGcontrast = bgSelectedNodeColor.contrast(
|
|
28
|
+
bgBodyColor,
|
|
29
|
+
"WCAG21"
|
|
30
|
+
);
|
|
31
|
+
expect(WCAGcontrast).to.be.at.least(3);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type { AdditionalAssertion };
|
|
36
|
+
export { WCAGNonTextContrast };
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import beautify from "cssbeautify";
|
|
2
|
-
import * as less from "less";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import * as fs from "fs";
|
|
5
|
-
|
|
6
|
-
// cssbeautify is an old package that doesn't support ES modules
|
|
7
|
-
const cssbeautify = beautify as (css: string) => string;
|
|
8
|
-
|
|
9
|
-
const getSubdirectories = (dirPath: string): string[] => {
|
|
10
|
-
const subdirs = fs
|
|
11
|
-
.readdirSync(dirPath)
|
|
12
|
-
.map((file) => path.join(dirPath, file))
|
|
13
|
-
.filter((path) => fs.statSync(path).isDirectory());
|
|
14
|
-
return subdirs.reduce(
|
|
15
|
-
(acc, subdir) => [...acc, subdir, ...getSubdirectories(subdir)],
|
|
16
|
-
subdirs
|
|
17
|
-
);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const renderLess = async (lessCode: string) => {
|
|
21
|
-
const { css } = await less.render(lessCode, {
|
|
22
|
-
paths: getSubdirectories(path.join(__dirname, "../")),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return cssbeautify(css);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export { renderLess };
|
|
1
|
+
import beautify from "cssbeautify";
|
|
2
|
+
import * as less from "less";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
|
|
6
|
+
// cssbeautify is an old package that doesn't support ES modules
|
|
7
|
+
const cssbeautify = beautify as (css: string) => string;
|
|
8
|
+
|
|
9
|
+
const getSubdirectories = (dirPath: string): string[] => {
|
|
10
|
+
const subdirs = fs
|
|
11
|
+
.readdirSync(dirPath)
|
|
12
|
+
.map((file) => path.join(dirPath, file))
|
|
13
|
+
.filter((path) => fs.statSync(path).isDirectory());
|
|
14
|
+
return subdirs.reduce(
|
|
15
|
+
(acc, subdir) => [...acc, subdir, ...getSubdirectories(subdir)],
|
|
16
|
+
subdirs
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const renderLess = async (lessCode: string) => {
|
|
21
|
+
const { css } = await less.render(lessCode, {
|
|
22
|
+
paths: getSubdirectories(path.join(__dirname, "../")),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return cssbeautify(css);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { renderLess };
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// patch for @open-wc/testing types
|
|
2
|
-
// TODO: remove when this PR gets merged:
|
|
3
|
-
// https://github.com/open-wc/open-wc/pull/2652
|
|
4
|
-
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
6
|
-
/// <reference path="../../node_modules/@open-wc/testing/register-chai-plugins.d.ts" />
|
|
7
|
-
|
|
8
|
-
export { html } from "@open-wc/testing-helpers";
|
|
9
|
-
export { unsafeStatic } from "@open-wc/testing-helpers";
|
|
10
|
-
export { triggerBlurFor } from "@open-wc/testing-helpers";
|
|
11
|
-
export { triggerFocusFor } from "@open-wc/testing-helpers";
|
|
12
|
-
export { oneEvent } from "@open-wc/testing-helpers";
|
|
13
|
-
export { isIE } from "@open-wc/testing-helpers";
|
|
14
|
-
export { defineCE } from "@open-wc/testing-helpers";
|
|
15
|
-
export { aTimeout } from "@open-wc/testing-helpers";
|
|
16
|
-
export { nextFrame } from "@open-wc/testing-helpers";
|
|
17
|
-
export { litFixture } from "@open-wc/testing-helpers";
|
|
18
|
-
export { litFixtureSync } from "@open-wc/testing-helpers";
|
|
19
|
-
export { fixture } from "@open-wc/testing-helpers";
|
|
20
|
-
export { fixtureSync } from "@open-wc/testing-helpers";
|
|
21
|
-
export { fixtureCleanup } from "@open-wc/testing-helpers";
|
|
22
|
-
export { elementUpdated } from "@open-wc/testing-helpers";
|
|
23
|
-
export { waitUntil } from "@open-wc/testing-helpers";
|
|
24
|
-
|
|
25
|
-
export * as chai from "chai";
|
|
26
|
-
export { expect, assert, should } from "chai";
|
|
1
|
+
// patch for @open-wc/testing types
|
|
2
|
+
// TODO: remove when this PR gets merged:
|
|
3
|
+
// https://github.com/open-wc/open-wc/pull/2652
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
6
|
+
/// <reference path="../../node_modules/@open-wc/testing/register-chai-plugins.d.ts" />
|
|
7
|
+
|
|
8
|
+
export { html } from "@open-wc/testing-helpers";
|
|
9
|
+
export { unsafeStatic } from "@open-wc/testing-helpers";
|
|
10
|
+
export { triggerBlurFor } from "@open-wc/testing-helpers";
|
|
11
|
+
export { triggerFocusFor } from "@open-wc/testing-helpers";
|
|
12
|
+
export { oneEvent } from "@open-wc/testing-helpers";
|
|
13
|
+
export { isIE } from "@open-wc/testing-helpers";
|
|
14
|
+
export { defineCE } from "@open-wc/testing-helpers";
|
|
15
|
+
export { aTimeout } from "@open-wc/testing-helpers";
|
|
16
|
+
export { nextFrame } from "@open-wc/testing-helpers";
|
|
17
|
+
export { litFixture } from "@open-wc/testing-helpers";
|
|
18
|
+
export { litFixtureSync } from "@open-wc/testing-helpers";
|
|
19
|
+
export { fixture } from "@open-wc/testing-helpers";
|
|
20
|
+
export { fixtureSync } from "@open-wc/testing-helpers";
|
|
21
|
+
export { fixtureCleanup } from "@open-wc/testing-helpers";
|
|
22
|
+
export { elementUpdated } from "@open-wc/testing-helpers";
|
|
23
|
+
export { waitUntil } from "@open-wc/testing-helpers";
|
|
24
|
+
|
|
25
|
+
export * as chai from "chai";
|
|
26
|
+
export { expect, assert, should } from "chai";
|