@lion/ui 0.0.9 → 0.0.11

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 (158) hide show
  1. package/components/accordion/src/LionAccordion.js +2 -2
  2. package/components/core/src/SlotMixin.js +2 -1
  3. package/components/dialog/src/LionDialog.js +0 -28
  4. package/components/dialog/test/lion-dialog.test.js +11 -10
  5. package/components/form-integrations/test/form-group-methods.test.js +6 -4
  6. package/components/form-integrations/test/form-integrations.test.js +10 -5
  7. package/components/form-integrations/test/helpers/umbrella-form.js +10 -0
  8. package/components/form-integrations/test/model-value-consistency.test.js +29 -20
  9. package/components/input-datepicker/src/LionInputDatepicker.js +66 -104
  10. package/components/input-datepicker/src/localizeNamespaceLoader.js +188 -0
  11. package/components/input-datepicker/test/lion-input-datepicker.test.js +15 -20
  12. package/components/input-datepicker/test-helpers/DatepickerInputObject.js +3 -3
  13. package/components/input-tel/src/parsers.js +1 -1
  14. package/components/listbox/test-suites/ListboxMixin.suite.js +3 -3
  15. package/components/localize/src/singleton.js +1 -1
  16. package/components/localize/test/number/formatNumber.test.js +2 -6
  17. package/components/overlays/src/OverlayController.js +292 -291
  18. package/components/overlays/src/OverlayMixin.js +17 -18
  19. package/components/overlays/src/OverlaysManager.js +28 -53
  20. package/components/overlays/src/configurations/visibility-trigger-partials/withClickInteraction.js +32 -0
  21. package/components/overlays/src/configurations/visibility-trigger-partials/withHoverInteraction.js +76 -0
  22. package/components/overlays/src/configurations/withBottomSheetConfig.js +3 -0
  23. package/components/overlays/src/configurations/withDropdownConfig.js +3 -0
  24. package/components/overlays/src/configurations/withModalDialogConfig.js +3 -0
  25. package/components/overlays/src/configurations/withTooltipConfig.js +29 -0
  26. package/components/overlays/src/overlayDocumentStyle.js +24 -0
  27. package/components/overlays/src/overlayShadowDomStyle.js +138 -0
  28. package/components/overlays/src/singleton.js +3 -1
  29. package/components/overlays/test/ArrowMixin.test.js +0 -1
  30. package/components/overlays/test/OverlayController.test.js +372 -274
  31. package/components/overlays/test/OverlaysManager.test.js +23 -32
  32. package/components/overlays/test/global-positioning.test.js +4 -15
  33. package/components/overlays/test/local-positioning.test.js +65 -38
  34. package/components/overlays/test-suites/OverlayMixin.suite.js +18 -26
  35. package/components/overlays/types/OverlayConfig.ts +38 -11
  36. package/components/overlays/types/OverlayMixinTypes.ts +2 -2
  37. package/components/select-rich/src/LionSelectInvoker.js +2 -3
  38. package/components/select-rich/src/LionSelectRich.js +22 -20
  39. package/components/select-rich/test/lion-select-rich.test.js +5 -7
  40. package/components/tooltip/src/LionTooltip.js +3 -90
  41. package/components/tooltip/test/lion-tooltip.test.js +52 -20
  42. package/custom-elements.json +535 -503
  43. package/dist-types/components/core/src/SlotMixin.d.ts.map +1 -1
  44. package/dist-types/components/dialog/src/LionDialog.d.ts +0 -2
  45. package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
  46. package/dist-types/components/form-integrations/test/helpers/umbrella-form.d.ts +4 -0
  47. package/dist-types/components/form-integrations/test/helpers/umbrella-form.d.ts.map +1 -1
  48. package/dist-types/components/form-integrations/test/model-value-consistency.test.d.ts.map +1 -1
  49. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +3 -3
  50. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts.map +1 -1
  51. package/dist-types/components/input-datepicker/src/localizeNamespaceLoader.d.ts +4 -0
  52. package/dist-types/components/input-datepicker/src/localizeNamespaceLoader.d.ts.map +1 -0
  53. package/dist-types/components/overlays/src/OverlayController.d.ts +56 -101
  54. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  55. package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
  56. package/dist-types/components/overlays/src/OverlaysManager.d.ts +0 -7
  57. package/dist-types/components/overlays/src/OverlaysManager.d.ts.map +1 -1
  58. package/dist-types/components/overlays/src/configurations/visibility-trigger-partials/withClickInteraction.d.ts +12 -0
  59. package/dist-types/components/overlays/src/configurations/visibility-trigger-partials/withClickInteraction.d.ts.map +1 -0
  60. package/dist-types/components/overlays/src/configurations/visibility-trigger-partials/withHoverInteraction.d.ts +18 -0
  61. package/dist-types/components/overlays/src/configurations/visibility-trigger-partials/withHoverInteraction.d.ts.map +1 -0
  62. package/dist-types/components/overlays/src/configurations/withBottomSheetConfig.d.ts.map +1 -1
  63. package/dist-types/components/overlays/src/configurations/withDropdownConfig.d.ts.map +1 -1
  64. package/dist-types/components/overlays/src/configurations/withModalDialogConfig.d.ts.map +1 -1
  65. package/dist-types/components/overlays/src/configurations/withTooltipConfig.d.ts +8 -0
  66. package/dist-types/components/overlays/src/configurations/withTooltipConfig.d.ts.map +1 -0
  67. package/dist-types/components/overlays/src/overlayDocumentStyle.d.ts +2 -0
  68. package/dist-types/components/overlays/src/overlayDocumentStyle.d.ts.map +1 -0
  69. package/dist-types/components/overlays/src/overlayShadowDomStyle.d.ts +2 -0
  70. package/dist-types/components/overlays/src/overlayShadowDomStyle.d.ts.map +1 -0
  71. package/dist-types/components/overlays/src/singleton.d.ts +2 -1
  72. package/dist-types/components/overlays/src/singleton.d.ts.map +1 -1
  73. package/dist-types/components/overlays/test-suites/OverlayMixin.suite.d.ts.map +1 -1
  74. package/dist-types/components/overlays/types/OverlayConfig.d.ts +19 -10
  75. package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
  76. package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +2 -2
  77. package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
  78. package/dist-types/components/select-rich/src/LionSelectInvoker.d.ts +3 -27
  79. package/dist-types/components/select-rich/src/LionSelectInvoker.d.ts.map +1 -1
  80. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  81. package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts.map +1 -1
  82. package/dist-types/components/tooltip/src/LionTooltip.d.ts +4 -20
  83. package/dist-types/components/tooltip/src/LionTooltip.d.ts.map +1 -1
  84. package/dist-types/components/tooltip/test/lion-tooltip.test.d.ts.map +1 -1
  85. package/dist-types/exports/input-datepicker.d.ts +0 -1
  86. package/dist-types/exports/overlays.d.ts +1 -1
  87. package/dist-types/exports/types/overlays.d.ts +1 -0
  88. package/dist-types/exports/types/overlays.d.ts.map +1 -1
  89. package/dist-types/tsconfig.tsbuildinfo +1 -1
  90. package/docs/components/checkbox-group/overview.md +2 -2
  91. package/docs/components/collapsible/overview.md +1 -1
  92. package/docs/components/combobox/overview.md +1 -1
  93. package/docs/components/combobox/use-cases.md +1 -1
  94. package/docs/components/dialog/overview.md +5 -9
  95. package/docs/components/dialog/use-cases.md +43 -87
  96. package/docs/components/fieldset/overview.md +5 -5
  97. package/docs/components/form/overview.md +3 -3
  98. package/docs/components/icon/use-cases.md +1 -1
  99. package/docs/components/input/overview.md +1 -1
  100. package/docs/components/input-amount/overview.md +3 -3
  101. package/docs/components/input-date/overview.md +3 -3
  102. package/docs/components/input-datepicker/overview.md +5 -5
  103. package/docs/components/input-email/overview.md +2 -2
  104. package/docs/components/input-iban/overview.md +2 -2
  105. package/docs/components/input-range/overview.md +2 -2
  106. package/docs/components/input-stepper/overview.md +1 -1
  107. package/docs/components/input-tel/overview.md +1 -1
  108. package/docs/components/input-tel/use-cases.md +6 -6
  109. package/docs/components/input-tel-dropdown/overview.md +1 -1
  110. package/docs/components/radio-group/overview.md +3 -3
  111. package/docs/components/select-rich/use-cases.md +1 -1
  112. package/docs/components/tabs/examples.md +1 -1
  113. package/docs/fundamentals/index.md +4 -4
  114. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  115. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  116. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  117. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  118. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  119. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  120. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  121. package/docs/fundamentals/systems/core/overview.md +1 -1
  122. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  123. package/docs/fundamentals/systems/form/model-value.md +1 -1
  124. package/docs/fundamentals/systems/form/overview.md +28 -28
  125. package/docs/fundamentals/systems/form/use-cases.md +4 -0
  126. package/docs/fundamentals/systems/form/validate.md +1 -1
  127. package/docs/fundamentals/systems/localize/overview.md +4 -4
  128. package/docs/fundamentals/systems/overlays/assets/{applyDemoOverlayStyles.js → applyDemoOverlayStyles.mjs} +2 -1
  129. package/docs/fundamentals/systems/overlays/assets/demo-el-using-overlaymixin.mjs +119 -0
  130. package/docs/fundamentals/systems/overlays/assets/{demo-overlay-backdrop.js → demo-overlay-backdrop.mjs} +3 -4
  131. package/docs/fundamentals/systems/overlays/assets/demo-overlay-positioning.mjs +537 -0
  132. package/docs/fundamentals/systems/overlays/assets/umbrella-form.js +9 -2
  133. package/docs/fundamentals/systems/overlays/configuration.md +56 -54
  134. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  135. package/docs/fundamentals/systems/overlays/use-cases.md +83 -77
  136. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  137. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  138. package/docs/guides/how-to/create-a-custom-field.md +6 -6
  139. package/docs/guides/how-to/extend-a-native-input.md +3 -3
  140. package/docs/guides/index.md +8 -8
  141. package/docs/guides/principles/index.md +5 -5
  142. package/docs/guides/principles/styling.md +1 -1
  143. package/exports/input-datepicker.js +0 -1
  144. package/exports/overlays.js +1 -1
  145. package/exports/types/overlays.ts +1 -0
  146. package/package.json +2 -1
  147. package/components/input-datepicker/src/LionCalendarOverlayFrame.js +0 -125
  148. package/components/overlays/src/globalOverlaysStyle.js +0 -139
  149. package/dist-types/components/input-datepicker/src/LionCalendarOverlayFrame.d.ts +0 -10
  150. package/dist-types/components/input-datepicker/src/LionCalendarOverlayFrame.d.ts.map +0 -1
  151. package/dist-types/components/overlays/src/globalOverlaysStyle.d.ts +0 -2
  152. package/dist-types/components/overlays/src/globalOverlaysStyle.d.ts.map +0 -1
  153. package/dist-types/exports/define/lion-calendar-overlay-frame.d.ts +0 -2
  154. package/dist-types/exports/define/lion-calendar-overlay-frame.d.ts.map +0 -1
  155. package/docs/fundamentals/systems/overlays/assets/demo-overlay-system.js +0 -42
  156. package/docs/fundamentals/systems/overlays/form-integration.md +0 -65
  157. package/docs/fundamentals/systems/overlays/scope.md +0 -176
  158. package/exports/define/lion-calendar-overlay-frame.js +0 -3
@@ -27,24 +27,25 @@ describe('OverlaysManager', () => {
27
27
  mngr.teardown();
28
28
  });
29
29
 
30
- it('provides .globalRootNode as a render target on first access', () => {
31
- expect(document.body.querySelectorAll('.global-overlays').length).to.equal(0);
32
- const rootNode = mngr.globalRootNode;
33
- expect(document.body.querySelector('.global-overlays')).to.equal(rootNode);
30
+ it('provides global stylesheet for arrangement of body scroll', () => {
31
+ expect(document.head.querySelectorAll('[data-overlays]').length).to.equal(1);
34
32
  });
35
33
 
36
34
  it('provides .teardown() for cleanup', () => {
37
- const rootNode = mngr.globalRootNode;
38
- expect(document.body.querySelector('.global-overlays')).to.equal(rootNode);
39
- expect(document.head.querySelector('[data-global-overlays=""]')).not.be.undefined;
35
+ expect(document.head.querySelector('[data-overlays=""]')).not.be.undefined;
40
36
 
41
37
  mngr.teardown();
42
- expect(document.body.querySelectorAll('.global-overlays').length).to.equal(0);
43
- expect(document.head.querySelector('[data-global-overlays=""]')).be.null;
38
+ expect(document.head.querySelector('[data-overlays=""]')).be.null;
44
39
 
45
40
  // safety check via private access (do not use this)
46
- expect(OverlaysManager.__globalRootNode).to.be.undefined;
47
41
  expect(OverlaysManager.__globalStyleNode).to.be.undefined;
42
+
43
+ // @ts-ignore [allow-private-in-test]
44
+ expect(mngr.__list).to.be.empty;
45
+ // @ts-ignore [allow-private-in-test]
46
+ expect(mngr.__shownList).to.be.empty;
47
+ // @ts-ignore [allow-private-in-test]
48
+ expect(mngr.__siblingsInert).to.be.false;
48
49
  });
49
50
 
50
51
  it('can add/remove controllers', () => {
@@ -121,22 +122,18 @@ describe('OverlaysManager', () => {
121
122
  });
122
123
 
123
124
  describe('When initialized with "preventsScroll: true"', () => {
124
- it('adds class "global-overlays-scroll-lock-ios-fix" to body and html on iOS', async () => {
125
+ it('adds class "overlays-scroll-lock-ios-fix" to body and html on iOS', async () => {
125
126
  mockIOS();
126
127
  const dialog = new OverlayController({ ...defaultOptions, preventsScroll: true }, mngr);
127
128
  await dialog.show();
128
- expect(Array.from(document.body.classList)).to.contain(
129
- 'global-overlays-scroll-lock-ios-fix',
130
- );
129
+ expect(Array.from(document.body.classList)).to.contain('overlays-scroll-lock-ios-fix');
131
130
  expect(Array.from(document.documentElement.classList)).to.contain(
132
- 'global-overlays-scroll-lock-ios-fix',
131
+ 'overlays-scroll-lock-ios-fix',
133
132
  );
134
133
  await dialog.hide();
135
- expect(Array.from(document.body.classList)).to.not.contain(
136
- 'global-overlays-scroll-lock-ios-fix',
137
- );
134
+ expect(Array.from(document.body.classList)).to.not.contain('overlays-scroll-lock-ios-fix');
138
135
  expect(Array.from(document.documentElement.classList)).to.not.contain(
139
- 'global-overlays-scroll-lock-ios-fix',
136
+ 'overlays-scroll-lock-ios-fix',
140
137
  );
141
138
 
142
139
  // When we are not iOS nor MacSafari
@@ -145,30 +142,24 @@ describe('OverlaysManager', () => {
145
142
 
146
143
  const dialog2 = new OverlayController({ ...defaultOptions, preventsScroll: true }, mngr);
147
144
  await dialog2.show();
148
- expect(Array.from(document.body.classList)).to.not.contain(
149
- 'global-overlays-scroll-lock-ios-fix',
150
- );
145
+ expect(Array.from(document.body.classList)).to.not.contain('overlays-scroll-lock-ios-fix');
151
146
  expect(Array.from(document.documentElement.classList)).to.not.contain(
152
- 'global-overlays-scroll-lock-ios-fix',
147
+ 'overlays-scroll-lock-ios-fix',
153
148
  );
154
149
  });
155
150
 
156
- it('adds class "global-overlays-scroll-lock-ios-fix" to body on MacSafari', async () => {
151
+ it('adds class "overlays-scroll-lock-ios-fix" to body on MacSafari', async () => {
157
152
  mockMacSafari();
158
153
  const dialog = new OverlayController({ ...defaultOptions, preventsScroll: true }, mngr);
159
154
  await dialog.show();
160
- expect(Array.from(document.body.classList)).to.contain(
161
- 'global-overlays-scroll-lock-ios-fix',
162
- );
155
+ expect(Array.from(document.body.classList)).to.contain('overlays-scroll-lock-ios-fix');
163
156
  expect(Array.from(document.documentElement.classList)).to.not.contain(
164
- 'global-overlays-scroll-lock-ios-fix',
157
+ 'overlays-scroll-lock-ios-fix',
165
158
  );
166
159
  await dialog.hide();
167
- expect(Array.from(document.body.classList)).to.not.contain(
168
- 'global-overlays-scroll-lock-ios-fix',
169
- );
160
+ expect(Array.from(document.body.classList)).to.not.contain('overlays-scroll-lock-ios-fix');
170
161
  expect(Array.from(document.documentElement.classList)).to.not.contain(
171
- 'global-overlays-scroll-lock-ios-fix',
162
+ 'overlays-scroll-lock-ios-fix',
172
163
  );
173
164
  });
174
165
  });
@@ -18,25 +18,14 @@ describe('Global Positioning', () => {
18
18
  overlays.teardown();
19
19
  });
20
20
 
21
- describe('Basics', () => {
22
- it('puts ".contentNode" in the body of the page', async () => {
23
- const ctrl = new OverlayController({
24
- ...withDefaultGlobalConfig(),
25
- });
26
- await ctrl.show();
27
- expect(overlays.globalRootNode.children.length).to.equal(1);
28
- expect(overlays.globalRootNode.children[0]).to.have.trimmed.text('my content');
29
- });
30
- });
31
-
32
21
  describe('viewportConfig', () => {
33
22
  it('positions the overlay in center by default', async () => {
34
23
  const ctrl = new OverlayController({
35
24
  ...withDefaultGlobalConfig(),
36
25
  });
37
26
  await ctrl.show();
38
- expect(ctrl.content.classList.contains('global-overlays__overlay-container--center')).to.be
39
- .true;
27
+ expect(ctrl.contentWrapperNode.classList.contains('overlays__overlay-container--center')).to
28
+ .be.true;
40
29
  });
41
30
 
42
31
  it('positions relative to the viewport ', async () => {
@@ -60,8 +49,8 @@ describe('Global Positioning', () => {
60
49
  });
61
50
  await ctrl.show();
62
51
  expect(
63
- ctrl.content.classList.contains(
64
- `global-overlays__overlay-container--${viewportPlacement}`,
52
+ ctrl.contentWrapperNode.classList.contains(
53
+ `overlays__overlay-container--${viewportPlacement}`,
65
54
  ),
66
55
  ).to.be.true;
67
56
  });
@@ -9,6 +9,39 @@ import { normalizeTransformStyle } from './utils-tests/local-positioning-helpers
9
9
  * @typedef {import('../types/OverlayConfig.js').ViewportPlacement} ViewportPlacement
10
10
  */
11
11
 
12
+ /**
13
+ * Make sure we never use a native button element, since its dimensions
14
+ * are not cross browser consistent
15
+ * For debugging purposes, add colors...
16
+ * @param {{clickHandler?: function; width?: number; height?: number}} opts
17
+ */
18
+ function createInvokerSync({ clickHandler = () => {}, width = 100, height = 20 }) {
19
+ return /** @type {HTMLDivElement} */ (
20
+ fixtureSync(html`
21
+ <div
22
+ role="button"
23
+ style="width: ${width}px; height: ${height}px; background: red; color: white;"
24
+ @click=${clickHandler}
25
+ >
26
+ Invoker
27
+ </div>
28
+ `)
29
+ );
30
+ }
31
+
32
+ /**
33
+ * @param {{ width?: number; height?: number }} opts
34
+ */
35
+ function createContentSync({ width = 80, height = 20 }) {
36
+ return /** @type {HTMLDivElement} */ (
37
+ fixtureSync(html`
38
+ <div style="width: ${width}px; height: ${height}px; background: green; color: white;">
39
+ Content
40
+ </div>
41
+ `)
42
+ );
43
+ }
44
+
12
45
  const withLocalTestConfig = () =>
13
46
  /** @type {OverlayConfig} */ ({
14
47
  placementMode: 'local',
@@ -47,14 +80,13 @@ describe('Local Positioning', () => {
47
80
  });
48
81
  await fixture(html`
49
82
  <div style="position: fixed; left: 100px; top: 100px;">
50
- ${ctrl.invokerNode}${ctrl.content}
83
+ ${ctrl.invokerNode}${ctrl.__wrappingDialogNode}
51
84
  </div>
52
85
  `);
53
86
  await ctrl.show();
54
87
 
55
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
56
- 'translate(-30px, -18px)',
57
- 'translate should be -30px [to center = (80 - 20)/2*-1], -18px [to place above = 10 invoker height + 8 default padding]',
88
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
89
+ 'translate(70px, -508px)',
58
90
  );
59
91
  });
60
92
 
@@ -76,11 +108,11 @@ describe('Local Positioning', () => {
76
108
  });
77
109
  await fixture(html`
78
110
  <div style="position: fixed; left: 100px; top: 100px;">
79
- ${ctrl.invokerNode}${ctrl.content}
111
+ ${ctrl.invokerNode}${ctrl.contentWrapperNode}
80
112
  </div>
81
113
  `);
82
114
  await ctrl.show();
83
- expect(ctrl.content.getAttribute('data-popper-placement')).to.equal('top');
115
+ expect(ctrl.contentWrapperNode.getAttribute('data-popper-placement')).to.equal('top');
84
116
  });
85
117
 
86
118
  it('positions to preferred place if placement is set and space is available', async () => {
@@ -104,12 +136,12 @@ describe('Local Positioning', () => {
104
136
  });
105
137
  await fixture(html`
106
138
  <div style="position: absolute; left: 120px; top: 50px;">
107
- ${ctrl.invokerNode}${ctrl.content}
139
+ ${ctrl.invokerNode}${ctrl.contentWrapperNode}
108
140
  </div>
109
141
  `);
110
142
 
111
143
  await ctrl.show();
112
- expect(ctrl.content.getAttribute('data-popper-placement')).to.equal('left-start');
144
+ expect(ctrl.contentWrapperNode.getAttribute('data-popper-placement')).to.equal('left-start');
113
145
  });
114
146
 
115
147
  it('positions to different place if placement is set and no space is available', async () => {
@@ -130,11 +162,13 @@ describe('Local Positioning', () => {
130
162
  },
131
163
  });
132
164
  await fixture(html`
133
- <div style="position: absolute; top: 50px;">${ctrl.invokerNode}${ctrl.content}</div>
165
+ <div style="position: absolute; top: 50px;">
166
+ ${ctrl.invokerNode}${ctrl.contentWrapperNode}
167
+ </div>
134
168
  `);
135
169
 
136
170
  await ctrl.show();
137
- expect(ctrl.content.getAttribute('data-popper-placement')).to.equal('right');
171
+ expect(ctrl.contentWrapperNode.getAttribute('data-popper-placement')).to.equal('right');
138
172
  });
139
173
 
140
174
  it('allows the user to override default Popper modifiers', async () => {
@@ -164,7 +198,7 @@ describe('Local Positioning', () => {
164
198
  });
165
199
  await fixture(html`
166
200
  <div style="position: absolute; left: 100px; top: 50px;">
167
- ${ctrl.invokerNode}${ctrl.content}
201
+ ${ctrl.invokerNode}${ctrl.contentWrapperNode}
168
202
  </div>
169
203
  `);
170
204
 
@@ -175,37 +209,30 @@ describe('Local Positioning', () => {
175
209
  it('positions the Popper element correctly on show', async () => {
176
210
  const ctrl = new OverlayController({
177
211
  ...withLocalTestConfig(),
178
- contentNode: /** @type {HTMLElement} */ (
179
- fixtureSync(html` <div style="width: 80px; height: 20px;"></div> `)
180
- ),
181
- invokerNode: /** @type {HTMLElement} */ (
182
- fixtureSync(html`
183
- <div
184
- role="button"
185
- style="width: 100px; height: 20px;"
186
- @click=${() => ctrl.show()}
187
- ></div>
188
- `)
189
- ),
212
+ contentNode: createContentSync({ width: 80, height: 20 }),
213
+ invokerNode: createInvokerSync({ clickHandler: () => ctrl.show(), width: 100, height: 20 }),
190
214
  popperConfig: {
191
215
  placement: 'top',
192
216
  },
193
217
  });
194
218
  await fixture(html`
195
219
  <div style="position: absolute; top: 300px; left: 100px;">
196
- ${ctrl.invokerNode}${ctrl.content}
220
+ ${ctrl.invokerNode}${ctrl.__wrappingDialogNode}
197
221
  </div>
198
222
  `);
223
+
199
224
  await ctrl.show();
200
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
201
- 'translate(10px, -28px)',
202
- 'Popper positioning values',
225
+
226
+ // N.B. margin between invoker and content = 8px
227
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
228
+ 'translate(110px, -308px)',
229
+ '110 = (100 + (100-80)/2); -308= 300 + 8',
203
230
  );
204
231
 
205
232
  await ctrl.hide();
206
233
  await ctrl.show();
207
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
208
- 'translate(10px, -28px)',
234
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
235
+ 'translate(110px, -308px)',
209
236
  'Popper positioning values should be identical after hiding and showing',
210
237
  );
211
238
  });
@@ -241,12 +268,12 @@ describe('Local Positioning', () => {
241
268
  });
242
269
  await fixture(html`
243
270
  <div style="position: absolute; top: 300px; left: 100px;">
244
- ${ctrl.invokerNode} ${ctrl.content}
271
+ ${ctrl.invokerNode} ${ctrl.contentWrapperNode}
245
272
  </div>
246
273
  `);
247
274
 
248
275
  await ctrl.show();
249
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
276
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
250
277
  'translate3d(10px, -30px, 0px)',
251
278
  'Popper positioning values',
252
279
  );
@@ -267,7 +294,7 @@ describe('Local Positioning', () => {
267
294
  });
268
295
  await ctrl.show();
269
296
  expect(ctrl._popper.options.modifiers.offset.offset).to.equal('0, 20px');
270
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
297
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
271
298
  'translate3d(10px, -40px, 0px)',
272
299
  'Popper positioning Y value should be 10 less than previous, due to the added extra 10px offset',
273
300
  );
@@ -302,12 +329,12 @@ describe('Local Positioning', () => {
302
329
  });
303
330
  await fixture(html`
304
331
  <div style="position: absolute; top: 300px; left: 100px;">
305
- ${ctrl.invokerNode} ${ctrl.content}
332
+ ${ctrl.invokerNode} ${ctrl.contentWrapperNode}
306
333
  </div>
307
334
  `);
308
335
 
309
336
  await ctrl.show();
310
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
337
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
311
338
  'translate3d(10px, -30px, 0px)',
312
339
  'Popper positioning values',
313
340
  );
@@ -317,7 +344,7 @@ describe('Local Positioning', () => {
317
344
  modifiers: [{ name: 'offset', enabled: true, options: { offset: [0, 20] } }],
318
345
  },
319
346
  });
320
- expect(normalizeTransformStyle(ctrl.content.style.transform)).to.equal(
347
+ expect(normalizeTransformStyle(ctrl.contentWrapperNode.style.transform)).to.equal(
321
348
  'translate3d(10px, -40px, 0px)',
322
349
  'Popper positioning Y value should be 10 less than previous, due to the added extra 10px offset',
323
350
  );
@@ -333,7 +360,7 @@ describe('Local Positioning', () => {
333
360
  invokerNode,
334
361
  });
335
362
  await ctrl.show();
336
- expect(ctrl.content.style.minWidth).to.equal('60px');
363
+ expect(ctrl.contentWrapperNode.style.minWidth).to.equal('60px');
337
364
  });
338
365
 
339
366
  it('can set the contentNode maxWidth as the invokerNode width', async () => {
@@ -346,7 +373,7 @@ describe('Local Positioning', () => {
346
373
  invokerNode,
347
374
  });
348
375
  await ctrl.show();
349
- expect(ctrl.content.style.maxWidth).to.equal('60px');
376
+ expect(ctrl.contentWrapperNode.style.maxWidth).to.equal('60px');
350
377
  });
351
378
 
352
379
  it('can set the contentNode width as the invokerNode width', async () => {
@@ -359,7 +386,7 @@ describe('Local Positioning', () => {
359
386
  invokerNode,
360
387
  });
361
388
  await ctrl.show();
362
- expect(ctrl.content.style.width).to.equal('60px');
389
+ expect(ctrl.contentWrapperNode.style.width).to.equal('60px');
363
390
  });
364
391
  });
365
392
  });
@@ -1,7 +1,7 @@
1
1
  import { expect, fixture, html, nextFrame, aTimeout } from '@open-wc/testing';
2
2
 
3
3
  import sinon from 'sinon';
4
- import { overlays, OverlayController } from '@lion/ui/overlays.js';
4
+ import { overlays as overlaysManager, OverlayController } from '@lion/ui/overlays.js';
5
5
 
6
6
  import '@lion/ui/define/lion-dialog.js';
7
7
  import { _browserDetection } from '../src/OverlaysManager.js';
@@ -15,10 +15,12 @@ import { _browserDetection } from '../src/OverlaysManager.js';
15
15
  * @typedef {LitElement & OverlayHost & {_overlayCtrl:OverlayController}} OverlayEl
16
16
  */
17
17
 
18
- function getGlobalOverlayNodes() {
19
- return Array.from(overlays.globalRootNode.children).filter(
20
- child => !child.classList.contains('global-overlays__backdrop'),
21
- );
18
+ function getGlobalOverlayCtrls() {
19
+ return overlaysManager.list;
20
+ }
21
+
22
+ function resetOverlaysManager() {
23
+ overlaysManager.list.forEach(overlayCtrl => overlaysManager.remove(overlayCtrl));
22
24
  }
23
25
 
24
26
  /**
@@ -94,7 +96,7 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
94
96
  )
95
97
  );
96
98
 
97
- // For now, we skip this test for MacSafari, since the body.global-overlays-scroll-lock-ios-fix
99
+ // For now, we skip this test for MacSafari, since the body.overlays-scroll-lock-ios-fix
98
100
  // class results in a scrollbar when preventsScroll is true.
99
101
  // However, fully functioning interacive elements (input fields) in the dialog are more important
100
102
  if (_browserDetection.isMacSafari && elWithBigParent._overlayCtrl.preventsScroll) {
@@ -220,23 +222,21 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
220
222
  function sendCloseEvent(/** @type {Event} */ e) {
221
223
  e.target?.dispatchEvent(new Event('close-overlay', { bubbles: true }));
222
224
  }
223
- const closeBtn = /** @type {OverlayEl} */ (
224
- await fixture(html` <button @click=${sendCloseEvent}>close</button> `)
225
- );
226
225
 
227
226
  const el = /** @type {OverlayEl} */ (
228
227
  await fixture(html`
229
228
  <${tag} opened>
230
229
  <div slot="content">
231
230
  content of the overlay
232
- ${closeBtn}
231
+ <button @click=${sendCloseEvent}>close</button>
233
232
  </div>
234
233
  <button slot="invoker">invoker button</button>
235
234
  </${tag}>
236
235
  `)
237
236
  );
238
- closeBtn.click();
239
- await nextFrame(); // hide takes at least a frame
237
+ // @ts-ignore
238
+ el.querySelector('[slot=content] button').click();
239
+ await el._overlayCtrl._hideComplete;
240
240
  expect(el.opened).to.be.false;
241
241
  });
242
242
 
@@ -337,10 +337,7 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
337
337
  // For some reason, globalRootNode is not cleared properly on disconnectedCallback from previous overlay test fixtures...
338
338
  // Not sure why this "bug" happens...
339
339
  beforeEach(() => {
340
- const globalRootNode = document.querySelector('.global-overlays');
341
- if (globalRootNode) {
342
- globalRootNode.innerHTML = '';
343
- }
340
+ resetOverlaysManager();
344
341
  });
345
342
 
346
343
  it('supports nested overlays', async () => {
@@ -362,7 +359,7 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
362
359
  );
363
360
 
364
361
  if (el._overlayCtrl.placementMode === 'global') {
365
- expect(getGlobalOverlayNodes().length).to.equal(2);
362
+ expect(getGlobalOverlayCtrls().length).to.equal(2);
366
363
  }
367
364
 
368
365
  el.opened = true;
@@ -386,12 +383,12 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
386
383
  `)
387
384
  );
388
385
  if (el._overlayCtrl.placementMode === 'global') {
389
- expect(getGlobalOverlayNodes().length).to.equal(1);
386
+ expect(getGlobalOverlayCtrls().length).to.equal(1);
390
387
 
391
388
  const moveTarget = /** @type {OverlayEl} */ (await fixture('<div id="target"></div>'));
392
389
  moveTarget.appendChild(el);
393
390
  await el.updateComplete;
394
- expect(getGlobalOverlayNodes().length).to.equal(1);
391
+ expect(getGlobalOverlayCtrls().length).to.equal(1);
395
392
  }
396
393
  });
397
394
 
@@ -419,14 +416,9 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
419
416
 
420
417
  if (el._overlayCtrl.placementMode === 'global') {
421
418
  // Find the outlets that are not backdrop outlets
422
- const overlayContainerNodes = getGlobalOverlayNodes();
423
- expect(overlayContainerNodes.length).to.equal(2);
424
- const lastContentNodeInContainer = overlayContainerNodes[1];
419
+ expect(getGlobalOverlayCtrls().length).to.equal(2);
425
420
  // Check that the last container is the nested one with the intended content
426
- expect(lastContentNodeInContainer.firstElementChild.firstChild.textContent).to.equal(
427
- 'content of the nested overlay',
428
- );
429
- expect(lastContentNodeInContainer.firstElementChild.slot).to.equal('content');
421
+ expect(el.contains(nestedEl)).to.be.true;
430
422
  } else {
431
423
  const contentNode = /** @type {HTMLElement} */ (
432
424
  // @ts-ignore [allow-protected] in tests
@@ -1,8 +1,23 @@
1
1
  import { Options } from '@popperjs/core';
2
2
 
3
3
  export interface OverlayConfig {
4
- /** Determines the connection point in DOM (body vs next to invoker). */
4
+ // Positioning
5
+
6
+ /** Determines the positioning anchore (viewport vs invokerNode/referenceNode). */
5
7
  placementMode?: 'global' | 'local' | undefined;
8
+ /** Popper configuration. Will be used when placementMode is 'local' */
9
+ popperConfig?: Partial<Options>;
10
+ /** Viewport positioning configuration. Will be used when placementMode is 'global' */
11
+ viewportConfig?: ViewportConfig;
12
+ /** Hides other overlays when multiple are opened (currently exclusive to globalOverlayController) */
13
+ isBlocking?: boolean;
14
+ /** Will align contentNode with referenceNode (invokerNode by default) for local overlays. Usually needed for dropdowns. 'max' will prevent contentNode from exceeding width of referenceNode, 'min' guarantees that contentNode will be at least as wide as referenceNode. 'full' will make sure that the invoker width always is the same. */
15
+ inheritsReferenceWidth?: 'max' | 'full' | 'min' | 'none';
16
+ /** Change the default of 9999 */
17
+ zIndex?: number;
18
+
19
+ // Elements
20
+
6
21
  /** The interactive element (usually a button) invoking the dialog or tooltip */
7
22
  invokerNode?: HTMLElement;
8
23
  /** The element that is used to position the overlay content relative to. Usually, this is the same element as invokerNode. Should only be provided when invokerNode should not be positioned against */
@@ -15,10 +30,14 @@ export interface OverlayConfig {
15
30
  backdropNode?: HTMLElement;
16
31
  /** The element that should be called `.focus()` on after dialog closes */
17
32
  elementToFocusAfterHide?: HTMLElement;
33
+
34
+ // Backdrop
35
+
18
36
  /** Whether it should have a backdrop (currently exclusive to globalOverlayController) */
19
37
  hasBackdrop?: boolean;
20
- /** Hides other overlays when mutiple are opened (currently exclusive to globalOverlayController) */
21
- isBlocking?: boolean;
38
+
39
+ // User interaction
40
+
22
41
  /** Prevents scrolling body content when overlay opened (currently exclusive to globalOverlayController) */
23
42
  preventsScroll?: boolean;
24
43
  /** Rotates tab, implicitly set when 'isModal' */
@@ -29,8 +48,9 @@ export interface OverlayConfig {
29
48
  hidesOnOutsideClick?: boolean;
30
49
  /** Hides the overlay when pressing esc, even when contentNode has no focus */
31
50
  hidesOnOutsideEsc?: boolean;
32
- /** Will align contentNode with referenceNode (invokerNode by default) for local overlays. Usually needed for dropdowns. 'max' will prevent contentNode from exceeding width of referenceNode, 'min' guarantees that contentNode will be at least as wide as referenceNode. 'full' will make sure that the invoker width always is the same. */
33
- inheritsReferenceWidth?: 'max' | 'full' | 'min' | 'none';
51
+
52
+ // Accessibility
53
+
34
54
  /**
35
55
  * For non `isTooltip`:
36
56
  * - sets aria-expanded="true/false" and aria-haspopup="true" on invokerNode
@@ -42,14 +62,22 @@ export interface OverlayConfig {
42
62
  * - sets role="tooltip" and aria-labelledby/aria-describedby on the content
43
63
  */
44
64
  handlesAccessibility?: boolean;
65
+
66
+ // Tooltip
67
+
45
68
  /** Has a totally different interaction- and accessibility pattern from all other overlays. Will behave as role="tooltip" element instead of a role="dialog" element */
46
69
  isTooltip?: boolean;
47
70
  /** By default, the tooltip content is a 'description' for the invoker (uses aria-describedby) Setting this property to 'label' makes the content function as a label (via aria-labelledby) */
48
71
  invokerRelation?: 'label' | 'description';
49
- /** Popper configuration. Will be used when placementMode is 'local' */
50
- popperConfig?: Partial<Options>;
51
- /** Viewport configuration. Will be used when placementMode is 'global' */
52
- viewportConfig?: ViewportConfig;
72
+
73
+ /** render a div instead of dialog */
74
+ _noDialogEl?: Boolean;
75
+
76
+ /**
77
+ * Determines the conditions hiding/showing should be based on. It gets the OverlayController as input and returns an object with
78
+ * functions with Overlay phases as keys
79
+ */
80
+ visibilityTriggerFunction?: Function;
53
81
  }
54
82
 
55
83
  export type ViewportPlacement =
@@ -61,8 +89,7 @@ export type ViewportPlacement =
61
89
  | 'bottom-right'
62
90
  | 'bottom'
63
91
  | 'bottom-left'
64
- | 'left'
65
- | 'center';
92
+ | 'left';
66
93
 
67
94
  export interface ViewportConfig {
68
95
  placement: ViewportPlacement;
@@ -18,8 +18,8 @@ export interface DefineOverlayConfig {
18
18
 
19
19
  export declare class OverlayHost {
20
20
  opened: Boolean;
21
- get config(): OverlayConfig;
22
- set config(value: OverlayConfig);
21
+ get config(): Partial<OverlayConfig>;
22
+ set config(value: Partial<OverlayConfig>);
23
23
 
24
24
  open(): Promise<void>;
25
25
  close(): Promise<void>;
@@ -15,8 +15,7 @@ import { SlotMixin } from '@lion/ui/core.js';
15
15
  export class LionSelectInvoker extends SlotMixin(LionButton) {
16
16
  static get styles() {
17
17
  return [
18
- // TODO switch back to ...super.styles once fixed https://github.com/lit/lit.dev/pull/535
19
- ...LionButton.styles,
18
+ ...super.styles,
20
19
  css`
21
20
  :host {
22
21
  justify-content: space-between;
@@ -31,9 +30,9 @@ export class LionSelectInvoker extends SlotMixin(LionButton) {
31
30
  ];
32
31
  }
33
32
 
33
+ /** @type {any} */
34
34
  static get properties() {
35
35
  return {
36
- ...super.properties,
37
36
  selectedElement: { type: Object },
38
37
  hostElement: { type: Object },
39
38
  readOnly: { type: Boolean, reflect: true, attribute: 'readonly' },
@@ -408,27 +408,29 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
408
408
  * @protected
409
409
  */
410
410
  async _alignInvokerWidth() {
411
- if (this._overlayCtrl && this._overlayCtrl.content) {
412
- await this.updateComplete;
413
- const initContentDisplay = this._overlayCtrl.content.style.display;
414
- const initContentMinWidth = this._overlayCtrl.content.style.minWidth;
415
- const initContentWidth = this._overlayCtrl.content.style.width;
416
- this._overlayCtrl.content.style.display = '';
417
- this._overlayCtrl.content.style.minWidth = 'auto';
418
- this._overlayCtrl.content.style.width = 'auto';
419
- const contentWidth = this._overlayCtrl.content.getBoundingClientRect().width;
420
- /**
421
- * TODO when inside an overlay the current solution doesn't work.
422
- * Since that dialog is still hidden, open and close the select-rich
423
- * doesn't have any effect so the contentWidth returns 0
424
- */
425
- if (contentWidth > 0) {
426
- this._invokerNode.style.width = `${contentWidth + this._arrowWidth}px`;
427
- }
428
- this._overlayCtrl.content.style.display = initContentDisplay;
429
- this._overlayCtrl.content.style.minWidth = initContentMinWidth;
430
- this._overlayCtrl.content.style.width = initContentWidth;
411
+ if (!this._overlayCtrl?.content) {
412
+ return;
413
+ }
414
+
415
+ await this.updateComplete;
416
+ const initContentDisplay = this._overlayCtrl.content.style.display;
417
+ const initContentMinWidth = this._overlayCtrl.contentWrapperNode.style.minWidth;
418
+ const initContentWidth = this._overlayCtrl.contentWrapperNode.style.width;
419
+ this._overlayCtrl.content.style.display = '';
420
+ this._overlayCtrl.contentWrapperNode.style.minWidth = 'auto';
421
+ this._overlayCtrl.contentWrapperNode.style.width = 'auto';
422
+ const contentWidth = this._overlayCtrl.contentWrapperNode.getBoundingClientRect().width;
423
+ /**
424
+ * TODO when inside an overlay the current solution doesn't work.
425
+ * Since that dialog is still hidden, open and close the select-rich
426
+ * doesn't have any effect so the contentWidth returns 0
427
+ */
428
+ if (contentWidth > 0) {
429
+ this._invokerNode.style.width = `${contentWidth + this._arrowWidth}px`;
431
430
  }
431
+ this._overlayCtrl.content.style.display = initContentDisplay;
432
+ this._overlayCtrl.contentWrapperNode.style.minWidth = initContentMinWidth;
433
+ this._overlayCtrl.contentWrapperNode.style.width = initContentWidth;
432
434
  }
433
435
 
434
436
  /**