@internetarchive/modal-manager 2.0.4-alpha-webdev7960.1 → 2.0.5-webdev-8155.1
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/.editorconfig +29 -29
- package/.github/workflows/ci.yml +40 -30
- package/.github/workflows/gh-pages-main.yml +42 -42
- package/.github/workflows/pr-preview.yml +40 -40
- package/.prettierrc +4 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/tasks.json +12 -0
- package/LICENSE +661 -661
- package/README.md +139 -139
- package/custom-elements.json +170 -170
- package/demo/app-root.ts +366 -0
- package/dist/demo/app-root.d.ts +22 -0
- package/dist/demo/app-root.js +338 -0
- package/dist/demo/app-root.js.map +1 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -5
- package/dist/src/assets/arrow-left-icon.d.ts +2 -2
- package/dist/src/assets/arrow-left-icon.js +2 -2
- package/dist/src/assets/ia-logo-icon.d.ts +2 -2
- package/dist/src/assets/ia-logo-icon.js +2 -2
- package/dist/src/modal-config.d.ts +104 -104
- package/dist/src/modal-config.js +23 -24
- package/dist/src/modal-config.js.map +1 -1
- package/dist/src/modal-manager-host-bridge-interface.d.ts +12 -12
- package/dist/src/modal-manager-host-bridge-interface.js +1 -1
- package/dist/src/modal-manager-host-bridge.d.ts +34 -34
- package/dist/src/modal-manager-host-bridge.js +62 -62
- package/dist/src/modal-manager-host-bridge.js.map +1 -1
- package/dist/src/modal-manager-interface.d.ts +27 -27
- package/dist/src/modal-manager-interface.js +1 -1
- package/dist/src/modal-manager-mode.d.ts +10 -10
- package/dist/src/modal-manager-mode.js +11 -11
- package/dist/src/modal-manager.d.ts +137 -137
- package/dist/src/modal-manager.js +206 -212
- package/dist/src/modal-manager.js.map +1 -1
- package/dist/src/modal-template.d.ts +41 -41
- package/dist/src/modal-template.js +119 -118
- package/dist/src/modal-template.js.map +1 -1
- package/dist/src/shoelace/active-elements.d.ts +15 -15
- package/dist/src/shoelace/active-elements.js +28 -27
- package/dist/src/shoelace/active-elements.js.map +1 -1
- package/dist/src/shoelace/modal.d.ts +24 -24
- package/dist/src/shoelace/modal.js +130 -131
- package/dist/src/shoelace/modal.js.map +1 -1
- package/dist/src/shoelace/tabbable.d.ts +9 -9
- package/dist/src/shoelace/tabbable.js +168 -169
- package/dist/src/shoelace/tabbable.js.map +1 -1
- package/dist/test/modal-config.test.d.ts +1 -1
- package/dist/test/modal-config.test.js +68 -69
- package/dist/test/modal-config.test.js.map +1 -1
- package/dist/test/modal-manager.test.d.ts +1 -1
- package/dist/test/modal-manager.test.js +279 -282
- package/dist/test/modal-manager.test.js.map +1 -1
- package/dist/test/modal-template.test.d.ts +1 -1
- package/dist/test/modal-template.test.js +158 -167
- package/dist/test/modal-template.test.js.map +1 -1
- package/dist/vite.config.d.ts +2 -2
- package/dist/vite.config.js +22 -22
- package/dist/vitest.config.ci.d.ts +2 -0
- package/dist/vitest.config.ci.js +24 -0
- package/dist/vitest.config.ci.js.map +1 -0
- package/docs/assets/css/main.css +2678 -2678
- package/docs/classes/_src_modal_config_.modalconfig.html +429 -429
- package/docs/classes/_src_modal_manager_.modalmanager.html +7702 -7702
- package/docs/classes/_src_modal_manager_host_bridge_.modalmanagerhostbridge.html +409 -409
- package/docs/classes/_src_modal_template_.modaltemplate.html +7096 -7096
- package/docs/enums/_src_modal_manager_mode_.modalmanagermode.html +196 -196
- package/docs/globals.html +150 -150
- package/docs/index.html +252 -252
- package/docs/interfaces/_src_modal_manager_host_bridge_interface_.modalmanagerhostbridgeinterface.html +210 -210
- package/docs/interfaces/_src_modal_manager_interface_.modalmanagerinterface.html +7095 -7095
- package/docs/modules/_index_.html +208 -208
- package/docs/modules/_src_modal_config_.html +146 -146
- package/docs/modules/_src_modal_manager_.html +146 -146
- package/docs/modules/_src_modal_manager_host_bridge_.html +146 -146
- package/docs/modules/_src_modal_manager_host_bridge_interface_.html +146 -146
- package/docs/modules/_src_modal_manager_interface_.html +146 -146
- package/docs/modules/_src_modal_manager_mode_.html +146 -146
- package/docs/modules/_src_modal_template_.html +146 -146
- package/docs/modules/_test_modal_config_test_.html +106 -106
- package/docs/modules/_test_modal_manager_test_.html +106 -106
- package/docs/modules/_test_modal_template_test_.html +106 -106
- package/eslint.config.mjs +53 -0
- package/index.html +33 -300
- package/package.json +73 -85
- package/renovate.json +7 -7
- package/src/modal-config.ts +14 -14
- package/src/modal-manager-host-bridge.ts +2 -2
- package/src/modal-manager.ts +3 -3
- package/src/modal-template.ts +4 -2
- package/src/shoelace/LICENSE.md +6 -6
- package/src/shoelace/active-elements.ts +3 -2
- package/src/shoelace/modal.ts +5 -5
- package/src/shoelace/tabbable.ts +4 -3
- package/test/modal-config.test.ts +4 -4
- package/test/modal-manager.test.ts +33 -24
- package/test/modal-template.test.ts +42 -35
- package/tsconfig.json +25 -21
- package/vitest.config.ci.ts +27 -0
- package/.eslintrc.js +0 -14
- package/karma.conf.js +0 -24
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* c8 ignore start */
|
|
2
2
|
/**
|
|
3
3
|
* Use a generator so we can iterate and possibly break early.
|
|
4
4
|
* @example
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* }
|
|
14
14
|
*/
|
|
15
15
|
export function* activeElements(
|
|
16
|
-
activeElement: Element | null = document.activeElement
|
|
16
|
+
activeElement: Element | null = document.activeElement,
|
|
17
17
|
): Generator<Element> {
|
|
18
18
|
if (activeElement === null || activeElement === undefined) return;
|
|
19
19
|
|
|
@@ -31,3 +31,4 @@ export function* activeElements(
|
|
|
31
31
|
export function getDeepestActiveElement() {
|
|
32
32
|
return [...activeElements()].pop();
|
|
33
33
|
}
|
|
34
|
+
/* c8 ignore end */
|
package/src/shoelace/modal.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* c8 ignore start */
|
|
2
2
|
import { activeElements, getDeepestActiveElement } from './active-elements.js';
|
|
3
3
|
import { getTabbableElements } from './tabbable.js';
|
|
4
4
|
|
|
@@ -28,7 +28,7 @@ export default class Modal {
|
|
|
28
28
|
|
|
29
29
|
/** Deactivates focus trapping. */
|
|
30
30
|
deactivate() {
|
|
31
|
-
activeModals = activeModals.filter(modal => modal !== this.element);
|
|
31
|
+
activeModals = activeModals.filter((modal) => modal !== this.element);
|
|
32
32
|
this.currentFocus = null;
|
|
33
33
|
document.removeEventListener('focusin', this.handleFocusIn);
|
|
34
34
|
document.removeEventListener('keydown', this.handleKeyDown);
|
|
@@ -75,7 +75,7 @@ export default class Modal {
|
|
|
75
75
|
private possiblyHasTabbableChildren(element: HTMLElement) {
|
|
76
76
|
return (
|
|
77
77
|
this.elementsWithTabbableControls.includes(
|
|
78
|
-
element.tagName.toLowerCase()
|
|
78
|
+
element.tagName.toLowerCase(),
|
|
79
79
|
) || element.hasAttribute('controls')
|
|
80
80
|
// Should we add a data-attribute for people to set just in case they have an element where we don't know if it has possibly tabbable elements?
|
|
81
81
|
);
|
|
@@ -107,14 +107,13 @@ export default class Modal {
|
|
|
107
107
|
const tabbableElements = getTabbableElements(this.element);
|
|
108
108
|
|
|
109
109
|
let currentFocusIndex = tabbableElements.findIndex(
|
|
110
|
-
el => el === currentActiveElement
|
|
110
|
+
(el) => el === currentActiveElement,
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
this.previousFocus = this.currentFocus;
|
|
114
114
|
|
|
115
115
|
const addition = this.tabDirection === 'forward' ? 1 : -1;
|
|
116
116
|
|
|
117
|
-
// eslint-disable-next-line
|
|
118
117
|
while (true) {
|
|
119
118
|
if (currentFocusIndex + addition >= tabbableElements.length) {
|
|
120
119
|
currentFocusIndex = 0;
|
|
@@ -164,3 +163,4 @@ export default class Modal {
|
|
|
164
163
|
this.tabDirection = 'forward';
|
|
165
164
|
};
|
|
166
165
|
}
|
|
166
|
+
/* c8 ignore end */
|
package/src/shoelace/tabbable.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* c8 ignore start */
|
|
2
2
|
// Cached compute style calls. This is specifically for browsers that dont support `checkVisibility()`.
|
|
3
3
|
// computedStyle calls are "live" so they only need to be retrieved once for an element.
|
|
4
4
|
const computedStyleMap = new WeakMap<Element, CSSStyleDeclaration>();
|
|
@@ -160,7 +160,7 @@ export function getTabbableBoundary(root: HTMLElement | ShadowRoot) {
|
|
|
160
160
|
*/
|
|
161
161
|
function getSlottedChildrenOutsideRootElement(
|
|
162
162
|
slotElement: HTMLSlotElement,
|
|
163
|
-
root: HTMLElement | ShadowRoot
|
|
163
|
+
root: HTMLElement | ShadowRoot,
|
|
164
164
|
) {
|
|
165
165
|
return (
|
|
166
166
|
(slotElement.getRootNode({ composed: true }) as ShadowRoot | null)?.host !==
|
|
@@ -195,7 +195,7 @@ export function getTabbableElements(root: HTMLElement | ShadowRoot) {
|
|
|
195
195
|
el.assignedElements({ flatten: true }).forEach(
|
|
196
196
|
(assignedEl: Element) => {
|
|
197
197
|
walk(assignedEl as HTMLElement | ShadowRoot);
|
|
198
|
-
}
|
|
198
|
+
},
|
|
199
199
|
);
|
|
200
200
|
}
|
|
201
201
|
|
|
@@ -221,3 +221,4 @@ export function getTabbableElements(root: HTMLElement | ShadowRoot) {
|
|
|
221
221
|
return bTabindex - aTabindex;
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
|
+
/* c8 ignore end */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { expect } from '
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
2
|
import { html } from 'lit';
|
|
3
3
|
|
|
4
4
|
import { ModalConfig } from '../src/modal-config';
|
|
5
5
|
|
|
6
6
|
describe('Modal Config', () => {
|
|
7
|
-
|
|
7
|
+
test('can be instantiated properly', async () => {
|
|
8
8
|
const config = new ModalConfig();
|
|
9
9
|
const title = html`Foo`;
|
|
10
10
|
config.title = title;
|
|
@@ -14,7 +14,7 @@ describe('Modal Config', () => {
|
|
|
14
14
|
expect(config.headerColor).to.equal('green');
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
test('can be instantiated properly with constructor', async () => {
|
|
18
18
|
const title = html`Foo`;
|
|
19
19
|
const subtitle = html`Bar`;
|
|
20
20
|
const headline = html`Baz`;
|
|
@@ -59,7 +59,7 @@ describe('Modal Config', () => {
|
|
|
59
59
|
expect(config.closeOnBackdropClick).to.equal(closeOnBackdropClick);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
test('instantiates properly with defaults', async () => {
|
|
63
63
|
const config = new ModalConfig();
|
|
64
64
|
expect(config.title).to.equal(undefined);
|
|
65
65
|
expect(config.subtitle).to.equal(undefined);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
fixture,
|
|
3
|
-
expect,
|
|
4
3
|
oneEvent,
|
|
5
4
|
elementUpdated,
|
|
6
5
|
nextFrame,
|
|
7
|
-
|
|
6
|
+
fixtureCleanup,
|
|
7
|
+
} from '@open-wc/testing-helpers';
|
|
8
|
+
import { describe, test, expect, afterEach } from 'vitest';
|
|
8
9
|
import { TemplateResult, html } from 'lit';
|
|
9
10
|
|
|
10
11
|
import '../src/modal-manager';
|
|
@@ -16,7 +17,11 @@ import { ModalManagerInterface } from '../src/modal-manager-interface';
|
|
|
16
17
|
import { getTabbableElements } from '../src/shoelace/tabbable';
|
|
17
18
|
|
|
18
19
|
describe('Modal Manager', () => {
|
|
19
|
-
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fixtureCleanup();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('defaults to closed', async () => {
|
|
20
25
|
const el = (await fixture(html`
|
|
21
26
|
<modal-manager></modal-manager>
|
|
22
27
|
`)) as ModalManager;
|
|
@@ -24,7 +29,7 @@ describe('Modal Manager', () => {
|
|
|
24
29
|
expect(el.mode).to.equal('closed');
|
|
25
30
|
});
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
test('can be closed by calling closeModal', async () => {
|
|
28
33
|
const el = (await fixture(html`
|
|
29
34
|
<modal-manager .mode=${ModalManagerMode.Open}></modal-manager>
|
|
30
35
|
`)) as ModalManager;
|
|
@@ -40,7 +45,7 @@ describe('Modal Manager', () => {
|
|
|
40
45
|
expect(el.customModalContent).to.equal(undefined);
|
|
41
46
|
});
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
test('can be closed by clicking on the backdrop', async () => {
|
|
44
49
|
const el = (await fixture(html`
|
|
45
50
|
<modal-manager .mode=${ModalManagerMode.Open}></modal-manager>
|
|
46
51
|
`)) as ModalManager;
|
|
@@ -54,7 +59,7 @@ describe('Modal Manager', () => {
|
|
|
54
59
|
expect(el.mode).to.equal('closed');
|
|
55
60
|
});
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
test('emits a modeChanged event when opening', async () => {
|
|
58
63
|
const el = (await fixture(html`
|
|
59
64
|
<modal-manager></modal-manager>
|
|
60
65
|
`)) as ModalManager;
|
|
@@ -64,27 +69,28 @@ describe('Modal Manager', () => {
|
|
|
64
69
|
setTimeout(() => {
|
|
65
70
|
el.showModal({ config });
|
|
66
71
|
});
|
|
67
|
-
const response = await oneEvent(el, 'modeChanged'
|
|
72
|
+
const response = await oneEvent(el, 'modeChanged');
|
|
68
73
|
expect(response.detail.mode).to.equal(ModalManagerMode.Open);
|
|
69
74
|
});
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
test('emits a modeChanged event when closing', async () => {
|
|
72
77
|
const el = (await fixture(html`
|
|
73
78
|
<modal-manager></modal-manager>
|
|
74
79
|
`)) as ModalManager;
|
|
75
80
|
|
|
76
81
|
const config = new ModalConfig();
|
|
77
|
-
el.showModal({ config });
|
|
82
|
+
await el.showModal({ config });
|
|
78
83
|
await elementUpdated(el);
|
|
84
|
+
await nextFrame();
|
|
79
85
|
|
|
80
86
|
setTimeout(() => {
|
|
81
87
|
el.closeModal();
|
|
82
88
|
});
|
|
83
|
-
const response = await oneEvent(el, 'modeChanged'
|
|
89
|
+
const response = await oneEvent(el, 'modeChanged');
|
|
84
90
|
expect(response.detail.mode).to.equal(ModalManagerMode.Closed);
|
|
85
91
|
});
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
test('can show a modal', async () => {
|
|
88
94
|
const el = (await fixture(html`
|
|
89
95
|
<modal-manager></modal-manager>
|
|
90
96
|
`)) as ModalManager;
|
|
@@ -95,7 +101,7 @@ describe('Modal Manager', () => {
|
|
|
95
101
|
expect(el.mode).to.equal(ModalManagerMode.Open);
|
|
96
102
|
});
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
test('sets the --containerHeight CSS property when the window resizes', async () => {
|
|
99
105
|
const el = (await fixture(html`
|
|
100
106
|
<modal-manager></modal-manager>
|
|
101
107
|
`)) as ModalManager;
|
|
@@ -112,7 +118,7 @@ describe('Modal Manager', () => {
|
|
|
112
118
|
expect(propAfter).to.not.equal('');
|
|
113
119
|
});
|
|
114
120
|
|
|
115
|
-
|
|
121
|
+
test('calls the userClosedModalCallback when the user taps the backdrop', async () => {
|
|
116
122
|
const el = (await fixture(html`
|
|
117
123
|
<modal-manager></modal-manager>
|
|
118
124
|
`)) as ModalManager;
|
|
@@ -137,7 +143,7 @@ describe('Modal Manager', () => {
|
|
|
137
143
|
expect(callbackCalled).to.equal(true);
|
|
138
144
|
});
|
|
139
145
|
|
|
140
|
-
|
|
146
|
+
test('does not call the userClosedModalCallback when the modal just closes', async () => {
|
|
141
147
|
const el = (await fixture(html`
|
|
142
148
|
<modal-manager></modal-manager>
|
|
143
149
|
`)) as ModalManager;
|
|
@@ -157,7 +163,7 @@ describe('Modal Manager', () => {
|
|
|
157
163
|
expect(callbackCalled).to.equal(false);
|
|
158
164
|
});
|
|
159
165
|
|
|
160
|
-
|
|
166
|
+
test('calls the userPressedLeftNavButtonCallback when the user clicks the left nav button', async () => {
|
|
161
167
|
const el = (await fixture(html`
|
|
162
168
|
<modal-manager></modal-manager>
|
|
163
169
|
`)) as ModalManager;
|
|
@@ -185,7 +191,7 @@ describe('Modal Manager', () => {
|
|
|
185
191
|
expect(callbackCalled).to.equal(true);
|
|
186
192
|
});
|
|
187
193
|
|
|
188
|
-
|
|
194
|
+
test('mode is set to closed when close button is pressed', async () => {
|
|
189
195
|
const el = (await fixture(html`
|
|
190
196
|
<modal-manager></modal-manager>
|
|
191
197
|
`)) as ModalManager;
|
|
@@ -206,7 +212,7 @@ describe('Modal Manager', () => {
|
|
|
206
212
|
expect(el.mode).to.equal('closed');
|
|
207
213
|
});
|
|
208
214
|
|
|
209
|
-
|
|
215
|
+
test('mode is set to closed when close button gets spacebar pressed', async () => {
|
|
210
216
|
const el = (await fixture(html`
|
|
211
217
|
<modal-manager></modal-manager>
|
|
212
218
|
`)) as ModalManager;
|
|
@@ -229,7 +235,7 @@ describe('Modal Manager', () => {
|
|
|
229
235
|
expect(el.mode).to.equal('closed');
|
|
230
236
|
});
|
|
231
237
|
|
|
232
|
-
|
|
238
|
+
test('mode remains open when close button gets non-button keypress', async () => {
|
|
233
239
|
const el = (await fixture(html`
|
|
234
240
|
<modal-manager></modal-manager>
|
|
235
241
|
`)) as ModalManager;
|
|
@@ -252,7 +258,7 @@ describe('Modal Manager', () => {
|
|
|
252
258
|
expect(el.mode).to.equal('open');
|
|
253
259
|
});
|
|
254
260
|
|
|
255
|
-
|
|
261
|
+
test('allows the user to close by clicking on the backdrop if configured to', async () => {
|
|
256
262
|
const el = (await fixture(html`
|
|
257
263
|
<modal-manager></modal-manager>
|
|
258
264
|
`)) as ModalManager;
|
|
@@ -271,7 +277,7 @@ describe('Modal Manager', () => {
|
|
|
271
277
|
expect(el.mode).to.equal('closed');
|
|
272
278
|
});
|
|
273
279
|
|
|
274
|
-
|
|
280
|
+
test("doesn't allow the user to close by clicking on the backdrop if configured to", async () => {
|
|
275
281
|
const el = (await fixture(html`
|
|
276
282
|
<modal-manager></modal-manager>
|
|
277
283
|
`)) as ModalManagerInterface;
|
|
@@ -290,7 +296,7 @@ describe('Modal Manager', () => {
|
|
|
290
296
|
expect(el.getMode()).to.equal('open');
|
|
291
297
|
});
|
|
292
298
|
|
|
293
|
-
|
|
299
|
+
test('ia logo should not visible on modal', async () => {
|
|
294
300
|
const el = (await fixture(html`
|
|
295
301
|
<modal-manager></modal-manager>
|
|
296
302
|
`)) as ModalManagerInterface;
|
|
@@ -304,7 +310,7 @@ describe('Modal Manager', () => {
|
|
|
304
310
|
expect(logoIcon).to.not.exist;
|
|
305
311
|
});
|
|
306
312
|
|
|
307
|
-
|
|
313
|
+
test('should trap Tab key', async () => {
|
|
308
314
|
const el = (await fixture(html`
|
|
309
315
|
<modal-manager></modal-manager>
|
|
310
316
|
`)) as ModalManager;
|
|
@@ -319,6 +325,7 @@ describe('Modal Manager', () => {
|
|
|
319
325
|
const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' });
|
|
320
326
|
document.dispatchEvent(tabEvent);
|
|
321
327
|
await elementUpdated(el);
|
|
328
|
+
await nextFrame();
|
|
322
329
|
|
|
323
330
|
// Should be only one tabbable element
|
|
324
331
|
const modal = el.shadowRoot?.querySelector('modal-template') as HTMLElement;
|
|
@@ -326,13 +333,14 @@ describe('Modal Manager', () => {
|
|
|
326
333
|
expect(tabbableElements?.length).to.equal(1);
|
|
327
334
|
|
|
328
335
|
const closeButton = modal?.shadowRoot?.querySelector(
|
|
329
|
-
'.close-button'
|
|
336
|
+
'.close-button',
|
|
330
337
|
) as HTMLElement;
|
|
331
338
|
expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
|
|
332
339
|
|
|
333
340
|
// Tab again
|
|
334
341
|
el.dispatchEvent(tabEvent);
|
|
335
342
|
await elementUpdated(el);
|
|
343
|
+
await nextFrame();
|
|
336
344
|
|
|
337
345
|
// Should be only one tabbable element
|
|
338
346
|
expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
|
|
@@ -344,12 +352,13 @@ describe('Modal Manager', () => {
|
|
|
344
352
|
});
|
|
345
353
|
document.dispatchEvent(shiftTabEvent);
|
|
346
354
|
await elementUpdated(el);
|
|
355
|
+
await nextFrame();
|
|
347
356
|
|
|
348
357
|
// Should be only one tabbable element
|
|
349
358
|
expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
|
|
350
359
|
});
|
|
351
360
|
|
|
352
|
-
|
|
361
|
+
test('returns keyboard focus to the triggering element on close', async () => {
|
|
353
362
|
const config = new ModalConfig();
|
|
354
363
|
const el = (await fixture(html`
|
|
355
364
|
<div>
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { fixture,
|
|
1
|
+
import { fixture, oneEvent } from '@open-wc/testing-helpers';
|
|
2
|
+
import { describe, test, expect } from 'vitest';
|
|
2
3
|
import { html } from 'lit';
|
|
3
4
|
import '../src/modal-template';
|
|
4
5
|
import { ModalConfig } from '../src/modal-config';
|
|
6
|
+
import type { ModalTemplate } from '../src/modal-template';
|
|
5
7
|
|
|
6
8
|
describe('Modal Template', () => {
|
|
7
|
-
|
|
8
|
-
const el = await fixture(html`
|
|
9
|
+
test('has correct default configuration', async () => {
|
|
10
|
+
const el = await fixture<ModalTemplate>(html`
|
|
11
|
+
<modal-template></modal-template>
|
|
12
|
+
`);
|
|
9
13
|
|
|
10
14
|
const processingLogo = el.shadowRoot?.querySelector('.processing-logo');
|
|
11
15
|
const headline = el.shadowRoot?.querySelector('.headline');
|
|
@@ -15,15 +19,15 @@ describe('Modal Template', () => {
|
|
|
15
19
|
expect(headline).to.not.exist;
|
|
16
20
|
expect(message).to.not.exist;
|
|
17
21
|
expect(title).to.not.exist;
|
|
18
|
-
|
|
22
|
+
|
|
19
23
|
expect('hidden' in processingLogo!.classList);
|
|
20
24
|
});
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
test('does not show the title if one not provided', async () => {
|
|
23
27
|
const config = new ModalConfig();
|
|
24
28
|
config.title = undefined;
|
|
25
29
|
|
|
26
|
-
const el = await fixture(html`
|
|
30
|
+
const el = await fixture<ModalTemplate>(html`
|
|
27
31
|
<modal-template .config=${config}></modal-template>
|
|
28
32
|
`);
|
|
29
33
|
|
|
@@ -31,21 +35,24 @@ describe('Modal Template', () => {
|
|
|
31
35
|
expect(title).to.not.exist;
|
|
32
36
|
});
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
const el = await fixture(html`
|
|
36
|
-
|
|
38
|
+
test('emits closeButtonPressed event when close button is pressed', async () => {
|
|
39
|
+
const el = await fixture<ModalTemplate>(html`
|
|
40
|
+
<modal-template></modal-template>
|
|
41
|
+
`);
|
|
37
42
|
const closeButton = el.shadowRoot?.querySelector('.close-button');
|
|
38
43
|
const clickEvent = new MouseEvent('click');
|
|
39
44
|
|
|
40
45
|
setTimeout(() => {
|
|
41
46
|
closeButton?.dispatchEvent(clickEvent);
|
|
42
47
|
});
|
|
43
|
-
const response = await oneEvent(el, 'closeButtonPressed'
|
|
48
|
+
const response = await oneEvent(el, 'closeButtonPressed');
|
|
44
49
|
expect(response).to.exist;
|
|
45
50
|
});
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
const el = await fixture(html`
|
|
52
|
+
test('emits closeButtonPressed event when close button gets spacebar pressed', async () => {
|
|
53
|
+
const el = await fixture<ModalTemplate>(html`
|
|
54
|
+
<modal-template></modal-template>
|
|
55
|
+
`);
|
|
49
56
|
|
|
50
57
|
const closeButton = el.shadowRoot?.querySelector('.close-button');
|
|
51
58
|
const clickEvent = new KeyboardEvent('keydown', { key: ' ' });
|
|
@@ -53,14 +60,14 @@ describe('Modal Template', () => {
|
|
|
53
60
|
setTimeout(() => {
|
|
54
61
|
closeButton?.dispatchEvent(clickEvent);
|
|
55
62
|
});
|
|
56
|
-
const response = await oneEvent(el, 'closeButtonPressed'
|
|
63
|
+
const response = await oneEvent(el, 'closeButtonPressed');
|
|
57
64
|
expect(response).to.exist;
|
|
58
65
|
});
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
test('emits leftNavButtonPressed event when left nav button is pressed', async () => {
|
|
61
68
|
const config = new ModalConfig();
|
|
62
69
|
config.showLeftNavButton = true;
|
|
63
|
-
const el = await fixture(html`
|
|
70
|
+
const el = await fixture<ModalTemplate>(html`
|
|
64
71
|
<modal-template .config=${config}></modal-template>
|
|
65
72
|
`);
|
|
66
73
|
|
|
@@ -70,14 +77,14 @@ describe('Modal Template', () => {
|
|
|
70
77
|
setTimeout(() => {
|
|
71
78
|
leftNavButton?.dispatchEvent(clickEvent);
|
|
72
79
|
});
|
|
73
|
-
const response = await oneEvent(el, 'leftNavButtonPressed'
|
|
80
|
+
const response = await oneEvent(el, 'leftNavButtonPressed');
|
|
74
81
|
expect(response).to.exist;
|
|
75
82
|
});
|
|
76
83
|
|
|
77
|
-
|
|
84
|
+
test('emits leftNavButtonPressed event when left nav button gets spacebar pressed', async () => {
|
|
78
85
|
const config = new ModalConfig();
|
|
79
86
|
config.showLeftNavButton = true;
|
|
80
|
-
const el = await fixture(html`
|
|
87
|
+
const el = await fixture<ModalTemplate>(html`
|
|
81
88
|
<modal-template .config=${config}></modal-template>
|
|
82
89
|
`);
|
|
83
90
|
|
|
@@ -87,15 +94,15 @@ describe('Modal Template', () => {
|
|
|
87
94
|
setTimeout(() => {
|
|
88
95
|
leftNavButton?.dispatchEvent(clickEvent);
|
|
89
96
|
});
|
|
90
|
-
const response = await oneEvent(el, 'leftNavButtonPressed'
|
|
97
|
+
const response = await oneEvent(el, 'leftNavButtonPressed');
|
|
91
98
|
expect(response).to.exist;
|
|
92
99
|
});
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
test('shows the processing indicator if configured to', async () => {
|
|
95
102
|
const config = new ModalConfig();
|
|
96
103
|
config.showProcessingIndicator = true;
|
|
97
104
|
|
|
98
|
-
const el = await fixture(html`
|
|
105
|
+
const el = await fixture<ModalTemplate>(html`
|
|
99
106
|
<modal-template .config=${config}></modal-template>
|
|
100
107
|
`);
|
|
101
108
|
|
|
@@ -104,10 +111,10 @@ describe('Modal Template', () => {
|
|
|
104
111
|
expect('hidden' in classList).to.equal(false);
|
|
105
112
|
});
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
test('shows the left nav button if configured to', async () => {
|
|
108
115
|
const config = new ModalConfig();
|
|
109
116
|
config.showLeftNavButton = true;
|
|
110
|
-
const el = await fixture(html`
|
|
117
|
+
const el = await fixture<ModalTemplate>(html`
|
|
111
118
|
<modal-template .config=${config}></modal-template>
|
|
112
119
|
`);
|
|
113
120
|
|
|
@@ -115,10 +122,10 @@ describe('Modal Template', () => {
|
|
|
115
122
|
expect(leftNavButton).to.exist;
|
|
116
123
|
});
|
|
117
124
|
|
|
118
|
-
|
|
125
|
+
test('hides the left nav button if configured to', async () => {
|
|
119
126
|
const config = new ModalConfig();
|
|
120
127
|
config.showCloseButton = false;
|
|
121
|
-
const el = await fixture(html`
|
|
128
|
+
const el = await fixture<ModalTemplate>(html`
|
|
122
129
|
<modal-template .config=${config}></modal-template>
|
|
123
130
|
`);
|
|
124
131
|
|
|
@@ -126,11 +133,11 @@ describe('Modal Template', () => {
|
|
|
126
133
|
expect(closeButton).to.not.exist;
|
|
127
134
|
});
|
|
128
135
|
|
|
129
|
-
|
|
136
|
+
test('uses custom text for the left nav button if configured to', async () => {
|
|
130
137
|
const config = new ModalConfig();
|
|
131
138
|
config.showLeftNavButton = true;
|
|
132
139
|
config.leftNavButtonText = 'Previous';
|
|
133
|
-
const el = await fixture(html`
|
|
140
|
+
const el = await fixture<ModalTemplate>(html`
|
|
134
141
|
<modal-template .config=${config}></modal-template>
|
|
135
142
|
`);
|
|
136
143
|
|
|
@@ -140,11 +147,11 @@ describe('Modal Template', () => {
|
|
|
140
147
|
expect(leftNavButton?.innerHTML).to.contain('Previous');
|
|
141
148
|
});
|
|
142
149
|
|
|
143
|
-
|
|
150
|
+
test('does not use any text for the left nav button if not configured to', async () => {
|
|
144
151
|
const config = new ModalConfig();
|
|
145
152
|
config.showLeftNavButton = true;
|
|
146
153
|
|
|
147
|
-
const el = await fixture(html`
|
|
154
|
+
const el = await fixture<ModalTemplate>(html`
|
|
148
155
|
<modal-template .config=${config}></modal-template>
|
|
149
156
|
`);
|
|
150
157
|
|
|
@@ -152,10 +159,10 @@ describe('Modal Template', () => {
|
|
|
152
159
|
expect(leftNavButton?.innerHTML).not.to.contain('Previous');
|
|
153
160
|
});
|
|
154
161
|
|
|
155
|
-
|
|
162
|
+
test('shows the close button if configured to', async () => {
|
|
156
163
|
const config = new ModalConfig();
|
|
157
164
|
config.showCloseButton = true;
|
|
158
|
-
const el = await fixture(html`
|
|
165
|
+
const el = await fixture<ModalTemplate>(html`
|
|
159
166
|
<modal-template .config=${config}></modal-template>
|
|
160
167
|
`);
|
|
161
168
|
|
|
@@ -163,10 +170,10 @@ describe('Modal Template', () => {
|
|
|
163
170
|
expect(closeButton).to.exist;
|
|
164
171
|
});
|
|
165
172
|
|
|
166
|
-
|
|
173
|
+
test('hides the close button if configured to', async () => {
|
|
167
174
|
const config = new ModalConfig();
|
|
168
175
|
config.showCloseButton = false;
|
|
169
|
-
const el = await fixture(html`
|
|
176
|
+
const el = await fixture<ModalTemplate>(html`
|
|
170
177
|
<modal-template .config=${config}></modal-template>
|
|
171
178
|
`);
|
|
172
179
|
|
|
@@ -174,14 +181,14 @@ describe('Modal Template', () => {
|
|
|
174
181
|
expect(closeButton).to.not.exist;
|
|
175
182
|
});
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
test('shows the properties from the config', async () => {
|
|
178
185
|
const config = new ModalConfig();
|
|
179
186
|
config.title = html`Boop`;
|
|
180
187
|
config.subtitle = html`Bop`;
|
|
181
188
|
config.headline = html`Foo`;
|
|
182
189
|
config.message = html`Bar`;
|
|
183
190
|
|
|
184
|
-
const el = await fixture(html`
|
|
191
|
+
const el = await fixture<ModalTemplate>(html`
|
|
185
192
|
<modal-template .config=${config}></modal-template>
|
|
186
193
|
`);
|
|
187
194
|
|
package/tsconfig.json
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"module": "esnext",
|
|
5
|
-
"moduleResolution": "
|
|
6
|
-
"noEmitOnError": true,
|
|
7
|
-
"lib": [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"noEmitOnError": true,
|
|
7
|
+
"lib": [
|
|
8
|
+
"esnext",
|
|
9
|
+
"dom"
|
|
10
|
+
],
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": false,
|
|
13
|
+
"allowSyntheticDefaultImports": true,
|
|
14
|
+
"experimentalDecorators": true,
|
|
15
|
+
"importHelpers": true,
|
|
16
|
+
"outDir": "dist",
|
|
17
|
+
"sourceMap": true,
|
|
18
|
+
"inlineSources": true,
|
|
19
|
+
"declaration": true,
|
|
20
|
+
"rootDir": "./",
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"useDefineForClassFields": false,
|
|
23
|
+
},
|
|
24
|
+
"include": ["**/*.ts"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
2
|
+
import { defineConfig, mergeConfig } from 'vitest/config';
|
|
3
|
+
import { playwright } from '@vitest/browser-playwright'
|
|
4
|
+
import viteConfig from './vite.config';
|
|
5
|
+
|
|
6
|
+
// https://vitest.dev/config
|
|
7
|
+
export default mergeConfig(
|
|
8
|
+
viteConfig,
|
|
9
|
+
defineConfig({
|
|
10
|
+
server: {
|
|
11
|
+
host: '0.0.0.0',
|
|
12
|
+
port: 8080,
|
|
13
|
+
},
|
|
14
|
+
test: {
|
|
15
|
+
browser: {
|
|
16
|
+
headless: true,
|
|
17
|
+
enabled: true,
|
|
18
|
+
provider: playwright(),
|
|
19
|
+
instances: [{ browser: 'chromium' }],
|
|
20
|
+
},
|
|
21
|
+
watch: false,
|
|
22
|
+
coverage: {
|
|
23
|
+
include: ['src/**/*.ts'],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
);
|
package/.eslintrc.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
parser: '@typescript-eslint/parser',
|
|
4
|
-
plugins: ['@typescript-eslint', 'html'],
|
|
5
|
-
extends: ['plugin:@typescript-eslint/recommended'],
|
|
6
|
-
settings: {
|
|
7
|
-
'import/resolver': {
|
|
8
|
-
node: {
|
|
9
|
-
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
|
10
|
-
moduleDirectory: ['node_modules', 'src', 'demo'],
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
};
|