@lion/ui 0.8.2 → 0.8.3

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 (66) hide show
  1. package/components/overlays/src/OverlayController.js +40 -13
  2. package/components/overlays/test/OverlayController.test.js +233 -30
  3. package/components/overlays/test-helpers/mimicClick.js +2 -2
  4. package/custom-elements.json +31 -4
  5. package/dist-types/components/form-integrations/test/helpers/helpers.d.ts +3 -3
  6. package/dist-types/components/form-integrations/test/helpers/helpers.d.ts.map +1 -1
  7. package/dist-types/components/overlays/src/OverlayController.d.ts +9 -5
  8. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  9. package/dist-types/components/overlays/test/OverlayController.test.d.ts +1 -1
  10. package/dist-types/components/overlays/test/OverlayController.test.d.ts.map +1 -1
  11. package/dist-types/components/overlays/test-helpers/mimicClick.d.ts +1 -1
  12. package/dist-types/tsconfig.tsbuildinfo +1 -1
  13. package/docs/components/checkbox-group/overview.md +2 -2
  14. package/docs/components/collapsible/overview.md +1 -1
  15. package/docs/components/combobox/overview.md +1 -1
  16. package/docs/components/combobox/use-cases.md +1 -1
  17. package/docs/components/dialog/overview.md +1 -1
  18. package/docs/components/fieldset/overview.md +5 -5
  19. package/docs/components/form/overview.md +3 -3
  20. package/docs/components/icon/use-cases.md +1 -1
  21. package/docs/components/input/overview.md +1 -1
  22. package/docs/components/input-amount/overview.md +3 -3
  23. package/docs/components/input-date/overview.md +3 -3
  24. package/docs/components/input-datepicker/overview.md +5 -5
  25. package/docs/components/input-email/overview.md +2 -2
  26. package/docs/components/input-file/overview.md +1 -1
  27. package/docs/components/input-iban/overview.md +2 -2
  28. package/docs/components/input-range/overview.md +2 -2
  29. package/docs/components/input-stepper/overview.md +1 -1
  30. package/docs/components/input-tel/overview.md +1 -1
  31. package/docs/components/input-tel/use-cases.md +6 -6
  32. package/docs/components/input-tel-dropdown/overview.md +1 -1
  33. package/docs/components/listbox/use-cases.md +1 -1
  34. package/docs/components/radio-group/overview.md +3 -3
  35. package/docs/components/select/use-cases.md +1 -1
  36. package/docs/components/select-rich/use-cases.md +1 -1
  37. package/docs/components/tabs/examples.md +1 -1
  38. package/docs/fundamentals/index.md +4 -4
  39. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  40. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  41. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  42. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  43. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  44. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  45. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  46. package/docs/fundamentals/rationales/accessibility.md +1 -1
  47. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  48. package/docs/fundamentals/systems/core/overview.md +1 -1
  49. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  50. package/docs/fundamentals/systems/form/model-value.md +1 -1
  51. package/docs/fundamentals/systems/form/overview.md +28 -28
  52. package/docs/fundamentals/systems/form/validate.md +2 -2
  53. package/docs/fundamentals/systems/localize/overview.md +4 -4
  54. package/docs/fundamentals/systems/overlays/api-table.md +1 -0
  55. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  56. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  57. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  58. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  59. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  60. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  61. package/docs/guides/how-to/create-a-form.md +4 -4
  62. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  63. package/docs/guides/index.md +8 -8
  64. package/docs/guides/principles/index.md +5 -5
  65. package/docs/guides/principles/styling.md +1 -1
  66. package/package.json +1 -1
@@ -1,16 +1,17 @@
1
1
  import { overlays } from './singleton.js';
2
2
  import { containFocus } from './utils/contain-focus.js';
3
+ import { deepContains } from './utils/deep-contains.js';
3
4
  import { overlayShadowDomStyle } from './overlayShadowDomStyle.js';
4
5
  import { _adoptStyleUtils } from './utils/adopt-styles.js';
5
6
 
6
7
  /**
7
- * @typedef {import('@lion/ui/types/overlays.js').OverlayConfig} OverlayConfig
8
+ * @typedef {'setup'|'init'|'teardown'|'before-show'|'show'|'hide'|'add'|'remove'} OverlayPhase
8
9
  * @typedef {import('@lion/ui/types/overlays.js').ViewportConfig} ViewportConfig
9
- * @typedef {import('@popperjs/core').createPopper} Popper
10
+ * @typedef {import('@lion/ui/types/overlays.js').OverlayConfig} OverlayConfig
10
11
  * @typedef {import('@popperjs/core').Options} PopperOptions
11
12
  * @typedef {import('@popperjs/core').Placement} Placement
13
+ * @typedef {import('@popperjs/core').createPopper} Popper
12
14
  * @typedef {{ createPopper: Popper }} PopperModule
13
- * @typedef {'setup'|'init'|'teardown'|'before-show'|'show'|'hide'|'add'|'remove'} OverlayPhase
14
15
  */
15
16
 
16
17
  /**
@@ -103,6 +104,8 @@ async function preloadPopper() {
103
104
  return /** @type {* & Promise<PopperModule>} */ (import('@popperjs/core/dist/esm/popper.js'));
104
105
  }
105
106
 
107
+ const childDialogsClosedInEventLoopWeakmap = new WeakMap();
108
+
106
109
  /**
107
110
  * OverlayController is the fundament for every single type of overlay. With the right
108
111
  * configuration, it can be used to build (modal) dialogs, tooltips, dropdowns, popovers,
@@ -498,9 +501,6 @@ export class OverlayController extends EventTarget {
498
501
  '[OverlayController] .isTooltip only takes effect when .handlesAccessibility is enabled',
499
502
  );
500
503
  }
501
- // if (newConfig.popperConfig.modifiers.arrow && !newConfig.contentWrapperNode) {
502
- // throw new Error('You need to provide a .contentWrapperNode when Popper arrow is enabled');
503
- // }
504
504
  }
505
505
 
506
506
  /**
@@ -1146,11 +1146,40 @@ export class OverlayController extends EventTarget {
1146
1146
  ev.preventDefault();
1147
1147
  }
1148
1148
 
1149
- /** @private */
1150
- __escKeyHandler(/** @type {KeyboardEvent} */ ev) {
1151
- return ev.key === 'Escape' && this.hide();
1149
+ /**
1150
+ * @param {KeyboardEvent} event
1151
+ * @returns {void}
1152
+ */
1153
+ __escKeyHandler(event) {
1154
+ if (event.key !== 'Escape' || childDialogsClosedInEventLoopWeakmap.has(event)) return;
1155
+
1156
+ const hasPressedInside =
1157
+ event.composedPath().includes(this.contentNode) ||
1158
+ deepContains(this.contentNode, /** @type {HTMLElement|ShadowRoot} */ (event.target));
1159
+ if (hasPressedInside) {
1160
+ this.hide();
1161
+ // We could do event.stopPropagation() here, but we don't want to hide info for
1162
+ // the outside world about user interactions. Instead, we store the event in a WeakMap
1163
+ // that will be garbage collected after the event loop.
1164
+ childDialogsClosedInEventLoopWeakmap.set(event, this);
1165
+ }
1152
1166
  }
1153
1167
 
1168
+ /**
1169
+ * @param {KeyboardEvent} event
1170
+ * @returns {void}
1171
+ */
1172
+ #outsideEscKeyHandler = event => {
1173
+ if (event.key !== 'Escape') return;
1174
+
1175
+ const hasPressedInside =
1176
+ event.composedPath().includes(this.contentNode) ||
1177
+ deepContains(this.contentNode, /** @type {HTMLElement|ShadowRoot} */ (event.target));
1178
+ if (!hasPressedInside) {
1179
+ this.hide();
1180
+ }
1181
+ };
1182
+
1154
1183
  /**
1155
1184
  * @param {{ phase: OverlayPhase }} config
1156
1185
  * @protected
@@ -1175,11 +1204,9 @@ export class OverlayController extends EventTarget {
1175
1204
  */
1176
1205
  _handleHidesOnOutsideEsc({ phase }) {
1177
1206
  if (phase === 'show') {
1178
- this.__escKeyHandler = (/** @type {KeyboardEvent} */ ev) =>
1179
- ev.key === 'Escape' && this.hide();
1180
- document.addEventListener('keyup', this.__escKeyHandler);
1207
+ document.addEventListener('keyup', this.#outsideEscKeyHandler);
1181
1208
  } else if (phase === 'hide') {
1182
- document.removeEventListener('keyup', this.__escKeyHandler);
1209
+ document.removeEventListener('keyup', this.#outsideEscKeyHandler);
1183
1210
  }
1184
1211
  }
1185
1212
 
@@ -1,28 +1,41 @@
1
1
  /* eslint-disable no-new */
2
+ import { OverlayController, overlays } from '@lion/ui/overlays.js';
3
+ import { mimicClick } from '@lion/ui/overlays-test-helpers.js';
4
+ import sinon from 'sinon';
2
5
  import {
3
- aTimeout,
6
+ unsafeStatic,
7
+ fixtureSync,
4
8
  defineCE,
5
- expect,
9
+ aTimeout,
6
10
  fixture,
11
+ expect,
7
12
  html,
8
- unsafeStatic,
9
- fixtureSync,
10
13
  } from '@open-wc/testing';
11
- import sinon from 'sinon';
12
- import { OverlayController, overlays } from '@lion/ui/overlays.js';
13
- import { mimicClick } from '@lion/ui/overlays-test-helpers.js';
14
- import { keyCodes } from '../src/utils/key-codes.js';
15
- import { simulateTab } from '../src/utils/simulate-tab.js';
16
- import { _adoptStyleUtils } from '../src/utils/adopt-styles.js';
14
+
17
15
  import { createShadowHost } from '../test-helpers/createShadowHost.js';
16
+ import { _adoptStyleUtils } from '../src/utils/adopt-styles.js';
17
+ import { simulateTab } from '../src/utils/simulate-tab.js';
18
+ import { keyCodes } from '../src/utils/key-codes.js';
18
19
 
19
20
  /**
20
- * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig
21
21
  * @typedef {import('../types/OverlayConfig.js').ViewportPlacement} ViewportPlacement
22
+ * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig
22
23
  */
23
24
 
24
25
  const wrappingDialogNodeStyle = 'display: none; z-index: 9999; padding: 0px;';
25
26
 
27
+ /**
28
+ * A small wrapper function that closely mimics an escape press from a user
29
+ * (prevents common mistakes like no bubbling or keydown)
30
+ * @param {Element|Document} element
31
+ */
32
+ async function mimicEscapePress(element) {
33
+ element.dispatchEvent(
34
+ new KeyboardEvent('keyup', { key: 'Escape', bubbles: true, composed: true }),
35
+ );
36
+ await aTimeout(0);
37
+ }
38
+
26
39
  /**
27
40
  * Make sure that all browsers serialize html in a similar way
28
41
  * (Firefox tends to output empty style attrs)
@@ -74,6 +87,50 @@ const withLocalTestConfig = () =>
74
87
  ),
75
88
  });
76
89
 
90
+ /**
91
+ * @param {HTMLDivElement} parentContent
92
+ * @returns {Promise<{parentOverlay: OverlayController; childOverlay: OverlayController}>}
93
+ */
94
+ async function createNestedEscControllers(parentContent) {
95
+ const childContent = /** @type {HTMLDivElement} */ (parentContent.querySelector('div[id]'));
96
+ // Assert valid fixure
97
+ const isValid =
98
+ (parentContent.id === 'parent-overlay--hidesOnEsc' ||
99
+ parentContent.id === 'parent-overlay--hidesOnOutsideEsc') &&
100
+ (childContent.id === 'child-overlay--hidesOnEsc' ||
101
+ childContent.id === 'child-overlay--hidesOnOutsideEsc');
102
+ if (!isValid) {
103
+ throw new Error('Provide a valid fixture');
104
+ }
105
+
106
+ if (parentContent.hasAttribute('data-convert-to-shadow-root')) {
107
+ const shadowRootParent = parentContent.attachShadow({ mode: 'open' });
108
+ shadowRootParent.appendChild(childContent);
109
+ }
110
+
111
+ const parentHasOutsideOnEsc = parentContent.id === 'parent-overlay--hidesOnOutsideEsc';
112
+ const childHasOutsideOnEsc = childContent.id === 'child-overlay--hidesOnOutsideEsc';
113
+
114
+ const parentConfig = parentHasOutsideOnEsc ? { hidesOnOutsideEsc: true } : { hidesOnEsc: true };
115
+ const childConfig = childHasOutsideOnEsc ? { hidesOnOutsideEsc: true } : { hidesOnEsc: true };
116
+
117
+ const parentOverlay = new OverlayController({
118
+ ...withGlobalTestConfig(),
119
+ contentNode: parentContent,
120
+ ...parentConfig,
121
+ });
122
+ const childOverlay = new OverlayController({
123
+ ...withGlobalTestConfig(),
124
+ contentNode: childContent,
125
+ ...childConfig,
126
+ });
127
+
128
+ await parentOverlay.show();
129
+ await childOverlay.show();
130
+
131
+ return { parentOverlay, childOverlay };
132
+ }
133
+
77
134
  afterEach(() => {
78
135
  overlays.teardown();
79
136
  });
@@ -574,74 +631,221 @@ describe('OverlayController', () => {
574
631
  });
575
632
 
576
633
  describe('hidesOnEsc', () => {
577
- it('hides when [escape] is pressed', async () => {
634
+ it('hides on [Escape] press', async () => {
578
635
  const ctrl = new OverlayController({
579
636
  ...withGlobalTestConfig(),
580
637
  hidesOnEsc: true,
581
638
  });
582
639
  await ctrl.show();
583
- ctrl.contentNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
584
- await aTimeout(0);
640
+ await mimicEscapePress(ctrl.contentNode);
585
641
  expect(ctrl.isShown).to.be.false;
586
642
  });
587
643
 
588
- it("doesn't hide when [escape] is pressed and hidesOnEsc is set to false", async () => {
644
+ it('stays shown on [Escape] press with `.hidesOnEsc` set to false', async () => {
589
645
  const ctrl = new OverlayController({
590
646
  ...withGlobalTestConfig(),
591
647
  hidesOnEsc: false,
592
648
  });
593
649
  await ctrl.show();
594
- ctrl.contentNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
595
- await aTimeout(0);
650
+ await mimicEscapePress(ctrl.contentNode);
596
651
  expect(ctrl.isShown).to.be.true;
597
652
  });
598
653
 
599
- it('stays shown when [escape] is pressed on outside element', async () => {
654
+ it('stays shown on [Escape] press on outside element', async () => {
600
655
  const ctrl = new OverlayController({
601
656
  ...withGlobalTestConfig(),
602
657
  hidesOnEsc: true,
603
658
  });
604
659
  await ctrl.show();
605
- document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
660
+ await mimicEscapePress(document);
606
661
  expect(ctrl.isShown).to.be.true;
607
662
  });
608
663
 
609
- it('does not hide when [escape] is pressed with modal <dialog> and "hidesOnEsc" is false', async () => {
664
+ it('stays shown on [Escape] press with modal <dialog> and `.hidesOnEsc` is false', async () => {
610
665
  const ctrl = new OverlayController({
611
666
  ...withGlobalTestConfig(),
612
667
  trapsKeyboardFocus: true,
613
668
  hidesOnEsc: false,
614
669
  });
615
670
  await ctrl.show();
616
- ctrl.contentNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
617
- await aTimeout(0);
671
+ await mimicEscapePress(ctrl.contentNode);
618
672
  expect(ctrl.isShown).to.be.true;
619
673
  });
674
+
675
+ it('parent stays shown on [Escape] press in a nested overlay', async () => {
676
+ const parentContent = /** @type {HTMLDivElement} */ (
677
+ await fixture(
678
+ html` <!-- -->
679
+ <div id="parent-overlay--hidesOnEsc">
680
+ <div id="child-overlay--hidesOnEsc">we press [Escape] here</div>
681
+ </div>`,
682
+ )
683
+ );
684
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
685
+ await mimicEscapePress(childOverlay.contentNode);
686
+
687
+ expect(parentOverlay.isShown).to.be.true;
688
+ expect(childOverlay.isShown).to.be.false;
689
+ });
620
690
  });
621
691
 
622
692
  describe('hidesOnOutsideEsc', () => {
623
- it('hides when [escape] is pressed on outside element', async () => {
693
+ it('hides on [Escape] press on outside element', async () => {
624
694
  const ctrl = new OverlayController({
625
695
  ...withGlobalTestConfig(),
626
696
  hidesOnOutsideEsc: true,
627
697
  });
628
698
  await ctrl.show();
629
- document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
630
- await aTimeout(0);
699
+ await mimicEscapePress(document);
631
700
  expect(ctrl.isShown).to.be.false;
632
701
  });
633
702
 
634
- it('stays shown when [escape] is pressed on inside element', async () => {
703
+ it('stays shown on [Escape] press on inside element', async () => {
635
704
  const ctrl = new OverlayController({
636
705
  ...withGlobalTestConfig(),
637
706
  hidesOnOutsideEsc: true,
638
707
  });
639
708
  await ctrl.show();
640
- ctrl.contentNode.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
709
+ await mimicEscapePress(ctrl.contentNode);
641
710
  expect(ctrl.isShown).to.be.true;
642
711
  });
712
+
713
+ it('stays shown on [Escape] press in a nested overlay', async () => {
714
+ const parentContent = /** @type {HTMLDivElement} */ (
715
+ await fixture(
716
+ html` <!-- -->
717
+ <div id="parent-overlay--hidesOnOutsideEsc">
718
+ <div id="child-overlay--hidesOnOutsideEsc">we press [Escape] here</div>
719
+ </div>`,
720
+ )
721
+ );
722
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
723
+ mimicEscapePress(childOverlay.contentNode);
724
+
725
+ expect(parentOverlay.isShown).to.be.true;
726
+ expect(childOverlay.isShown).to.be.true;
727
+ });
643
728
  });
644
729
 
730
+ describe('Nested hidesOnEsc / hidesOnOutsideEsc', () => {
731
+ describe('Parent has hidesOnEsc and child has hidesOnOutsideEsc', () => {
732
+ it('on [Escape] press in child overlay: parent hides, child stays shown', async () => {
733
+ const parentContent = /** @type {HTMLDivElement} */ (
734
+ await fixture(
735
+ html` <!-- -->
736
+ <div id="parent-overlay--hidesOnEsc">
737
+ <div id="child-overlay--hidesOnOutsideEsc">we press [Escape] here</div>
738
+ </div>`,
739
+ )
740
+ );
741
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
742
+ await mimicEscapePress(childOverlay.contentNode);
743
+
744
+ expect(parentOverlay.isShown).to.be.false;
745
+ expect(childOverlay.isShown).to.be.true;
746
+ });
747
+
748
+ it('on [Escape] press outside overlays: parent stays shown, child hides', async () => {
749
+ const parentContent = /** @type {HTMLDivElement} */ (
750
+ await fixture(
751
+ html` <!-- we press [Escape] here -->
752
+ <div id="parent-overlay--hidesOnEsc">
753
+ <div id="child-overlay--hidesOnOutsideEsc"></div>
754
+ </div>`,
755
+ )
756
+ );
757
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
758
+ await mimicEscapePress(document);
759
+
760
+ expect(parentOverlay.isShown).to.be.true;
761
+ expect(childOverlay.isShown).to.be.false;
762
+ });
763
+
764
+ it('on [Escape] press in parent overlay: parent is hidden, child is hidden', async () => {
765
+ const parentContent = /** @type {HTMLDivElement} */ (
766
+ await fixture(
767
+ html` <!-- -->
768
+ <div id="parent-overlay--hidesOnEsc">
769
+ we press [Escape] here
770
+ <div id="child-overlay--hidesOnOutsideEsc"></div>
771
+ </div>`,
772
+ )
773
+ );
774
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
775
+ await mimicEscapePress(parentContent);
776
+
777
+ expect(parentOverlay.isShown).to.be.false;
778
+ expect(childOverlay.isShown).to.be.false;
779
+ });
780
+ });
781
+ describe('Parent has hidesOnOutsideEsc and child has hidesOnEsc', () => {
782
+ it('on [Escape] press in child overlay: parent stays shown, child hides', async () => {
783
+ const parentContent = /** @type {HTMLDivElement} */ (
784
+ await fixture(
785
+ html` <!-- -->
786
+ <div id="parent-overlay--hidesOnOutsideEsc">
787
+ <div id="child-overlay--hidesOnEsc">we press [Escape] here</div>
788
+ </div>`,
789
+ )
790
+ );
791
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
792
+ await mimicEscapePress(childOverlay.contentNode);
793
+
794
+ expect(parentOverlay.isShown).to.be.true;
795
+ expect(childOverlay.isShown).to.be.false;
796
+ });
797
+
798
+ it('on [Escape] press outside overlays: parent hides, child stays shown', async () => {
799
+ const parentContent = /** @type {HTMLDivElement} */ (
800
+ await fixture(
801
+ html` <!-- we press [Escape] here -->
802
+ <div id="parent-overlay--hidesOnOutsideEsc">
803
+ <div id="child-overlay--hidesOnEsc"></div>
804
+ </div>`,
805
+ )
806
+ );
807
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
808
+ await mimicEscapePress(document);
809
+
810
+ expect(parentOverlay.isShown).to.be.false;
811
+ expect(childOverlay.isShown).to.be.true;
812
+ });
813
+
814
+ it('on [Escape] press in parent overlay: parent hides, child stays shown', async () => {
815
+ const parentContent = /** @type {HTMLDivElement} */ (
816
+ await fixture(
817
+ html` <!-- we press [Escape] here -->
818
+ <div id="parent-overlay--hidesOnOutsideEsc">
819
+ <div id="child-overlay--hidesOnEsc"></div>
820
+ </div>`,
821
+ )
822
+ );
823
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
824
+ await mimicEscapePress(document);
825
+
826
+ expect(parentOverlay.isShown).to.be.false;
827
+ expect(childOverlay.isShown).to.be.true;
828
+ });
829
+ });
830
+
831
+ describe('With shadow dom', () => {
832
+ it('on [Escape] press in child overlay in shadow root: parent hides, child stays shown', async () => {
833
+ const parentContent = /** @type {HTMLDivElement} */ (
834
+ await fixture(
835
+ html` <!-- -->
836
+ <div id="parent-overlay--hidesOnEsc" data-convert-to-shadow-root>
837
+ <div id="child-overlay--hidesOnOutsideEsc">we press [Escape] here</div>
838
+ </div>`,
839
+ )
840
+ );
841
+ const { parentOverlay, childOverlay } = await createNestedEscControllers(parentContent);
842
+ await mimicEscapePress(childOverlay.contentNode);
843
+
844
+ expect(parentOverlay.isShown).to.be.false;
845
+ expect(childOverlay.isShown).to.be.true;
846
+ });
847
+ });
848
+ });
645
849
  describe('hidesOnOutsideClick', () => {
646
850
  it('hides on outside click', async () => {
647
851
  const contentNode = /** @type {HTMLElement} */ (await fixture('<div>Content</div>'));
@@ -684,9 +888,8 @@ describe('OverlayController', () => {
684
888
  expect(ctrl.isShown).to.be.true;
685
889
 
686
890
  // Don't hide on inside mousedown & outside mouseup
687
- ctrl.contentNode.dispatchEvent(new MouseEvent('mousedown'));
688
- await aTimeout(0);
689
- document.body.dispatchEvent(new MouseEvent('mouseup'));
891
+ await mimicClick(ctrl.contentNode, { releaseElement: document.body, isAsync: true });
892
+
690
893
  await aTimeout(0);
691
894
  expect(ctrl.isShown).to.be.true;
692
895
 
@@ -8,9 +8,9 @@ async function sleep(t = 0) {
8
8
 
9
9
  /**
10
10
  * @param {HTMLElement} el
11
- * @param {{isAsync?:boolean, releaseElement?: HTMLElement}} config
11
+ * @param {{isAsync?:boolean, releaseElement?: HTMLElement}} config releaseElement can be different when the mouse is dragged before release
12
12
  */
13
- export async function mimicClick(el, { isAsync, releaseElement } = { isAsync: false }) {
13
+ export async function mimicClick(el, { isAsync = false, releaseElement } = {}) {
14
14
  const releaseEl = releaseElement || el;
15
15
  el.dispatchEvent(new MouseEvent('mousedown'));
16
16
  if (isAsync) {
@@ -114306,10 +114306,11 @@
114306
114306
  }
114307
114307
  },
114308
114308
  {
114309
- "name": "{ isAsync, releaseElement }",
114310
- "default": "{ isAsync: false }"
114309
+ "name": "{ isAsync = false, releaseElement }",
114310
+ "default": "{}"
114311
114311
  },
114312
114312
  {
114313
+ "description": "releaseElement can be different when the mouse is dragged before release",
114313
114314
  "name": "config",
114314
114315
  "type": {
114315
114316
  "text": "{isAsync?:boolean, releaseElement?: HTMLElement}"
@@ -114997,10 +114998,36 @@
114997
114998
  "name": "__escKeyHandler",
114998
114999
  "parameters": [
114999
115000
  {
115000
- "name": "ev"
115001
+ "name": "event",
115002
+ "type": {
115003
+ "text": "KeyboardEvent"
115004
+ }
115001
115005
  }
115002
115006
  ],
115003
- "privacy": "private"
115007
+ "return": {
115008
+ "type": {
115009
+ "text": "void"
115010
+ }
115011
+ },
115012
+ "privacy": "protected"
115013
+ },
115014
+ {
115015
+ "kind": "field",
115016
+ "name": "#outsideEscKeyHandler",
115017
+ "privacy": "private",
115018
+ "parameters": [
115019
+ {
115020
+ "name": "event",
115021
+ "type": {
115022
+ "text": "KeyboardEvent"
115023
+ }
115024
+ }
115025
+ ],
115026
+ "return": {
115027
+ "type": {
115028
+ "text": "void"
115029
+ }
115030
+ }
115004
115031
  },
115005
115032
  {
115006
115033
  "kind": "method",
@@ -5,15 +5,15 @@
5
5
  /**
6
6
  * @param {LionForm} formGroupEl
7
7
  */
8
- export function getAllFormElements(formGroupEl: LionForm): any[];
8
+ export function getAllFormElements(formGroupEl: LionForm): any;
9
9
  /**
10
10
  * @param {LionForm} formGroupEl
11
11
  */
12
- export function getAllTagNames(formGroupEl: LionForm): any[];
12
+ export function getAllTagNames(formGroupEl: LionForm): any;
13
13
  /**
14
14
  * @param {LionForm} formGroupEl
15
15
  */
16
- export function getAllFieldsAndFormGroups(formGroupEl: LionForm): any[];
16
+ export function getAllFieldsAndFormGroups(formGroupEl: LionForm): any;
17
17
  export type LionForm = import('../../../form/src/LionForm.js').LionForm;
18
18
  export type LionField = import('../../../form-core/src/LionField.js').LionField;
19
19
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../../components/form-integrations/test/helpers/helpers.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,gDAFW,QAAQ,SAWlB;AAED;;GAEG;AACH,4CAFW,QAAQ,SAWlB;AAED;;GAEG;AACH,uDAFW,QAAQ,SAKlB;uBAtCY,OAAO,+BAA+B,EAAE,QAAQ;wBAChD,OAAO,qCAAqC,EAAE,SAAS"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../../components/form-integrations/test/helpers/helpers.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,gDAFW,QAAQ,OAWlB;AAED;;GAEG;AACH,4CAFW,QAAQ,OAWlB;AAED;;GAEG;AACH,uDAFW,QAAQ,OAKlB;uBAtCY,OAAO,+BAA+B,EAAE,QAAQ;wBAChD,OAAO,qCAAqC,EAAE,SAAS"}
@@ -31,8 +31,11 @@ export class OverlayController extends EventTarget {
31
31
  private __hasActiveTrapsKeyboardFocus;
32
32
  /** @private */
33
33
  private __hasActiveBackdrop;
34
- /** @private */
35
- private __escKeyHandler;
34
+ /**
35
+ * @param {KeyboardEvent} event
36
+ * @returns {void}
37
+ */
38
+ __escKeyHandler(event: KeyboardEvent): void;
36
39
  /**
37
40
  * When the overlay is a modal dialog hidesOnEsc works out of the box, so we prevent that.
38
41
  *
@@ -434,17 +437,18 @@ export class OverlayController extends EventTarget {
434
437
  private __createPopperInstance;
435
438
  _popper: any;
436
439
  _hasDisabledInvoker(): boolean;
440
+ #private;
437
441
  }
438
442
  export namespace OverlayController {
439
443
  const popperModule: Promise<PopperModule> | undefined;
440
444
  }
441
- export type OverlayConfig = import('@lion/ui/types/overlays.js').OverlayConfig;
445
+ export type OverlayPhase = 'setup' | 'init' | 'teardown' | 'before-show' | 'show' | 'hide' | 'add' | 'remove';
442
446
  export type ViewportConfig = import('@lion/ui/types/overlays.js').ViewportConfig;
443
- export type Popper = any;
447
+ export type OverlayConfig = import('@lion/ui/types/overlays.js').OverlayConfig;
444
448
  export type PopperOptions = import('@popperjs/core').Options;
445
449
  export type Placement = import('@popperjs/core').Placement;
450
+ export type Popper = any;
446
451
  export type PopperModule = {
447
452
  createPopper: Popper;
448
453
  };
449
- export type OverlayPhase = 'setup' | 'init' | 'teardown' | 'before-show' | 'show' | 'hide' | 'add' | 'remove';
450
454
  //# sourceMappingURL=OverlayController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayController.d.ts","sourceRoot":"","sources":["../../../../components/overlays/src/OverlayController.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OverlayController.d.ts","sourceRoot":"","sources":["../../../../components/overlays/src/OverlayController.js"],"names":[],"mappings":"AA4GA;;;;;GAKG;AACH;IACE;;;;OAIG;IACH,qBAHW,aAAa,4DA2FvB;IAtFC,wDAAsB;IACtB,eAAe;IACf,uBAA4B;IAE5B,eAAe;IACf,uCAA0C;IAE1C,4BAA4B;IAC5B,QADW,aAAa,CACR;IAEhB;;;OAGG;IACH,0BAHU,aAAa,CA2DtB;IAGD,iBAAiB;IACjB,6BAA+E;IAC/E,eAAe;IACf,wBAAgC;IAEhC,eAAe;IACf,sCAA0C;IAC1C,eAAe;IACf,4BAA+B;IAi7BjC;;;OAGG;IACH,uBAHW,aAAa,GACX,IAAI,CAehB;IA5BD;;;;;OAKG;IAEH,wBAEC;IAx6BD;;;OAGG;IACH,uCAEC;IAED;;;OAGG;IACH,kDAEC;IAED;;;OAGG;IACH,oDAEC;IAED;;;OAGG;IACH,2CAEC;IAED;;;;;OAKG;IACH,iCAEC;IAED;;;OAGG;IACH,+BAEC;IAED;;;;;;OAMG;IACH,sCAIC;IAED;;;;OAIG;IACH,gCAEC;IAED;;;OAGG;IACH,2CAIC;IAED;;;OAGG;IACH,2BAEC;IAED;;;OAGG;IACH,0BAEC;IAED;;;OAGG;IACH,8BAEC;IAED;;;OAGG;IACH,kCAEC;IAED;;;OAGG;IACH,0BAEC;IAED;;;OAGG;IACH,mCAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;;;;OAMG;IACH,0EAEC;IAED;;;;;;;;;;;OAWG;IACH,oCAEC;IAED;;;;OAIG;IACH,yBAEC;IAED;;;;OAIG;IACH,2DAEC;IAED;;;OAGG;IACH,qDAEC;IAED;;;OAGG;IACH,0EAEC;IAED,0CAEC;IAED;;;;OAIG;IACH,wDAEC;IAED;;OAEG;IACH,2BAGC;IAED;;OAEG;IACH,wBAEC;IAED;;;;;;OAMG;IACH,uBAFY,aAAa,QAmCxB;IA7BC;;;OAGG;IACH,qBAA+B;IAuB/B,eAAe;IACf,kCAA0C;IAG5C;;;OAGG;IAEH,gCAmBC;IAED;;OAEG;IACH,wBAoBC;IAjBG,iDAAuC;IAmB3C;;;OAGG;IACH,8BAOC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,kCA8CC;IA3BC,qEAAiD;IAO/C,iDAAyD;IAsB7D;;;;OAIG;IACH;eAHoB,YAAY;aAc/B;IAED;;;OAGG;IACH,qCAmCC;IAED;;;;OAIG;IACH,6BAMC;IAED,eAAe;IACf,+BAWC;IAED,uBAEC;IAED;;;;OAIG;IACH,+BAFW,WAAW,iBA2CrB;IAnCC,wCAEE;IADA,iDAA2B;IAoC/B;;;OAGG;IACH;eAHoB,YAAY;sBA2B/B;IAED;;;OAGG;IACH;eAHoB,YAAY;aAgD/B;IAtCK,sCAAkD;IAClD,uCAAoD;IACpD,4CAA8D;IAC9D,6CAAgE;IAK9D,sCAAiF;IACjF,uCAAmF;IA+B3F;;;OAGG;IACH,sBA0CC;IAzCC,wCAEE;IADA,iDAA2B;IA0C/B;;;;OAIG;IAGH;sBAJyB,WAAW;qBAAc,WAAW;sBAI1B;IAEnC;;;OAGG;IAEH;sBAJyB,WAAW;qBAAc,WAAW;sBAY5D;IAED;;;;OAIG;IAGH;sBAJyB,WAAW;qBAAc,WAAW;sBAI1B;IAEnC;;OAEG;IAEH;sBAHyB,WAAW;qBAAc,WAAW;sBAU5D;IAED,iBAAiB;IACjB,gCAiBC;IAED,wBAEC;IAED;;;;OAIG;IACH;eAHoB,YAAY;aAoC/B;IAED;;OAEG;IACH;eAFoB,YAAY;aAc/B;IATK,gCAGE;IAQR;;;OAGG;IACH;eAHoB,YAAY;aAa/B;IAED;;;OAGG;IACH;eAHoB,YAAY;aAa/B;IAED,iCAEC;IAED;;;;;;OAMG;IACH;;eAHyC,YAAY;aA+BpD;IAtBS,2CAAmD;IAMrD,2CAAiC;IAkBzC,2CAEC;IAED;;;OAGG;IACH;eAHoB,YAAY;aAkB/B;IAED,iCAYC;IALC;;kBAA0D;IAO5D;;aAYC;IA+CD;;;OAGG;IACH;eAHoB,YAAY;aAe/B;IAED;;;OAGG;IACH;eAHoB,YAAY;aAS/B;IAED,iBAAiB;IACjB,gDAkBC;IAED;;;OAGG;IACH;eAHoB,YAAY;aAyF/B;IA7DG,iDAAiD;IACjD,oEAGC;IAED,4CAGC;IAED,iDAAiD;IACjD,oEAYC;IAuCL;;;OAGG;IACH;eAHoB,YAAY;aAW/B;IAED,iBAIC;IAED,eAAe;IACf,+BAYC;IATG,aAAwB;IAW5B,+BAQC;;CACF;;wBACU,QAAQ,YAAY,CAAC,GAAG,SAAS;;2BAn1C/B,OAAO,GAAC,MAAM,GAAC,UAAU,GAAC,aAAa,GAAC,MAAM,GAAC,MAAM,GAAC,KAAK,GAAC,QAAQ;6BACpE,OAAO,4BAA4B,EAAE,cAAc;4BACnD,OAAO,4BAA4B,EAAE,aAAa;4BAClD,OAAO,gBAAgB,EAAE,OAAO;wBAChC,OAAO,gBAAgB,EAAE,SAAS;;2BAElC;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE"}
@@ -1,3 +1,3 @@
1
- export type OverlayConfig = import('../types/OverlayConfig.js').OverlayConfig;
2
1
  export type ViewportPlacement = import('../types/OverlayConfig.js').ViewportPlacement;
2
+ export type OverlayConfig = import('../types/OverlayConfig.js').OverlayConfig;
3
3
  //# sourceMappingURL=OverlayController.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayController.test.d.ts","sourceRoot":"","sources":["../../../../components/overlays/test/OverlayController.test.js"],"names":[],"mappings":"4BAmBa,OAAO,2BAA2B,EAAE,aAAa;gCACjD,OAAO,2BAA2B,EAAE,iBAAiB"}
1
+ {"version":3,"file":"OverlayController.test.d.ts","sourceRoot":"","sources":["../../../../components/overlays/test/OverlayController.test.js"],"names":[],"mappings":"gCAoBa,OAAO,2BAA2B,EAAE,iBAAiB;4BACrD,OAAO,2BAA2B,EAAE,aAAa"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @param {HTMLElement} el
3
- * @param {{isAsync?:boolean, releaseElement?: HTMLElement}} config
3
+ * @param {{isAsync?:boolean, releaseElement?: HTMLElement}} config releaseElement can be different when the mouse is dragged before release
4
4
  */
5
5
  export function mimicClick(el: HTMLElement, { isAsync, releaseElement }?: {
6
6
  isAsync?: boolean;