@internetarchive/modal-manager 2.0.4 → 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.
Files changed (101) hide show
  1. package/.editorconfig +29 -29
  2. package/.github/workflows/ci.yml +40 -30
  3. package/.github/workflows/gh-pages-main.yml +42 -42
  4. package/.github/workflows/pr-preview.yml +40 -40
  5. package/.prettierrc +4 -0
  6. package/.vscode/extensions.json +10 -0
  7. package/.vscode/tasks.json +12 -0
  8. package/LICENSE +661 -661
  9. package/README.md +139 -139
  10. package/custom-elements.json +170 -170
  11. package/demo/app-root.ts +366 -0
  12. package/dist/demo/app-root.d.ts +22 -0
  13. package/dist/demo/app-root.js +338 -0
  14. package/dist/demo/app-root.js.map +1 -0
  15. package/dist/index.d.ts +7 -7
  16. package/dist/index.js +5 -5
  17. package/dist/src/assets/arrow-left-icon.d.ts +2 -2
  18. package/dist/src/assets/arrow-left-icon.js +2 -2
  19. package/dist/src/assets/ia-logo-icon.d.ts +2 -2
  20. package/dist/src/assets/ia-logo-icon.js +2 -2
  21. package/dist/src/modal-config.d.ts +104 -104
  22. package/dist/src/modal-config.js +23 -24
  23. package/dist/src/modal-config.js.map +1 -1
  24. package/dist/src/modal-manager-host-bridge-interface.d.ts +12 -12
  25. package/dist/src/modal-manager-host-bridge-interface.js +1 -1
  26. package/dist/src/modal-manager-host-bridge.d.ts +34 -34
  27. package/dist/src/modal-manager-host-bridge.js +62 -62
  28. package/dist/src/modal-manager-host-bridge.js.map +1 -1
  29. package/dist/src/modal-manager-interface.d.ts +27 -27
  30. package/dist/src/modal-manager-interface.js +1 -1
  31. package/dist/src/modal-manager-mode.d.ts +10 -10
  32. package/dist/src/modal-manager-mode.js +11 -11
  33. package/dist/src/modal-manager.d.ts +137 -137
  34. package/dist/src/modal-manager.js +243 -249
  35. package/dist/src/modal-manager.js.map +1 -1
  36. package/dist/src/modal-template.d.ts +41 -41
  37. package/dist/src/modal-template.js +119 -118
  38. package/dist/src/modal-template.js.map +1 -1
  39. package/dist/src/shoelace/active-elements.d.ts +15 -15
  40. package/dist/src/shoelace/active-elements.js +28 -27
  41. package/dist/src/shoelace/active-elements.js.map +1 -1
  42. package/dist/src/shoelace/modal.d.ts +24 -24
  43. package/dist/src/shoelace/modal.js +130 -131
  44. package/dist/src/shoelace/modal.js.map +1 -1
  45. package/dist/src/shoelace/tabbable.d.ts +9 -9
  46. package/dist/src/shoelace/tabbable.js +168 -169
  47. package/dist/src/shoelace/tabbable.js.map +1 -1
  48. package/dist/test/modal-config.test.d.ts +1 -1
  49. package/dist/test/modal-config.test.js +68 -69
  50. package/dist/test/modal-config.test.js.map +1 -1
  51. package/dist/test/modal-manager.test.d.ts +1 -1
  52. package/dist/test/modal-manager.test.js +306 -309
  53. package/dist/test/modal-manager.test.js.map +1 -1
  54. package/dist/test/modal-template.test.d.ts +1 -1
  55. package/dist/test/modal-template.test.js +158 -167
  56. package/dist/test/modal-template.test.js.map +1 -1
  57. package/dist/vite.config.d.ts +2 -2
  58. package/dist/vite.config.js +22 -22
  59. package/dist/vitest.config.ci.d.ts +2 -0
  60. package/dist/vitest.config.ci.js +24 -0
  61. package/dist/vitest.config.ci.js.map +1 -0
  62. package/docs/assets/css/main.css +2678 -2678
  63. package/docs/classes/_src_modal_config_.modalconfig.html +429 -429
  64. package/docs/classes/_src_modal_manager_.modalmanager.html +7702 -7702
  65. package/docs/classes/_src_modal_manager_host_bridge_.modalmanagerhostbridge.html +409 -409
  66. package/docs/classes/_src_modal_template_.modaltemplate.html +7096 -7096
  67. package/docs/enums/_src_modal_manager_mode_.modalmanagermode.html +196 -196
  68. package/docs/globals.html +150 -150
  69. package/docs/index.html +252 -252
  70. package/docs/interfaces/_src_modal_manager_host_bridge_interface_.modalmanagerhostbridgeinterface.html +210 -210
  71. package/docs/interfaces/_src_modal_manager_interface_.modalmanagerinterface.html +7095 -7095
  72. package/docs/modules/_index_.html +208 -208
  73. package/docs/modules/_src_modal_config_.html +146 -146
  74. package/docs/modules/_src_modal_manager_.html +146 -146
  75. package/docs/modules/_src_modal_manager_host_bridge_.html +146 -146
  76. package/docs/modules/_src_modal_manager_host_bridge_interface_.html +146 -146
  77. package/docs/modules/_src_modal_manager_interface_.html +146 -146
  78. package/docs/modules/_src_modal_manager_mode_.html +146 -146
  79. package/docs/modules/_src_modal_template_.html +146 -146
  80. package/docs/modules/_test_modal_config_test_.html +106 -106
  81. package/docs/modules/_test_modal_manager_test_.html +106 -106
  82. package/docs/modules/_test_modal_template_test_.html +106 -106
  83. package/eslint.config.mjs +53 -0
  84. package/index.html +33 -300
  85. package/package.json +73 -85
  86. package/renovate.json +7 -7
  87. package/src/modal-config.ts +14 -14
  88. package/src/modal-manager-host-bridge.ts +2 -2
  89. package/src/modal-manager.ts +305 -305
  90. package/src/modal-template.ts +4 -2
  91. package/src/shoelace/LICENSE.md +6 -6
  92. package/src/shoelace/active-elements.ts +3 -2
  93. package/src/shoelace/modal.ts +5 -5
  94. package/src/shoelace/tabbable.ts +4 -3
  95. package/test/modal-config.test.ts +4 -4
  96. package/test/modal-manager.test.ts +402 -393
  97. package/test/modal-template.test.ts +42 -35
  98. package/tsconfig.json +25 -21
  99. package/vitest.config.ci.ts +27 -0
  100. package/.eslintrc.js +0 -14
  101. package/karma.conf.js +0 -24
@@ -1,310 +1,307 @@
1
- import { __awaiter } from "tslib";
2
- import { fixture, expect, oneEvent, elementUpdated, nextFrame, } from '@open-wc/testing';
3
- import { html } from 'lit';
4
- import '../src/modal-manager';
5
- import { ModalConfig } from '../src/modal-config';
6
- import { ModalManagerMode } from '../src/modal-manager-mode';
7
- import { getTabbableElements } from '../src/shoelace/tabbable';
8
- describe('Modal Manager', () => {
9
- it('defaults to closed', () => __awaiter(void 0, void 0, void 0, function* () {
10
- const el = (yield fixture(html `
11
- <modal-manager></modal-manager>
12
- `));
13
- expect(el.mode).to.equal('closed');
14
- }));
15
- it('can be closed by calling closeModal', () => __awaiter(void 0, void 0, void 0, function* () {
16
- const el = (yield fixture(html `
17
- <modal-manager .mode=${ModalManagerMode.Open}></modal-manager>
18
- `));
19
- el.customModalContent = 'foo';
20
- yield elementUpdated(el);
21
- expect(el.customModalContent).to.equal('foo');
22
- el.closeModal();
23
- yield elementUpdated(el);
24
- expect(el.mode).to.equal('closed');
25
- expect(el.customModalContent).to.equal(undefined);
26
- }));
27
- it('can be closed by clicking on the backdrop', () => __awaiter(void 0, void 0, void 0, function* () {
28
- var _a;
29
- const el = (yield fixture(html `
30
- <modal-manager .mode=${ModalManagerMode.Open}></modal-manager>
31
- `));
32
- const backdrop = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.backdrop');
33
- const clickEvent = new MouseEvent('click');
34
- backdrop === null || backdrop === void 0 ? void 0 : backdrop.dispatchEvent(clickEvent);
35
- yield elementUpdated(el);
36
- expect(el.mode).to.equal('closed');
37
- }));
38
- it('emits a modeChanged event when opening', () => __awaiter(void 0, void 0, void 0, function* () {
39
- const el = (yield fixture(html `
40
- <modal-manager></modal-manager>
41
- `));
42
- const config = new ModalConfig();
43
- setTimeout(() => {
44
- el.showModal({ config });
45
- });
46
- const response = yield oneEvent(el, 'modeChanged', false);
47
- expect(response.detail.mode).to.equal(ModalManagerMode.Open);
48
- }));
49
- it('emits a modeChanged event when closing', () => __awaiter(void 0, void 0, void 0, function* () {
50
- const el = (yield fixture(html `
51
- <modal-manager></modal-manager>
52
- `));
53
- const config = new ModalConfig();
54
- el.showModal({ config });
55
- yield elementUpdated(el);
56
- setTimeout(() => {
57
- el.closeModal();
58
- });
59
- const response = yield oneEvent(el, 'modeChanged', false);
60
- expect(response.detail.mode).to.equal(ModalManagerMode.Closed);
61
- }));
62
- it('can show a modal', () => __awaiter(void 0, void 0, void 0, function* () {
63
- const el = (yield fixture(html `
64
- <modal-manager></modal-manager>
65
- `));
66
- const config = new ModalConfig();
67
- el.showModal({ config });
68
- yield elementUpdated(el);
69
- expect(el.mode).to.equal(ModalManagerMode.Open);
70
- }));
71
- it('sets the --containerHeight CSS property when the window resizes', () => __awaiter(void 0, void 0, void 0, function* () {
72
- const el = (yield fixture(html `
73
- <modal-manager></modal-manager>
74
- `));
75
- const config = new ModalConfig();
76
- el.showModal({ config });
77
- yield elementUpdated(el);
78
- const event = new Event('resize');
79
- const propBefore = el.style.getPropertyValue('--containerHeight');
80
- expect(propBefore).to.equal('');
81
- window.dispatchEvent(event);
82
- yield elementUpdated(el);
83
- const propAfter = el.style.getPropertyValue('--containerHeight');
84
- expect(propAfter).to.not.equal('');
85
- }));
86
- it('calls the userClosedModalCallback when the user taps the backdrop', () => __awaiter(void 0, void 0, void 0, function* () {
87
- var _b;
88
- const el = (yield fixture(html `
89
- <modal-manager></modal-manager>
90
- `));
91
- const config = new ModalConfig();
92
- let callbackCalled = false;
93
- const callback = () => {
94
- callbackCalled = true;
95
- };
96
- el.showModal({
97
- config,
98
- userClosedModalCallback: callback,
99
- });
100
- yield elementUpdated(el);
101
- const backdrop = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.backdrop');
102
- const clickEvent = new MouseEvent('click');
103
- backdrop === null || backdrop === void 0 ? void 0 : backdrop.dispatchEvent(clickEvent);
104
- yield elementUpdated(el);
105
- expect(callbackCalled).to.equal(true);
106
- }));
107
- it('does not call the userClosedModalCallback when the modal just closes', () => __awaiter(void 0, void 0, void 0, function* () {
108
- const el = (yield fixture(html `
109
- <modal-manager></modal-manager>
110
- `));
111
- const config = new ModalConfig();
112
- let callbackCalled = false;
113
- const callback = () => {
114
- callbackCalled = true;
115
- };
116
- el.showModal({
117
- config,
118
- userClosedModalCallback: callback,
119
- });
120
- yield elementUpdated(el);
121
- el.closeModal();
122
- yield elementUpdated(el);
123
- expect(callbackCalled).to.equal(false);
124
- }));
125
- it('calls the userPressedLeftNavButtonCallback when the user clicks the left nav button', () => __awaiter(void 0, void 0, void 0, function* () {
126
- var _c;
127
- const el = (yield fixture(html `
128
- <modal-manager></modal-manager>
129
- `));
130
- const config = new ModalConfig();
131
- config.showLeftNavButton = true;
132
- let callbackCalled = false;
133
- const callback = () => {
134
- callbackCalled = true;
135
- };
136
- el.showModal({
137
- config,
138
- userPressedLeftNavButtonCallback: callback,
139
- });
140
- yield elementUpdated(el);
141
- const modalTemplate = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('modal-template');
142
- expect(modalTemplate).to.exist;
143
- modalTemplate === null || modalTemplate === void 0 ? void 0 : modalTemplate.dispatchEvent(new Event('leftNavButtonPressed'));
144
- yield elementUpdated(el);
145
- expect(callbackCalled).to.equal(true);
146
- }));
147
- it('mode is set to closed when close button is pressed', () => __awaiter(void 0, void 0, void 0, function* () {
148
- var _d, _e;
149
- const el = (yield fixture(html `
150
- <modal-manager></modal-manager>
151
- `));
152
- const config = new ModalConfig();
153
- el.showModal({ config });
154
- yield elementUpdated(el);
155
- expect(el.mode).to.equal('open');
156
- const modal = (_d = el.shadowRoot) === null || _d === void 0 ? void 0 : _d.querySelector('modal-template');
157
- const closeButton = (_e = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _e === void 0 ? void 0 : _e.querySelector('.close-button');
158
- const clickEvent = new MouseEvent('click');
159
- closeButton === null || closeButton === void 0 ? void 0 : closeButton.dispatchEvent(clickEvent);
160
- yield elementUpdated(el);
161
- expect(el.mode).to.equal('closed');
162
- }));
163
- it('mode is set to closed when close button gets spacebar pressed', () => __awaiter(void 0, void 0, void 0, function* () {
164
- var _f, _g;
165
- const el = (yield fixture(html `
166
- <modal-manager></modal-manager>
167
- `));
168
- const config = new ModalConfig();
169
- el.showModal({ config });
170
- yield elementUpdated(el);
171
- expect(el.mode).to.equal('open');
172
- const modal = (_f = el.shadowRoot) === null || _f === void 0 ? void 0 : _f.querySelector('modal-template');
173
- const closeButton = (_g = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _g === void 0 ? void 0 : _g.querySelector('.close-button');
174
- // Close with keyboard
175
- const spacebarEvent = new KeyboardEvent('keydown', { key: ' ' });
176
- closeButton === null || closeButton === void 0 ? void 0 : closeButton.dispatchEvent(spacebarEvent);
177
- yield elementUpdated(el);
178
- expect(el.mode).to.equal('closed');
179
- }));
180
- it('mode remains open when close button gets non-button keypress', () => __awaiter(void 0, void 0, void 0, function* () {
181
- var _h, _j;
182
- const el = (yield fixture(html `
183
- <modal-manager></modal-manager>
184
- `));
185
- const config = new ModalConfig();
186
- el.showModal({ config });
187
- yield elementUpdated(el);
188
- expect(el.mode).to.equal('open');
189
- const modal = (_h = el.shadowRoot) === null || _h === void 0 ? void 0 : _h.querySelector('modal-template');
190
- const closeButton = (_j = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _j === void 0 ? void 0 : _j.querySelector('.close-button');
191
- // Close with keyboard
192
- const keyboardEvent = new KeyboardEvent('keydown', { key: '.' });
193
- closeButton === null || closeButton === void 0 ? void 0 : closeButton.dispatchEvent(keyboardEvent);
194
- yield elementUpdated(el);
195
- expect(el.mode).to.equal('open');
196
- }));
197
- it('allows the user to close by clicking on the backdrop if configured to', () => __awaiter(void 0, void 0, void 0, function* () {
198
- var _k;
199
- const el = (yield fixture(html `
200
- <modal-manager></modal-manager>
201
- `));
202
- const config = new ModalConfig();
203
- config.closeOnBackdropClick = true;
204
- el.showModal({ config });
205
- yield elementUpdated(el);
206
- const backdrop = (_k = el.shadowRoot) === null || _k === void 0 ? void 0 : _k.querySelector('.backdrop');
207
- const clickEvent = new MouseEvent('click');
208
- backdrop === null || backdrop === void 0 ? void 0 : backdrop.dispatchEvent(clickEvent);
209
- yield elementUpdated(el);
210
- expect(el.mode).to.equal('closed');
211
- }));
212
- it("doesn't allow the user to close by clicking on the backdrop if configured to", () => __awaiter(void 0, void 0, void 0, function* () {
213
- var _l;
214
- const el = (yield fixture(html `
215
- <modal-manager></modal-manager>
216
- `));
217
- const config = new ModalConfig();
218
- config.closeOnBackdropClick = false;
219
- el.showModal({ config });
220
- yield elementUpdated(el);
221
- const backdrop = (_l = el.shadowRoot) === null || _l === void 0 ? void 0 : _l.querySelector('.backdrop');
222
- const clickEvent = new MouseEvent('click');
223
- backdrop === null || backdrop === void 0 ? void 0 : backdrop.dispatchEvent(clickEvent);
224
- yield elementUpdated(el);
225
- expect(el.getMode()).to.equal('open');
226
- }));
227
- it('ia logo should not visible on modal', () => __awaiter(void 0, void 0, void 0, function* () {
228
- var _m;
229
- const el = (yield fixture(html `
230
- <modal-manager></modal-manager>
231
- `));
232
- const config = new ModalConfig();
233
- config.showHeaderLogo = false;
234
- el.showModal({ config });
235
- yield elementUpdated(el);
236
- const logoIcon = (_m = el.shadowRoot) === null || _m === void 0 ? void 0 : _m.querySelector('.logo-icon');
237
- expect(logoIcon).to.not.exist;
238
- }));
239
- it('should trap Tab key', () => __awaiter(void 0, void 0, void 0, function* () {
240
- var _o, _p, _q, _r, _s;
241
- const el = (yield fixture(html `
242
- <modal-manager></modal-manager>
243
- `));
244
- const config = new ModalConfig();
245
- el.showModal({ config });
246
- yield elementUpdated(el);
247
- expect(el.mode).to.equal('open');
248
- // Tab once to focus
249
- const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' });
250
- document.dispatchEvent(tabEvent);
251
- yield elementUpdated(el);
252
- // Should be only one tabbable element
253
- const modal = (_o = el.shadowRoot) === null || _o === void 0 ? void 0 : _o.querySelector('modal-template');
254
- const tabbableElements = getTabbableElements(modal);
255
- expect(tabbableElements === null || tabbableElements === void 0 ? void 0 : tabbableElements.length).to.equal(1);
256
- const closeButton = (_p = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _p === void 0 ? void 0 : _p.querySelector('.close-button');
257
- expect((_q = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _q === void 0 ? void 0 : _q.activeElement).to.equal(closeButton);
258
- // Tab again
259
- el.dispatchEvent(tabEvent);
260
- yield elementUpdated(el);
261
- // Should be only one tabbable element
262
- expect((_r = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _r === void 0 ? void 0 : _r.activeElement).to.equal(closeButton);
263
- // Shift + Tab
264
- const shiftTabEvent = new KeyboardEvent('keydown', {
265
- key: 'Tab',
266
- shiftKey: true,
267
- });
268
- document.dispatchEvent(shiftTabEvent);
269
- yield elementUpdated(el);
270
- // Should be only one tabbable element
271
- expect((_s = modal === null || modal === void 0 ? void 0 : modal.shadowRoot) === null || _s === void 0 ? void 0 : _s.activeElement).to.equal(closeButton);
272
- }));
273
- it('returns keyboard focus to the triggering element on close', () => __awaiter(void 0, void 0, void 0, function* () {
274
- const config = new ModalConfig();
275
- const el = (yield fixture(html `
276
- <div>
277
- <button>Another button</button>
278
- <button
279
- id="open-modal-btn"
280
- @click=${() => {
281
- const modal = el.querySelector('modal-manager');
282
- modal.showModal({ config });
283
- }}
284
- >
285
- Open modal
286
- </button>
287
- <modal-manager></modal-manager>
288
- </div>
289
- `));
290
- const openBtn = el.querySelector('#open-modal-btn');
291
- const modal = el.querySelector('modal-manager');
292
- // Focus is initially on the Open button
293
- openBtn.focus();
294
- expect(document.activeElement).to.equal(openBtn);
295
- // Focus enters the modal when it is opened
296
- openBtn.click();
297
- yield nextFrame();
298
- expect(document.activeElement).to.equal(modal);
299
- // With the modal already open, simulate showing different content.
300
- // This step is to ensure that even if showModal is called multiple times, we still
301
- // maintain the originally-focused element (subsequent calls do not overwrite it).
302
- modal.showModal({ config: new ModalConfig() });
303
- yield nextFrame();
304
- // Focus returns to the Open button when the modal closes
305
- modal.closeModal();
306
- yield modal.updateComplete;
307
- expect(document.activeElement).to.equal(openBtn);
308
- }));
309
- });
1
+ import { fixture, oneEvent, elementUpdated, nextFrame, fixtureCleanup, } from '@open-wc/testing-helpers';
2
+ import { describe, test, expect, afterEach } from 'vitest';
3
+ import { html } from 'lit';
4
+ import '../src/modal-manager';
5
+ import { ModalConfig } from '../src/modal-config';
6
+ import { ModalManagerMode } from '../src/modal-manager-mode';
7
+ import { getTabbableElements } from '../src/shoelace/tabbable';
8
+ describe('Modal Manager', () => {
9
+ afterEach(() => {
10
+ fixtureCleanup();
11
+ });
12
+ test('defaults to closed', async () => {
13
+ const el = (await fixture(html `
14
+ <modal-manager></modal-manager>
15
+ `));
16
+ expect(el.mode).to.equal('closed');
17
+ });
18
+ test('can be closed by calling closeModal', async () => {
19
+ const el = (await fixture(html `
20
+ <modal-manager .mode=${ModalManagerMode.Open}></modal-manager>
21
+ `));
22
+ el.customModalContent = 'foo';
23
+ await elementUpdated(el);
24
+ expect(el.customModalContent).to.equal('foo');
25
+ el.closeModal();
26
+ await elementUpdated(el);
27
+ expect(el.mode).to.equal('closed');
28
+ expect(el.customModalContent).to.equal(undefined);
29
+ });
30
+ test('can be closed by clicking on the backdrop', async () => {
31
+ const el = (await fixture(html `
32
+ <modal-manager .mode=${ModalManagerMode.Open}></modal-manager>
33
+ `));
34
+ const backdrop = el.shadowRoot?.querySelector('.backdrop');
35
+ const clickEvent = new MouseEvent('click');
36
+ backdrop?.dispatchEvent(clickEvent);
37
+ await elementUpdated(el);
38
+ expect(el.mode).to.equal('closed');
39
+ });
40
+ test('emits a modeChanged event when opening', async () => {
41
+ const el = (await fixture(html `
42
+ <modal-manager></modal-manager>
43
+ `));
44
+ const config = new ModalConfig();
45
+ setTimeout(() => {
46
+ el.showModal({ config });
47
+ });
48
+ const response = await oneEvent(el, 'modeChanged');
49
+ expect(response.detail.mode).to.equal(ModalManagerMode.Open);
50
+ });
51
+ test('emits a modeChanged event when closing', async () => {
52
+ const el = (await fixture(html `
53
+ <modal-manager></modal-manager>
54
+ `));
55
+ const config = new ModalConfig();
56
+ await el.showModal({ config });
57
+ await elementUpdated(el);
58
+ await nextFrame();
59
+ setTimeout(() => {
60
+ el.closeModal();
61
+ });
62
+ const response = await oneEvent(el, 'modeChanged');
63
+ expect(response.detail.mode).to.equal(ModalManagerMode.Closed);
64
+ });
65
+ test('can show a modal', async () => {
66
+ const el = (await fixture(html `
67
+ <modal-manager></modal-manager>
68
+ `));
69
+ const config = new ModalConfig();
70
+ el.showModal({ config });
71
+ await elementUpdated(el);
72
+ expect(el.mode).to.equal(ModalManagerMode.Open);
73
+ });
74
+ test('sets the --containerHeight CSS property when the window resizes', async () => {
75
+ const el = (await fixture(html `
76
+ <modal-manager></modal-manager>
77
+ `));
78
+ const config = new ModalConfig();
79
+ el.showModal({ config });
80
+ await elementUpdated(el);
81
+ const event = new Event('resize');
82
+ const propBefore = el.style.getPropertyValue('--containerHeight');
83
+ expect(propBefore).to.equal('');
84
+ window.dispatchEvent(event);
85
+ await elementUpdated(el);
86
+ const propAfter = el.style.getPropertyValue('--containerHeight');
87
+ expect(propAfter).to.not.equal('');
88
+ });
89
+ test('calls the userClosedModalCallback when the user taps the backdrop', async () => {
90
+ const el = (await fixture(html `
91
+ <modal-manager></modal-manager>
92
+ `));
93
+ const config = new ModalConfig();
94
+ let callbackCalled = false;
95
+ const callback = () => {
96
+ callbackCalled = true;
97
+ };
98
+ el.showModal({
99
+ config,
100
+ userClosedModalCallback: callback,
101
+ });
102
+ await elementUpdated(el);
103
+ const backdrop = el.shadowRoot?.querySelector('.backdrop');
104
+ const clickEvent = new MouseEvent('click');
105
+ backdrop?.dispatchEvent(clickEvent);
106
+ await elementUpdated(el);
107
+ expect(callbackCalled).to.equal(true);
108
+ });
109
+ test('does not call the userClosedModalCallback when the modal just closes', async () => {
110
+ const el = (await fixture(html `
111
+ <modal-manager></modal-manager>
112
+ `));
113
+ const config = new ModalConfig();
114
+ let callbackCalled = false;
115
+ const callback = () => {
116
+ callbackCalled = true;
117
+ };
118
+ el.showModal({
119
+ config,
120
+ userClosedModalCallback: callback,
121
+ });
122
+ await elementUpdated(el);
123
+ el.closeModal();
124
+ await elementUpdated(el);
125
+ expect(callbackCalled).to.equal(false);
126
+ });
127
+ test('calls the userPressedLeftNavButtonCallback when the user clicks the left nav button', async () => {
128
+ const el = (await fixture(html `
129
+ <modal-manager></modal-manager>
130
+ `));
131
+ const config = new ModalConfig();
132
+ config.showLeftNavButton = true;
133
+ let callbackCalled = false;
134
+ const callback = () => {
135
+ callbackCalled = true;
136
+ };
137
+ el.showModal({
138
+ config,
139
+ userPressedLeftNavButtonCallback: callback,
140
+ });
141
+ await elementUpdated(el);
142
+ const modalTemplate = el.shadowRoot?.querySelector('modal-template');
143
+ expect(modalTemplate).to.exist;
144
+ modalTemplate?.dispatchEvent(new Event('leftNavButtonPressed'));
145
+ await elementUpdated(el);
146
+ expect(callbackCalled).to.equal(true);
147
+ });
148
+ test('mode is set to closed when close button is pressed', async () => {
149
+ const el = (await fixture(html `
150
+ <modal-manager></modal-manager>
151
+ `));
152
+ const config = new ModalConfig();
153
+ el.showModal({ config });
154
+ await elementUpdated(el);
155
+ expect(el.mode).to.equal('open');
156
+ const modal = el.shadowRoot?.querySelector('modal-template');
157
+ const closeButton = modal?.shadowRoot?.querySelector('.close-button');
158
+ const clickEvent = new MouseEvent('click');
159
+ closeButton?.dispatchEvent(clickEvent);
160
+ await elementUpdated(el);
161
+ expect(el.mode).to.equal('closed');
162
+ });
163
+ test('mode is set to closed when close button gets spacebar pressed', async () => {
164
+ const el = (await fixture(html `
165
+ <modal-manager></modal-manager>
166
+ `));
167
+ const config = new ModalConfig();
168
+ el.showModal({ config });
169
+ await elementUpdated(el);
170
+ expect(el.mode).to.equal('open');
171
+ const modal = el.shadowRoot?.querySelector('modal-template');
172
+ const closeButton = modal?.shadowRoot?.querySelector('.close-button');
173
+ // Close with keyboard
174
+ const spacebarEvent = new KeyboardEvent('keydown', { key: ' ' });
175
+ closeButton?.dispatchEvent(spacebarEvent);
176
+ await elementUpdated(el);
177
+ expect(el.mode).to.equal('closed');
178
+ });
179
+ test('mode remains open when close button gets non-button keypress', async () => {
180
+ const el = (await fixture(html `
181
+ <modal-manager></modal-manager>
182
+ `));
183
+ const config = new ModalConfig();
184
+ el.showModal({ config });
185
+ await elementUpdated(el);
186
+ expect(el.mode).to.equal('open');
187
+ const modal = el.shadowRoot?.querySelector('modal-template');
188
+ const closeButton = modal?.shadowRoot?.querySelector('.close-button');
189
+ // Close with keyboard
190
+ const keyboardEvent = new KeyboardEvent('keydown', { key: '.' });
191
+ closeButton?.dispatchEvent(keyboardEvent);
192
+ await elementUpdated(el);
193
+ expect(el.mode).to.equal('open');
194
+ });
195
+ test('allows the user to close by clicking on the backdrop if configured to', async () => {
196
+ const el = (await fixture(html `
197
+ <modal-manager></modal-manager>
198
+ `));
199
+ const config = new ModalConfig();
200
+ config.closeOnBackdropClick = true;
201
+ el.showModal({ config });
202
+ await elementUpdated(el);
203
+ const backdrop = el.shadowRoot?.querySelector('.backdrop');
204
+ const clickEvent = new MouseEvent('click');
205
+ backdrop?.dispatchEvent(clickEvent);
206
+ await elementUpdated(el);
207
+ expect(el.mode).to.equal('closed');
208
+ });
209
+ test("doesn't allow the user to close by clicking on the backdrop if configured to", async () => {
210
+ const el = (await fixture(html `
211
+ <modal-manager></modal-manager>
212
+ `));
213
+ const config = new ModalConfig();
214
+ config.closeOnBackdropClick = false;
215
+ el.showModal({ config });
216
+ await elementUpdated(el);
217
+ const backdrop = el.shadowRoot?.querySelector('.backdrop');
218
+ const clickEvent = new MouseEvent('click');
219
+ backdrop?.dispatchEvent(clickEvent);
220
+ await elementUpdated(el);
221
+ expect(el.getMode()).to.equal('open');
222
+ });
223
+ test('ia logo should not visible on modal', async () => {
224
+ const el = (await fixture(html `
225
+ <modal-manager></modal-manager>
226
+ `));
227
+ const config = new ModalConfig();
228
+ config.showHeaderLogo = false;
229
+ el.showModal({ config });
230
+ await elementUpdated(el);
231
+ const logoIcon = el.shadowRoot?.querySelector('.logo-icon');
232
+ expect(logoIcon).to.not.exist;
233
+ });
234
+ test('should trap Tab key', async () => {
235
+ const el = (await fixture(html `
236
+ <modal-manager></modal-manager>
237
+ `));
238
+ const config = new ModalConfig();
239
+ el.showModal({ config });
240
+ await elementUpdated(el);
241
+ expect(el.mode).to.equal('open');
242
+ // Tab once to focus
243
+ const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' });
244
+ document.dispatchEvent(tabEvent);
245
+ await elementUpdated(el);
246
+ await nextFrame();
247
+ // Should be only one tabbable element
248
+ const modal = el.shadowRoot?.querySelector('modal-template');
249
+ const tabbableElements = getTabbableElements(modal);
250
+ expect(tabbableElements?.length).to.equal(1);
251
+ const closeButton = modal?.shadowRoot?.querySelector('.close-button');
252
+ expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
253
+ // Tab again
254
+ el.dispatchEvent(tabEvent);
255
+ await elementUpdated(el);
256
+ await nextFrame();
257
+ // Should be only one tabbable element
258
+ expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
259
+ // Shift + Tab
260
+ const shiftTabEvent = new KeyboardEvent('keydown', {
261
+ key: 'Tab',
262
+ shiftKey: true,
263
+ });
264
+ document.dispatchEvent(shiftTabEvent);
265
+ await elementUpdated(el);
266
+ await nextFrame();
267
+ // Should be only one tabbable element
268
+ expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
269
+ });
270
+ test('returns keyboard focus to the triggering element on close', async () => {
271
+ const config = new ModalConfig();
272
+ const el = (await fixture(html `
273
+ <div>
274
+ <button>Another button</button>
275
+ <button
276
+ id="open-modal-btn"
277
+ @click=${() => {
278
+ const modal = el.querySelector('modal-manager');
279
+ modal.showModal({ config });
280
+ }}
281
+ >
282
+ Open modal
283
+ </button>
284
+ <modal-manager></modal-manager>
285
+ </div>
286
+ `));
287
+ const openBtn = el.querySelector('#open-modal-btn');
288
+ const modal = el.querySelector('modal-manager');
289
+ // Focus is initially on the Open button
290
+ openBtn.focus();
291
+ expect(document.activeElement).to.equal(openBtn);
292
+ // Focus enters the modal when it is opened
293
+ openBtn.click();
294
+ await nextFrame();
295
+ expect(document.activeElement).to.equal(modal);
296
+ // With the modal already open, simulate showing different content.
297
+ // This step is to ensure that even if showModal is called multiple times, we still
298
+ // maintain the originally-focused element (subsequent calls do not overwrite it).
299
+ modal.showModal({ config: new ModalConfig() });
300
+ await nextFrame();
301
+ // Focus returns to the Open button when the modal closes
302
+ modal.closeModal();
303
+ await modal.updateComplete;
304
+ expect(document.activeElement).to.equal(openBtn);
305
+ });
306
+ });
310
307
  //# sourceMappingURL=modal-manager.test.js.map