@lion/ui 0.8.2 → 0.8.4

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 (77) hide show
  1. package/components/core/src/ScopedElementsMixin.js +3 -3
  2. package/components/overlays/src/OverlayController.js +40 -13
  3. package/components/overlays/test/OverlayController.test.js +233 -30
  4. package/components/overlays/test-helpers/mimicClick.js +2 -2
  5. package/custom-elements.json +32 -5
  6. package/dist-types/components/core/src/ScopedElementsMixin.d.ts +4 -4
  7. package/dist-types/components/form-integrations/test/helpers/helpers.d.ts +3 -3
  8. package/dist-types/components/form-integrations/test/helpers/helpers.d.ts.map +1 -1
  9. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
  10. package/dist-types/components/input-file/src/LionInputFile.d.ts +1 -1
  11. package/dist-types/components/input-file/src/LionSelectedFileList.d.ts +1 -1
  12. package/dist-types/components/overlays/src/OverlayController.d.ts +9 -5
  13. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  14. package/dist-types/components/overlays/test/OverlayController.test.d.ts +1 -1
  15. package/dist-types/components/overlays/test/OverlayController.test.d.ts.map +1 -1
  16. package/dist-types/components/overlays/test-helpers/mimicClick.d.ts +1 -1
  17. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +1 -1
  18. package/dist-types/components/switch/src/LionSwitch.d.ts +1 -1
  19. package/dist-types/exports/types/form-core.d.ts +1 -0
  20. package/dist-types/exports/types/form-core.d.ts.map +1 -1
  21. package/dist-types/tsconfig.tsbuildinfo +1 -1
  22. package/docs/components/checkbox-group/overview.md +2 -2
  23. package/docs/components/collapsible/overview.md +1 -1
  24. package/docs/components/combobox/overview.md +1 -1
  25. package/docs/components/combobox/use-cases.md +1 -1
  26. package/docs/components/dialog/overview.md +1 -1
  27. package/docs/components/fieldset/overview.md +5 -5
  28. package/docs/components/form/overview.md +3 -3
  29. package/docs/components/icon/use-cases.md +1 -1
  30. package/docs/components/input/overview.md +1 -1
  31. package/docs/components/input-amount/overview.md +3 -3
  32. package/docs/components/input-date/overview.md +3 -3
  33. package/docs/components/input-datepicker/overview.md +5 -5
  34. package/docs/components/input-email/overview.md +2 -2
  35. package/docs/components/input-file/overview.md +1 -1
  36. package/docs/components/input-iban/overview.md +2 -2
  37. package/docs/components/input-range/overview.md +2 -2
  38. package/docs/components/input-stepper/overview.md +1 -1
  39. package/docs/components/input-tel/overview.md +1 -1
  40. package/docs/components/input-tel/use-cases.md +6 -6
  41. package/docs/components/input-tel-dropdown/overview.md +1 -1
  42. package/docs/components/listbox/use-cases.md +1 -1
  43. package/docs/components/radio-group/overview.md +3 -3
  44. package/docs/components/select/use-cases.md +61 -1
  45. package/docs/components/select-rich/use-cases.md +1 -1
  46. package/docs/components/tabs/examples.md +1 -1
  47. package/docs/fundamentals/index.md +4 -4
  48. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  49. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  50. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  51. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  52. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  53. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  54. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  55. package/docs/fundamentals/rationales/accessibility.md +1 -1
  56. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  57. package/docs/fundamentals/systems/core/overview.md +1 -1
  58. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  59. package/docs/fundamentals/systems/form/model-value.md +1 -1
  60. package/docs/fundamentals/systems/form/overview.md +28 -28
  61. package/docs/fundamentals/systems/form/validate.md +2 -2
  62. package/docs/fundamentals/systems/localize/overview.md +4 -4
  63. package/docs/fundamentals/systems/overlays/api-table.md +1 -0
  64. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  65. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  66. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  67. package/docs/fundamentals/tools/ajax/overview.md +22 -1
  68. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  69. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  70. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  71. package/docs/guides/how-to/create-a-form.md +4 -4
  72. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  73. package/docs/guides/index.md +8 -8
  74. package/docs/guides/principles/index.md +5 -5
  75. package/docs/guides/principles/styling.md +1 -1
  76. package/exports/types/form-core.ts +1 -0
  77. package/package.json +1 -1
@@ -21,9 +21,9 @@ export function supportsScopedRegistry() {
21
21
  }
22
22
 
23
23
  /**
24
- * This file is combination of '@open-wc/scoped-elements@v3/lit-element.js' and '@open-wc/scoped-elements@v3/html-element.js'.
25
- * Then on top of those, some code from '@open-wc/scoped-elements@v2' is brought to to make polyfill not mandatory.
26
- * This can be a great help for ssr scenarios, allowing elements to be consumed without needing knowledge about internall
24
+ * This file is a combination of '@open-wc/scoped-elements@v3/lit-element.js' and '@open-wc/scoped-elements@v3/html-element.js'.
25
+ * To make the polyfill an opt-in, some code from '@open-wc/scoped-elements@v2' is added as well.
26
+ * This can be a great help for ssr scenarios, allowing elements to be consumed without needing knowledge about internal
27
27
  * consumption.
28
28
  * (N.B. at this point in time, this is limited to the scenario where there's one version of lion on the page).
29
29
  *
@@ -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",
@@ -142184,7 +142211,7 @@
142184
142211
  },
142185
142212
  {
142186
142213
  "kind": "mixin",
142187
- "description": "This file is combination of '@open-wc/scoped-elements@v3/lit-element.js' and '@open-wc/scoped-elements@v3/html-element.js'.\nThen on top of those, some code from '@open-wc/scoped-elements@v2' is brought to to make polyfill not mandatory.\nThis can be a great help for ssr scenarios, allowing elements to be consumed without needing knowledge about internall\nconsumption.\n(N.B. at this point in time, this is limited to the scenario where there's one version of lion on the page).\n\n## Considerations\nIn its current state, the [scoped-custom-element-registry](https://github.com/webcomponents/polyfills/tree/master/packages/scoped-custom-element-registry) draft spec has uncertainties:\n- the spec is not yet final; it's not clear how long it will be dependent on a polyfill\n- the polyfill conflicts with new browser functionality (form-associated custom elements in Safari, ShadowRoot.createElement in Chrome Canary, etc.).\n- the spsc is not compatible with SSR and it remains unclear if it will be in the future\n\nAlso see: https://github.com/webcomponents/polyfills/issues?q=scoped-custom-element-registry\n\nIn previous considerations (last year), we betted on the spec to evolve quickly and the polyfill to be stable.\nTill this day, little progress has been made. In the meantime.",
142214
+ "description": "This file is a combination of '@open-wc/scoped-elements@v3/lit-element.js' and '@open-wc/scoped-elements@v3/html-element.js'.\nTo make the polyfill an opt-in, some code from '@open-wc/scoped-elements@v2' is added as well.\nThis can be a great help for ssr scenarios, allowing elements to be consumed without needing knowledge about internal\nconsumption.\n(N.B. at this point in time, this is limited to the scenario where there's one version of lion on the page).\n\n## Considerations\nIn its current state, the [scoped-custom-element-registry](https://github.com/webcomponents/polyfills/tree/master/packages/scoped-custom-element-registry) draft spec has uncertainties:\n- the spec is not yet final; it's not clear how long it will be dependent on a polyfill\n- the polyfill conflicts with new browser functionality (form-associated custom elements in Safari, ShadowRoot.createElement in Chrome Canary, etc.).\n- the spsc is not compatible with SSR and it remains unclear if it will be in the future\n\nAlso see: https://github.com/webcomponents/polyfills/issues?q=scoped-custom-element-registry\n\nIn previous considerations (last year), we betted on the spec to evolve quickly and the polyfill to be stable.\nTill this day, little progress has been made. In the meantime.",
142188
142215
  "name": "ScopedElementsMixin",
142189
142216
  "members": [
142190
142217
  {
@@ -10,9 +10,9 @@
10
10
  */
11
11
  export function supportsScopedRegistry(): boolean;
12
12
  /**
13
- * This file is combination of '@open-wc/scoped-elements@v3/lit-element.js' and '@open-wc/scoped-elements@v3/html-element.js'.
14
- * Then on top of those, some code from '@open-wc/scoped-elements@v2' is brought to to make polyfill not mandatory.
15
- * This can be a great help for ssr scenarios, allowing elements to be consumed without needing knowledge about internall
13
+ * This file is a combination of '@open-wc/scoped-elements@v3/lit-element.js' and '@open-wc/scoped-elements@v3/html-element.js'.
14
+ * To make the polyfill an opt-in, some code from '@open-wc/scoped-elements@v2' is added as well.
15
+ * This can be a great help for ssr scenarios, allowing elements to be consumed without needing knowledge about internal
16
16
  * consumption.
17
17
  * (N.B. at this point in time, this is limited to the scenario where there's one version of lion on the page).
18
18
  *
@@ -40,7 +40,7 @@ export function supportsScopedRegistry(): boolean;
40
40
  * @param {T} superclass
41
41
  * @return {T & ScopedElementsHostConstructor & ScopedElementsHostV2Constructor}
42
42
  */
43
- export function ScopedElementsMixin<T extends import("@open-wc/dedupe-mixin").Constructor<import("lit").LitElement>>(superclass: T): T & import("@open-wc/dedupe-mixin").Constructor<import("../../../../../node_modules/@open-wc/scoped-elements/types.js").ScopedElementsHost> & import("./types.js").ScopedElementsHostV2Constructor;
43
+ export function ScopedElementsMixin<T extends import("@open-wc/dedupe-mixin").Constructor<import("lit").LitElement>>(superclass: T): T & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/lit-element.js").ScopedElementsHost> & import("./types.js").ScopedElementsHostV2Constructor;
44
44
  export type ScopedElementsMap = import('../../form-core/types/validate/ValidateMixinTypes.js').ScopedElementsMap;
45
45
  export type ScopedElementsHostConstructor = import('@open-wc/dedupe-mixin').Constructor<ScopedElementsHost>;
46
46
  export type ScopedElementsHost = import('@open-wc/scoped-elements/lit-element.js').ScopedElementsHost;
@@ -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"}
@@ -1,4 +1,4 @@
1
- declare const LionInputDatepicker_base: typeof LionInputDate & import("@open-wc/dedupe-mixin").Constructor<import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost> & Pick<typeof import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost, "prototype" | "localizeNamespaces" | "waitForLocalizeNamespaces"> & Pick<typeof import("lit").LitElement, typeof Symbol.metadata | "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "getPropertyOptions" | "shadowRootOptions"> & import("@open-wc/dedupe-mixin").Constructor<import("../../overlays/types/OverlayMixinTypes.js").OverlayHost> & Pick<typeof import("../../overlays/types/OverlayMixinTypes.js").OverlayHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../../overlays/types/ArrowMixinTypes.js").ArrowHost> & Pick<typeof import("../../overlays/types/ArrowMixinTypes.js").ArrowHost, "prototype" | "properties" | "styles"> & import("@open-wc/dedupe-mixin").Constructor<import("../../../../../node_modules/@open-wc/scoped-elements/types.js").ScopedElementsHost> & import("../../core/src/types.js").ScopedElementsHostV2Constructor;
1
+ declare const LionInputDatepicker_base: typeof LionInputDate & import("@open-wc/dedupe-mixin").Constructor<import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost> & Pick<typeof import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost, "prototype" | "localizeNamespaces" | "waitForLocalizeNamespaces"> & Pick<typeof import("lit").LitElement, typeof Symbol.metadata | "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "getPropertyOptions" | "shadowRootOptions"> & import("@open-wc/dedupe-mixin").Constructor<import("../../overlays/types/OverlayMixinTypes.js").OverlayHost> & Pick<typeof import("../../overlays/types/OverlayMixinTypes.js").OverlayHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../../overlays/types/ArrowMixinTypes.js").ArrowHost> & Pick<typeof import("../../overlays/types/ArrowMixinTypes.js").ArrowHost, "prototype" | "properties" | "styles"> & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/lit-element.js").ScopedElementsHost> & import("../../core/src/types.js").ScopedElementsHostV2Constructor;
2
2
  /**
3
3
  * @typedef {import('../../form-core/src/validate/Validator.js').Validator} Validator
4
4
  * @typedef {import('lit').RenderOptions} RenderOptions
@@ -1,4 +1,4 @@
1
- declare const LionInputFile_base: typeof LionField & import("@open-wc/dedupe-mixin").Constructor<import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost> & Pick<typeof import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost, "prototype" | "localizeNamespaces" | "waitForLocalizeNamespaces"> & Pick<typeof import("lit").LitElement, typeof Symbol.metadata | "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "getPropertyOptions" | "shadowRootOptions"> & import("@open-wc/dedupe-mixin").Constructor<import("../../../../../node_modules/@open-wc/scoped-elements/types.js").ScopedElementsHost> & import("../../core/src/types.js").ScopedElementsHostV2Constructor;
1
+ declare const LionInputFile_base: typeof LionField & import("@open-wc/dedupe-mixin").Constructor<import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost> & Pick<typeof import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost, "prototype" | "localizeNamespaces" | "waitForLocalizeNamespaces"> & Pick<typeof import("lit").LitElement, typeof Symbol.metadata | "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "getPropertyOptions" | "shadowRootOptions"> & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/lit-element.js").ScopedElementsHost> & import("../../core/src/types.js").ScopedElementsHostV2Constructor;
2
2
  export class LionInputFile extends LionInputFile_base {
3
3
  static get scopedElements(): {
4
4
  'lion-selected-file-list': typeof LionSelectedFileList;
@@ -1,4 +1,4 @@
1
- declare const LionSelectedFileList_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("../../../../../node_modules/@open-wc/scoped-elements/types.js").ScopedElementsHost> & import("../../core/src/types.js").ScopedElementsHostV2Constructor & import("@open-wc/dedupe-mixin").Constructor<import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost> & Pick<typeof import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost, "prototype" | "localizeNamespaces" | "waitForLocalizeNamespaces"> & Pick<typeof LitElement, typeof Symbol.metadata | "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "getPropertyOptions" | "shadowRootOptions">;
1
+ declare const LionSelectedFileList_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/lit-element.js").ScopedElementsHost> & import("../../core/src/types.js").ScopedElementsHostV2Constructor & import("@open-wc/dedupe-mixin").Constructor<import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost> & Pick<typeof import("../../localize/types/LocalizeMixinTypes.js").LocalizeMixinHost, "prototype" | "localizeNamespaces" | "waitForLocalizeNamespaces"> & Pick<typeof LitElement, typeof Symbol.metadata | "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "getPropertyOptions" | "shadowRootOptions">;
2
2
  /**
3
3
  * @typedef {import('lit').TemplateResult} TemplateResult
4
4
  * @typedef {import('../types/input-file.js').InputFile} InputFile