@internetarchive/modal-manager 2.0.3 → 2.0.4-alpha-webdev7960.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 (73) hide show
  1. package/.editorconfig +29 -29
  2. package/.eslintrc.js +14 -14
  3. package/.github/workflows/ci.yml +30 -30
  4. package/.github/workflows/gh-pages-main.yml +42 -42
  5. package/.github/workflows/pr-preview.yml +40 -40
  6. package/LICENSE +661 -661
  7. package/README.md +139 -139
  8. package/custom-elements.json +170 -170
  9. package/dist/index.d.ts +7 -7
  10. package/dist/index.js +5 -5
  11. package/dist/src/assets/arrow-left-icon.d.ts +2 -2
  12. package/dist/src/assets/arrow-left-icon.js +2 -2
  13. package/dist/src/assets/ia-logo-icon.d.ts +2 -2
  14. package/dist/src/assets/ia-logo-icon.js +2 -2
  15. package/dist/src/modal-config.d.ts +104 -104
  16. package/dist/src/modal-config.js +24 -24
  17. package/dist/src/modal-manager-host-bridge-interface.d.ts +12 -12
  18. package/dist/src/modal-manager-host-bridge-interface.js +1 -1
  19. package/dist/src/modal-manager-host-bridge.d.ts +34 -34
  20. package/dist/src/modal-manager-host-bridge.js +62 -62
  21. package/dist/src/modal-manager-interface.d.ts +27 -27
  22. package/dist/src/modal-manager-interface.js +1 -1
  23. package/dist/src/modal-manager-mode.d.ts +10 -10
  24. package/dist/src/modal-manager-mode.js +11 -11
  25. package/dist/src/modal-manager.d.ts +137 -127
  26. package/dist/src/modal-manager.js +212 -197
  27. package/dist/src/modal-manager.js.map +1 -1
  28. package/dist/src/modal-template.d.ts +41 -41
  29. package/dist/src/modal-template.js +115 -115
  30. package/dist/src/shoelace/active-elements.d.ts +15 -15
  31. package/dist/src/shoelace/active-elements.js +27 -27
  32. package/dist/src/shoelace/modal.d.ts +24 -24
  33. package/dist/src/shoelace/modal.js +131 -131
  34. package/dist/src/shoelace/tabbable.d.ts +9 -9
  35. package/dist/src/shoelace/tabbable.js +169 -169
  36. package/dist/test/modal-config.test.d.ts +1 -1
  37. package/dist/test/modal-config.test.js +69 -69
  38. package/dist/test/modal-manager.test.d.ts +1 -1
  39. package/dist/test/modal-manager.test.js +275 -240
  40. package/dist/test/modal-manager.test.js.map +1 -1
  41. package/dist/test/modal-template.test.d.ts +1 -1
  42. package/dist/test/modal-template.test.js +156 -156
  43. package/dist/vite.config.d.ts +2 -2
  44. package/dist/vite.config.js +22 -22
  45. package/docs/assets/css/main.css +2678 -2678
  46. package/docs/classes/_src_modal_config_.modalconfig.html +429 -429
  47. package/docs/classes/_src_modal_manager_.modalmanager.html +7702 -7702
  48. package/docs/classes/_src_modal_manager_host_bridge_.modalmanagerhostbridge.html +409 -409
  49. package/docs/classes/_src_modal_template_.modaltemplate.html +7096 -7096
  50. package/docs/enums/_src_modal_manager_mode_.modalmanagermode.html +196 -196
  51. package/docs/globals.html +150 -150
  52. package/docs/index.html +252 -252
  53. package/docs/interfaces/_src_modal_manager_host_bridge_interface_.modalmanagerhostbridgeinterface.html +210 -210
  54. package/docs/interfaces/_src_modal_manager_interface_.modalmanagerinterface.html +7095 -7095
  55. package/docs/modules/_index_.html +208 -208
  56. package/docs/modules/_src_modal_config_.html +146 -146
  57. package/docs/modules/_src_modal_manager_.html +146 -146
  58. package/docs/modules/_src_modal_manager_host_bridge_.html +146 -146
  59. package/docs/modules/_src_modal_manager_host_bridge_interface_.html +146 -146
  60. package/docs/modules/_src_modal_manager_interface_.html +146 -146
  61. package/docs/modules/_src_modal_manager_mode_.html +146 -146
  62. package/docs/modules/_src_modal_template_.html +146 -146
  63. package/docs/modules/_test_modal_config_test_.html +106 -106
  64. package/docs/modules/_test_modal_manager_test_.html +106 -106
  65. package/docs/modules/_test_modal_template_test_.html +106 -106
  66. package/index.html +300 -300
  67. package/karma.conf.js +24 -24
  68. package/package.json +85 -85
  69. package/renovate.json +7 -7
  70. package/src/modal-manager.ts +22 -0
  71. package/src/shoelace/LICENSE.md +6 -6
  72. package/test/modal-manager.test.ts +52 -6
  73. package/tsconfig.json +21 -21
package/package.json CHANGED
@@ -1,85 +1,85 @@
1
- {
2
- "name": "@internetarchive/modal-manager",
3
- "version": "2.0.3",
4
- "description": "A Modal Manager Web Component",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/internetarchive/iaux-modal-manager.git"
8
- },
9
- "license": "AGPL-3.0-only",
10
- "main": "dist/index.js",
11
- "module": "dist/index.js",
12
- "types": "dist/index.d.ts",
13
- "scripts": {
14
- "prepare": "npm run build && husky install",
15
- "prepare:ghpages": "rimraf ghpages && npm run prepare && vite build && cp -R assets/images ghpages/assets/images",
16
- "start": "concurrently --kill-others --names tsc,es-dev-server \"npm run tsc:watch\" \"es-dev-server --app-index index.html --node-resolve --open --watch --preserve-symlinks\"",
17
- "docs": "typedoc",
18
- "build": "tsc",
19
- "tsc:watch": "tsc --watch",
20
- "lint:eslint": "eslint --ext .ts . --ignore-path .gitignore",
21
- "format:eslint": "eslint --ext .ts . --fix --ignore-path .gitignore",
22
- "lint:prettier": "prettier \"**/*.ts\" --check --ignore-path .gitignore",
23
- "format:prettier": "prettier \"**/*.ts\" --write --ignore-path .gitignore",
24
- "lint": "npm run lint:eslint && npm run lint:prettier",
25
- "format": "npm run format:eslint && npm run format:prettier",
26
- "circular": "madge --circular .",
27
- "test": "tsc && npm run lint && npm run circular && karma start --coverage",
28
- "test:watch": "concurrently --kill-others --names tsc,karma \"npm run tsc:watch\" \"karma start --auto-watch=true --single-run=false\"",
29
- "publish:alpha": "npm run test && npm run prepare && npm version prerelease --preid alpha --no-git-tag-version && npm publish --tag alpha"
30
- },
31
- "dependencies": {
32
- "@internetarchive/ia-activity-indicator": "^0.0.6",
33
- "@internetarchive/icon-close": "^1.3.4",
34
- "@internetarchive/icon-ia-logo": "^1.3.4",
35
- "@internetarchive/icon-user": "^1.3.4",
36
- "lit": "^2.8.0",
37
- "throttle-debounce": "^5.0.0"
38
- },
39
- "devDependencies": {
40
- "@open-wc/eslint-config": "^9.2.2",
41
- "@open-wc/testing": "^4.0.0",
42
- "@open-wc/testing-karma": "^3.0.0",
43
- "@types/mocha": "^10.0.0",
44
- "@types/node": "^20.8.4",
45
- "@types/throttle-debounce": "^5.0.0",
46
- "@typescript-eslint/eslint-plugin": "^7.1.0",
47
- "@typescript-eslint/parser": "^7.1.0",
48
- "concurrently": "^8.0.0",
49
- "deepmerge": "^4.0.0",
50
- "es-dev-server": "^2.0.0",
51
- "eslint": "^8.57.0",
52
- "eslint-config-prettier": "^6.11.0",
53
- "husky": "^9.0.0",
54
- "lint-staged": "^14.0.1",
55
- "madge": "^6.0.0",
56
- "prettier": "^2.7.1",
57
- "rimraf": "^5.0.5",
58
- "tslib": "^2.4.0",
59
- "typedoc": "^0.25.0",
60
- "typescript": "^4.7.4",
61
- "vite": "^4.4.11"
62
- },
63
- "eslintConfig": {
64
- "extends": [
65
- "@open-wc/eslint-config",
66
- "eslint-config-prettier"
67
- ]
68
- },
69
- "prettier": {
70
- "singleQuote": true,
71
- "arrowParens": "avoid"
72
- },
73
- "husky": {
74
- "hooks": {
75
- "pre-commit": "lint-staged"
76
- }
77
- },
78
- "lint-staged": {
79
- "*.ts": [
80
- "eslint --fix",
81
- "prettier --write",
82
- "git add"
83
- ]
84
- }
85
- }
1
+ {
2
+ "name": "@internetarchive/modal-manager",
3
+ "version": "2.0.4-alpha-webdev7960.1",
4
+ "description": "A Modal Manager Web Component",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/internetarchive/iaux-modal-manager.git"
8
+ },
9
+ "license": "AGPL-3.0-only",
10
+ "main": "dist/index.js",
11
+ "module": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "scripts": {
14
+ "prepare": "npm run build && husky install",
15
+ "prepare:ghpages": "rimraf ghpages && npm run prepare && vite build && cp -R assets/images ghpages/assets/images",
16
+ "start": "concurrently --kill-others --names tsc,es-dev-server \"npm run tsc:watch\" \"es-dev-server --app-index index.html --node-resolve --open --watch --preserve-symlinks\"",
17
+ "docs": "typedoc",
18
+ "build": "tsc",
19
+ "tsc:watch": "tsc --watch",
20
+ "lint:eslint": "eslint --ext .ts . --ignore-path .gitignore",
21
+ "format:eslint": "eslint --ext .ts . --fix --ignore-path .gitignore",
22
+ "lint:prettier": "prettier \"**/*.ts\" --check --ignore-path .gitignore",
23
+ "format:prettier": "prettier \"**/*.ts\" --write --ignore-path .gitignore",
24
+ "lint": "npm run lint:eslint && npm run lint:prettier",
25
+ "format": "npm run format:eslint && npm run format:prettier",
26
+ "circular": "madge --circular .",
27
+ "test": "tsc && npm run lint && npm run circular && karma start --coverage",
28
+ "test:watch": "concurrently --kill-others --names tsc,karma \"npm run tsc:watch\" \"karma start --auto-watch=true --single-run=false\"",
29
+ "publish:alpha": "npm run test && npm run prepare && npm version prerelease --preid alpha --no-git-tag-version && npm publish --tag alpha"
30
+ },
31
+ "dependencies": {
32
+ "@internetarchive/ia-activity-indicator": "^0.0.6",
33
+ "@internetarchive/icon-close": "^1.3.4",
34
+ "@internetarchive/icon-ia-logo": "^1.3.4",
35
+ "@internetarchive/icon-user": "^1.3.4",
36
+ "lit": "^2.8.0",
37
+ "throttle-debounce": "^5.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@open-wc/eslint-config": "^9.2.2",
41
+ "@open-wc/testing": "^4.0.0",
42
+ "@open-wc/testing-karma": "^3.0.0",
43
+ "@types/mocha": "^10.0.0",
44
+ "@types/node": "^20.8.4",
45
+ "@types/throttle-debounce": "^5.0.0",
46
+ "@typescript-eslint/eslint-plugin": "^7.1.0",
47
+ "@typescript-eslint/parser": "^7.1.0",
48
+ "concurrently": "^8.0.0",
49
+ "deepmerge": "^4.0.0",
50
+ "es-dev-server": "^2.0.0",
51
+ "eslint": "^8.57.0",
52
+ "eslint-config-prettier": "^6.11.0",
53
+ "husky": "^9.0.0",
54
+ "lint-staged": "^14.0.1",
55
+ "madge": "^6.0.0",
56
+ "prettier": "^2.7.1",
57
+ "rimraf": "^5.0.5",
58
+ "tslib": "^2.4.0",
59
+ "typedoc": "^0.25.0",
60
+ "typescript": "^4.7.4",
61
+ "vite": "^4.4.11"
62
+ },
63
+ "eslintConfig": {
64
+ "extends": [
65
+ "@open-wc/eslint-config",
66
+ "eslint-config-prettier"
67
+ ]
68
+ },
69
+ "prettier": {
70
+ "singleQuote": true,
71
+ "arrowParens": "avoid"
72
+ },
73
+ "husky": {
74
+ "hooks": {
75
+ "pre-commit": "lint-staged"
76
+ }
77
+ },
78
+ "lint-staged": {
79
+ "*.ts": [
80
+ "eslint --fix",
81
+ "prettier --write",
82
+ "git add"
83
+ ]
84
+ }
85
+ }
package/renovate.json CHANGED
@@ -1,7 +1,7 @@
1
- {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": [
4
- "config:base",
5
- ":preserveSemverRanges"
6
- ]
7
- }
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:base",
5
+ ":preserveSemverRanges"
6
+ ]
7
+ }
@@ -9,6 +9,7 @@ import {
9
9
  import { property, customElement, query } from 'lit/decorators.js';
10
10
 
11
11
  import Modal from './shoelace/modal';
12
+ import { getDeepestActiveElement } from './shoelace/active-elements';
12
13
 
13
14
  import './modal-template';
14
15
  import { ModalTemplate } from './modal-template';
@@ -112,6 +113,10 @@ export class ModalManager extends LitElement implements ModalManagerInterface {
112
113
  this.customModalContent = undefined;
113
114
  if (this.modalTemplate) this.modalTemplate.config = new ModalConfig();
114
115
  this.modal.deactivate();
116
+
117
+ // Return focus to the triggering element, if possible
118
+ (this.triggeringElement as HTMLElement)?.focus?.();
119
+ this.triggeringElement = undefined;
115
120
  }
116
121
 
117
122
  /**
@@ -122,6 +127,12 @@ export class ModalManager extends LitElement implements ModalManagerInterface {
122
127
  */
123
128
  private closeOnBackdropClick = true;
124
129
 
130
+ /**
131
+ * The element that had focus when the modal was opened, so that we can return focus
132
+ * to it after the modal closes.
133
+ */
134
+ private triggeringElement?: Element;
135
+
125
136
  /**
126
137
  * A callback if the user closes the modal
127
138
  *
@@ -173,6 +184,9 @@ export class ModalManager extends LitElement implements ModalManagerInterface {
173
184
  userClosedModalCallback?: () => void;
174
185
  userPressedLeftNavButtonCallback?: () => void;
175
186
  }): Promise<void> {
187
+ // If the dialog is being opened, make note of what element was focused beforehand
188
+ if (this.mode === ModalManagerMode.Closed) this.captureFocusedElement();
189
+
176
190
  this.closeOnBackdropClick = options.config.closeOnBackdropClick;
177
191
  this.userClosedModalCallback = options.userClosedModalCallback;
178
192
  this.userPressedLeftNavButtonCallback =
@@ -187,6 +201,14 @@ export class ModalManager extends LitElement implements ModalManagerInterface {
187
201
  this.modal.activate();
188
202
  }
189
203
 
204
+ /**
205
+ * Sets the triggering element to the one that is currently focused, as deep
206
+ * within Shadow DOM as possible.
207
+ */
208
+ private captureFocusedElement(): void {
209
+ this.triggeringElement = getDeepestActiveElement();
210
+ }
211
+
190
212
  /** @inheritdoc */
191
213
  updated(changed: PropertyValues): void {
192
214
  /* istanbul ignore else */
@@ -1,7 +1,7 @@
1
- Copyright (c) 2020 A Beautiful Site, LLC
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
-
5
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
-
1
+ Copyright (c) 2020 A Beautiful Site, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
7
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,4 +1,10 @@
1
- import { fixture, expect, oneEvent, elementUpdated } from '@open-wc/testing';
1
+ import {
2
+ fixture,
3
+ expect,
4
+ oneEvent,
5
+ elementUpdated,
6
+ nextFrame,
7
+ } from '@open-wc/testing';
2
8
  import { TemplateResult, html } from 'lit';
3
9
 
4
10
  import '../src/modal-manager';
@@ -322,16 +328,14 @@ describe('Modal Manager', () => {
322
328
  const closeButton = modal?.shadowRoot?.querySelector(
323
329
  '.close-button'
324
330
  ) as HTMLElement;
325
- const activeElement = modal?.shadowRoot?.activeElement as HTMLElement;
326
-
327
- expect(activeElement).to.equal(closeButton);
331
+ expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
328
332
 
329
333
  // Tab again
330
334
  el.dispatchEvent(tabEvent);
331
335
  await elementUpdated(el);
332
336
 
333
337
  // Should be only one tabbable element
334
- expect(activeElement).to.equal(closeButton);
338
+ expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
335
339
 
336
340
  // Shift + Tab
337
341
  const shiftTabEvent = new KeyboardEvent('keydown', {
@@ -342,6 +346,48 @@ describe('Modal Manager', () => {
342
346
  await elementUpdated(el);
343
347
 
344
348
  // Should be only one tabbable element
345
- expect(activeElement).to.equal(closeButton);
349
+ expect(modal?.shadowRoot?.activeElement).to.equal(closeButton);
350
+ });
351
+
352
+ it('returns keyboard focus to the triggering element on close', async () => {
353
+ const config = new ModalConfig();
354
+ const el = (await fixture(html`
355
+ <div>
356
+ <button>Another button</button>
357
+ <button
358
+ id="open-modal-btn"
359
+ @click=${() => {
360
+ const modal = el.querySelector('modal-manager') as ModalManager;
361
+ modal.showModal({ config });
362
+ }}
363
+ >
364
+ Open modal
365
+ </button>
366
+ <modal-manager></modal-manager>
367
+ </div>
368
+ `)) as HTMLDivElement;
369
+
370
+ const openBtn = el.querySelector('#open-modal-btn') as HTMLButtonElement;
371
+ const modal = el.querySelector('modal-manager') as ModalManager;
372
+
373
+ // Focus is initially on the Open button
374
+ openBtn.focus();
375
+ expect(document.activeElement).to.equal(openBtn);
376
+
377
+ // Focus enters the modal when it is opened
378
+ openBtn.click();
379
+ await nextFrame();
380
+ expect(document.activeElement).to.equal(modal);
381
+
382
+ // With the modal already open, simulate showing different content.
383
+ // This step is to ensure that even if showModal is called multiple times, we still
384
+ // maintain the originally-focused element (subsequent calls do not overwrite it).
385
+ modal.showModal({ config: new ModalConfig() });
386
+ await nextFrame();
387
+
388
+ // Focus returns to the Open button when the modal closes
389
+ modal.closeModal();
390
+ await modal.updateComplete;
391
+ expect(document.activeElement).to.equal(openBtn);
346
392
  });
347
393
  });
package/tsconfig.json CHANGED
@@ -1,21 +1,21 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es6",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "noEmitOnError": true,
7
- "lib": ["es2017", "dom"],
8
- "strict": true,
9
- "esModuleInterop": false,
10
- "allowSyntheticDefaultImports": true,
11
- "experimentalDecorators": true,
12
- "importHelpers": true,
13
- "outDir": "dist",
14
- "sourceMap": true,
15
- "inlineSources": true,
16
- "declaration": true,
17
- "rootDir": "./",
18
- "skipLibCheck": true
19
- },
20
- "include": ["**/*.ts"]
21
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "esnext",
5
+ "moduleResolution": "node",
6
+ "noEmitOnError": true,
7
+ "lib": ["es2017", "dom"],
8
+ "strict": true,
9
+ "esModuleInterop": false,
10
+ "allowSyntheticDefaultImports": true,
11
+ "experimentalDecorators": true,
12
+ "importHelpers": true,
13
+ "outDir": "dist",
14
+ "sourceMap": true,
15
+ "inlineSources": true,
16
+ "declaration": true,
17
+ "rootDir": "./",
18
+ "skipLibCheck": true
19
+ },
20
+ "include": ["**/*.ts"]
21
+ }