@lion/ui 0.0.11 → 0.0.13

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 (160) hide show
  1. package/components/accordion/src/LionAccordion.js +22 -9
  2. package/components/accordion/test/lion-accordion.test.js +29 -32
  3. package/components/input-tel/translations/bg.js +3 -1
  4. package/components/input-tel/translations/cs.js +3 -1
  5. package/components/input-tel/translations/de.js +3 -1
  6. package/components/input-tel/translations/en.js +3 -1
  7. package/components/input-tel/translations/es.js +3 -1
  8. package/components/input-tel/translations/fr.js +3 -1
  9. package/components/input-tel/translations/hu.js +3 -1
  10. package/components/input-tel/translations/it.js +3 -1
  11. package/components/input-tel/translations/nl.js +3 -1
  12. package/components/input-tel/translations/pl.js +3 -1
  13. package/components/input-tel/translations/ro.js +3 -1
  14. package/components/input-tel/translations/ru.js +3 -1
  15. package/components/input-tel/translations/sk.js +3 -1
  16. package/components/input-tel/translations/uk.js +3 -1
  17. package/components/input-tel/translations/zh.js +3 -1
  18. package/components/input-tel-dropdown/src/LionInputTelDropdown.js +5 -5
  19. package/components/listbox/src/ListboxMixin.js +0 -4
  20. package/components/listbox/types/ListboxMixinTypes.ts +1 -4
  21. package/components/overlays/src/OverlayController.js +28 -34
  22. package/components/overlays/src/utils/adopt-styles.js +136 -0
  23. package/components/overlays/test/OverlayController.test.js +24 -55
  24. package/components/overlays/test/local-positioning.test.js +1 -1
  25. package/components/overlays/test/utils-tests/adopt-styles.test.js +177 -0
  26. package/components/overlays/test-helpers/createShadowHost.js +16 -0
  27. package/components/overlays/{test/utils-tests/local-positioning-helpers.js → test-helpers/normalizeTransformStyle.js} +0 -0
  28. package/components/select-rich/src/LionSelectRich.js +14 -6
  29. package/components/select-rich/test/lion-select-rich.test.js +25 -1
  30. package/components/tooltip/src/LionTooltip.js +16 -2
  31. package/components/tooltip/test/lion-tooltip.test.js +1 -2
  32. package/custom-elements.json +432 -183
  33. package/dist-types/components/accordion/src/LionAccordion.d.ts.map +1 -1
  34. package/dist-types/components/input-tel/translations/bg-BG.d.ts +3 -1
  35. package/dist-types/components/input-tel/translations/bg.d.ts +3 -1
  36. package/dist-types/components/input-tel/translations/cs-CZ.d.ts +3 -1
  37. package/dist-types/components/input-tel/translations/cs.d.ts +3 -1
  38. package/dist-types/components/input-tel/translations/de-DE.d.ts +3 -1
  39. package/dist-types/components/input-tel/translations/de.d.ts +3 -1
  40. package/dist-types/components/input-tel/translations/en-AU.d.ts +3 -1
  41. package/dist-types/components/input-tel/translations/en-GB.d.ts +3 -1
  42. package/dist-types/components/input-tel/translations/en-US.d.ts +3 -1
  43. package/dist-types/components/input-tel/translations/en.d.ts +3 -1
  44. package/dist-types/components/input-tel/translations/es-ES.d.ts +3 -1
  45. package/dist-types/components/input-tel/translations/es.d.ts +3 -1
  46. package/dist-types/components/input-tel/translations/fr-BE.d.ts +3 -1
  47. package/dist-types/components/input-tel/translations/fr-FR.d.ts +3 -1
  48. package/dist-types/components/input-tel/translations/fr.d.ts +3 -1
  49. package/dist-types/components/input-tel/translations/hu-HU.d.ts +3 -1
  50. package/dist-types/components/input-tel/translations/hu.d.ts +3 -1
  51. package/dist-types/components/input-tel/translations/it-IT.d.ts +3 -1
  52. package/dist-types/components/input-tel/translations/it.d.ts +3 -1
  53. package/dist-types/components/input-tel/translations/nl-BE.d.ts +3 -1
  54. package/dist-types/components/input-tel/translations/nl-NL.d.ts +3 -1
  55. package/dist-types/components/input-tel/translations/nl.d.ts +3 -1
  56. package/dist-types/components/input-tel/translations/pl-PL.d.ts +3 -1
  57. package/dist-types/components/input-tel/translations/pl.d.ts +3 -1
  58. package/dist-types/components/input-tel/translations/ro-RO.d.ts +3 -1
  59. package/dist-types/components/input-tel/translations/ro.d.ts +3 -1
  60. package/dist-types/components/input-tel/translations/ru-RU.d.ts +3 -1
  61. package/dist-types/components/input-tel/translations/ru.d.ts +3 -1
  62. package/dist-types/components/input-tel/translations/sk-SK.d.ts +3 -1
  63. package/dist-types/components/input-tel/translations/sk.d.ts +3 -1
  64. package/dist-types/components/input-tel/translations/uk-UA.d.ts +3 -1
  65. package/dist-types/components/input-tel/translations/uk.d.ts +3 -1
  66. package/dist-types/components/input-tel/translations/zh.d.ts +3 -1
  67. package/dist-types/components/listbox/src/ListboxMixin.d.ts.map +1 -1
  68. package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts +1 -2
  69. package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts.map +1 -1
  70. package/dist-types/components/overlays/src/OverlayController.d.ts +8 -5
  71. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  72. package/dist-types/components/overlays/src/utils/adopt-styles.d.ts +24 -0
  73. package/dist-types/components/overlays/src/utils/adopt-styles.d.ts.map +1 -0
  74. package/dist-types/components/overlays/test/OverlayController.test.d.ts.map +1 -1
  75. package/dist-types/components/overlays/test/utils-tests/adopt-styles.test.d.ts +2 -0
  76. package/dist-types/components/overlays/test/utils-tests/adopt-styles.test.d.ts.map +1 -0
  77. package/dist-types/components/overlays/test-helpers/createShadowHost.d.ts +8 -0
  78. package/dist-types/components/overlays/test-helpers/createShadowHost.d.ts.map +1 -0
  79. package/dist-types/components/overlays/{test/utils-tests/local-positioning-helpers.d.ts → test-helpers/normalizeTransformStyle.d.ts} +1 -1
  80. package/dist-types/components/overlays/test-helpers/normalizeTransformStyle.d.ts.map +1 -0
  81. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +35 -3
  82. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  83. package/dist-types/components/tooltip/src/LionTooltip.d.ts.map +1 -1
  84. package/dist-types/tsconfig.tsbuildinfo +1 -1
  85. package/docs/components/button/use-cases.md +2 -0
  86. package/docs/components/checkbox-group/overview.md +2 -2
  87. package/docs/components/collapsible/overview.md +1 -1
  88. package/docs/components/combobox/overview.md +1 -1
  89. package/docs/components/combobox/src/demo-selection-display.js +1 -1
  90. package/docs/components/combobox/use-cases.md +1 -1
  91. package/docs/components/dialog/overview.md +1 -1
  92. package/docs/components/dialog/src/demoStyle.js +18 -0
  93. package/docs/components/fieldset/overview.md +5 -5
  94. package/docs/components/form/overview.md +3 -3
  95. package/docs/components/icon/use-cases.md +1 -1
  96. package/docs/components/input/overview.md +1 -1
  97. package/docs/components/input-amount/overview.md +3 -3
  98. package/docs/components/input-date/overview.md +3 -3
  99. package/docs/components/input-datepicker/overview.md +5 -5
  100. package/docs/components/input-email/overview.md +2 -2
  101. package/docs/components/input-iban/overview.md +2 -2
  102. package/docs/components/input-range/overview.md +2 -2
  103. package/docs/components/input-stepper/overview.md +1 -1
  104. package/docs/components/input-tel/overview.md +1 -1
  105. package/docs/components/input-tel/use-cases.md +6 -6
  106. package/docs/components/input-tel-dropdown/overview.md +1 -1
  107. package/docs/components/radio-group/overview.md +3 -3
  108. package/docs/components/select-rich/use-cases.md +30 -24
  109. package/docs/components/tabs/examples.md +1 -1
  110. package/docs/fundamentals/index.md +4 -4
  111. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  112. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  113. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  114. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  115. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  116. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  117. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  118. package/docs/fundamentals/systems/core/overview.md +1 -1
  119. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  120. package/docs/fundamentals/systems/form/model-value.md +1 -1
  121. package/docs/fundamentals/systems/form/overview.md +28 -28
  122. package/docs/fundamentals/systems/form/validate.md +1 -1
  123. package/docs/fundamentals/systems/localize/overview.md +4 -4
  124. package/docs/fundamentals/systems/overlays/assets/demo-el-using-overlaymixin.mjs +0 -16
  125. package/docs/fundamentals/systems/overlays/configuration.md +18 -32
  126. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  127. package/docs/fundamentals/systems/overlays/use-cases.md +5 -5
  128. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  129. package/docs/fundamentals/tools/singleton-manager/.gitignore +1 -0
  130. package/docs/fundamentals/tools/singleton-manager/example-complex/demo-app.js +93 -0
  131. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/overlays/index.js +30 -0
  132. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/overlays/instance.js +4 -0
  133. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-a/page-a.js +35 -0
  134. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-b/node_modules/overlays/index.js +30 -0
  135. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-b/node_modules/overlays/instance.js +4 -0
  136. package/docs/fundamentals/tools/singleton-manager/example-complex/node_modules/page-b/page-b.js +49 -0
  137. package/docs/fundamentals/tools/singleton-manager/example-complex/overlayCompatibility.js +70 -0
  138. package/docs/fundamentals/tools/singleton-manager/example-fail/demo-app.js +91 -0
  139. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/overlays/index.js +30 -0
  140. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/overlays/instance.js +3 -0
  141. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-a/page-a.js +34 -0
  142. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-b/node_modules/overlays/index.js +30 -0
  143. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-b/node_modules/overlays/instance.js +3 -0
  144. package/docs/fundamentals/tools/singleton-manager/example-fail/node_modules/page-b/page-b.js +34 -0
  145. package/docs/fundamentals/tools/singleton-manager/example-success/demo-app.js +93 -0
  146. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/overlays/index.js +30 -0
  147. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/overlays/instance.js +4 -0
  148. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-a/page-a.js +34 -0
  149. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-b/node_modules/overlays/index.js +30 -0
  150. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-b/node_modules/overlays/instance.js +4 -0
  151. package/docs/fundamentals/tools/singleton-manager/example-success/node_modules/page-b/page-b.js +34 -0
  152. package/docs/fundamentals/tools/singleton-manager/example-success/overlayCompatibility.js +23 -0
  153. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  154. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  155. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  156. package/docs/guides/index.md +8 -8
  157. package/docs/guides/principles/index.md +5 -5
  158. package/docs/guides/principles/styling.md +1 -1
  159. package/package.json +2 -2
  160. package/dist-types/components/overlays/test/utils-tests/local-positioning-helpers.d.ts.map +0 -1
@@ -0,0 +1,136 @@
1
+ // See: https://github.com/ing-bank/lion/issues/1880
2
+
3
+ /**
4
+ * @typedef {import('lit').CSSResult|CSSStyleSheet} AdoptableStyle
5
+ * @typedef {(renderRoot:DocumentOrShadowRoot, style: AdoptableStyle, opts?: {teardown?: boolean}) => void} AdoptStyleFn
6
+ * @typedef {(renderRoot:DocumentOrShadowRoot, styles: AdoptableStyle[], opts?: {teardown?: boolean}) => void} AdoptStylesFn
7
+ */
8
+
9
+ // Shared protected object that can be spied/mocked in tests
10
+ export const _adoptStyleUtils = {
11
+ // Mocking Document.prototype.adoptedStyleSheets seemed impossible
12
+ supportsAdoptingStyleSheets:
13
+ window.ShadowRoot &&
14
+ // @ts-ignore
15
+ (window.ShadyCSS === undefined || window.ShadyCSS.nativeShadow) &&
16
+ 'adoptedStyleSheets' in Document.prototype &&
17
+ 'replace' in CSSStyleSheet.prototype,
18
+ /** @type {AdoptStyleFn} */
19
+ // @ts-ignore
20
+ adoptStyle: undefined,
21
+ /** @type {AdoptStylesFn} */
22
+ // @ts-ignore
23
+ adoptStyles: undefined,
24
+ };
25
+
26
+ const styleCache = new Map();
27
+
28
+ /**
29
+ * @param {CSSStyleSheet} cssStyleSheet
30
+ */
31
+ export function serializeConstructableStylesheet(cssStyleSheet) {
32
+ return Array.from(cssStyleSheet.cssRules)
33
+ .map(r => r.cssText)
34
+ .join('');
35
+ }
36
+
37
+ /**
38
+ * @param {DocumentOrShadowRoot} renderRoot
39
+ * @param {AdoptableStyle} style
40
+ * @param {{teardown?: boolean}} opts
41
+ */
42
+ function adoptStyleWhenAdoptedStylesheetsNotSupported(
43
+ renderRoot,
44
+ style,
45
+ { teardown = false } = {},
46
+ ) {
47
+ const adoptRoot = /** @type {ShadowRoot|Document['body']} */ (
48
+ renderRoot === document ? document.body : renderRoot
49
+ );
50
+ // @ts-ignore
51
+ const styleText = style.cssText || serializeConstructableStylesheet(style);
52
+
53
+ if (!teardown) {
54
+ const styleEl = document.createElement('style');
55
+ // keep notation, so it's not renamed in minification/build
56
+ // eslint-disable-next-line dot-notation
57
+ const nonce = window['litNonce'];
58
+ if (nonce !== undefined) {
59
+ styleEl.setAttribute('nonce', nonce);
60
+ }
61
+ styleEl.textContent = styleText;
62
+ adoptRoot.appendChild(styleEl);
63
+ } else {
64
+ const foundStyleEls = Array.from(adoptRoot.querySelectorAll('style'));
65
+
66
+ for (const foundStyleEl of foundStyleEls) {
67
+ if (foundStyleEl.textContent === styleText) {
68
+ foundStyleEl.remove();
69
+ break;
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @param {DocumentOrShadowRoot} renderRoot
77
+ * @param {AdoptableStyle} style
78
+ * @param {{teardown?: boolean}} opts
79
+ */
80
+ function handleCache(renderRoot, style, { teardown = false } = {}) {
81
+ let haltFurtherExecution = false;
82
+ if (!styleCache.has(renderRoot)) {
83
+ styleCache.set(renderRoot, []);
84
+ }
85
+ const addedStylesForRoot = styleCache.get(renderRoot);
86
+ const foundStyle = addedStylesForRoot.find(
87
+ (/** @type {import("lit").CSSResultOrNative} */ addedStyle) => style === addedStyle,
88
+ );
89
+
90
+ if (foundStyle && teardown) {
91
+ addedStylesForRoot.splice(addedStylesForRoot.indexOf(style), 1);
92
+ } else if (!foundStyle && !teardown) {
93
+ addedStylesForRoot.push(style);
94
+ } else if ((foundStyle && !teardown) || (!foundStyle && teardown)) {
95
+ // Already removed or added. We're done
96
+ haltFurtherExecution = true;
97
+ }
98
+
99
+ return { haltFurtherExecution };
100
+ }
101
+
102
+ /** @type {AdoptStyleFn} */
103
+ export function adoptStyle(renderRoot, style, { teardown = false } = {}) {
104
+ const { haltFurtherExecution } = handleCache(renderRoot, style, { teardown });
105
+ if (haltFurtherExecution) {
106
+ return;
107
+ }
108
+
109
+ if (!_adoptStyleUtils.supportsAdoptingStyleSheets) {
110
+ adoptStyleWhenAdoptedStylesheetsNotSupported(renderRoot, style, { teardown });
111
+ return;
112
+ }
113
+
114
+ const sheet = style instanceof CSSStyleSheet ? style : style.styleSheet;
115
+ if (!sheet) {
116
+ throw new Error(`Please provide a CSSResultOrNative style`);
117
+ }
118
+
119
+ if (!teardown) {
120
+ // @ts-ignore
121
+ // eslint-disable-next-line no-param-reassign
122
+ renderRoot.adoptedStyleSheets.push(sheet);
123
+ } else if (renderRoot.adoptedStyleSheets.includes(sheet)) {
124
+ renderRoot.adoptedStyleSheets.splice(renderRoot.adoptedStyleSheets.indexOf(sheet), 1);
125
+ }
126
+ }
127
+
128
+ /** @type {AdoptStylesFn} */
129
+ export function adoptStyles(renderRoot, styles, { teardown = false } = {}) {
130
+ for (const style of styles) {
131
+ _adoptStyleUtils.adoptStyle(renderRoot, style, { teardown });
132
+ }
133
+ }
134
+
135
+ _adoptStyleUtils.adoptStyle = adoptStyle;
136
+ _adoptStyleUtils.adoptStyles = adoptStyles;
@@ -11,9 +11,10 @@ import {
11
11
  import sinon from 'sinon';
12
12
  import { OverlayController, overlays } from '@lion/ui/overlays.js';
13
13
  import { mimicClick } from '@lion/ui/overlays-test-helpers.js';
14
- import { overlayShadowDomStyle } from '../src/overlayShadowDomStyle.js';
15
14
  import { keyCodes } from '../src/utils/key-codes.js';
16
15
  import { simulateTab } from '../src/utils/simulate-tab.js';
16
+ import { _adoptStyleUtils } from '../src/utils/adopt-styles.js';
17
+ import { createShadowHost } from '../test-helpers/createShadowHost.js';
17
18
 
18
19
  /**
19
20
  * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig
@@ -22,20 +23,6 @@ import { simulateTab } from '../src/utils/simulate-tab.js';
22
23
 
23
24
  const wrappingDialogNodeStyle = 'display: none; z-index: 9999;';
24
25
 
25
- /**
26
- * Returns element that adopts stylesheet
27
- * @param {Element} shadowOrBodyEl
28
- * @returns {ShadowRoot}
29
- */
30
- function getRootNodeOrBodyElThatAdoptsStylesheet(shadowOrBodyEl) {
31
- const rootNode = /** @type {* & DocumentOrShadowRoot} */ (shadowOrBodyEl.getRootNode());
32
- if (rootNode === document) {
33
- // @ts-ignore
34
- return document.body;
35
- }
36
- return rootNode;
37
- }
38
-
39
26
  /**
40
27
  * Make sure that all browsers serialize html in a similar way
41
28
  * (Firefox tends to output empty style attrs)
@@ -97,51 +84,17 @@ describe('OverlayController', () => {
97
84
  });
98
85
 
99
86
  describe('Stylesheets', () => {
100
- it('adds a stylesheet to the body when contentWrapper is located there', async () => {
101
- new OverlayController({
102
- ...withLocalTestConfig(),
103
- });
104
- // @ts-ignore
105
- if (document.body.adoptedStyleSheets) {
106
- // @ts-ignore
107
- expect(document.body.adoptedStyleSheets).to.include(overlayShadowDomStyle.styleSheet);
108
- }
109
- });
110
-
111
- it('adds a stylesheet to the shadowRoot when contentWrappeNode is located there', async () => {
87
+ it('calls adoptStyles', async () => {
88
+ const spy = sinon.spy(_adoptStyleUtils, 'adoptStyle');
89
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
112
90
  const contentNode = /** @type {HTMLElement} */ (await fixture('<div>contentful</div>'));
113
- const shadowHost = document.createElement('div');
114
- shadowHost.attachShadow({ mode: 'open' });
115
- /** @type {ShadowRoot} */ (shadowHost.shadowRoot).innerHTML = `<slot></slot>`;
116
91
  shadowHost.appendChild(contentNode);
117
- document.body.appendChild(shadowHost);
118
- const ctrl = new OverlayController({
92
+ new OverlayController({
119
93
  ...withLocalTestConfig(),
120
94
  contentNode,
121
95
  });
122
-
123
- const rootNodeOrBody = getRootNodeOrBodyElThatAdoptsStylesheet(ctrl.contentWrapperNode);
124
- expect(rootNodeOrBody).to.not.equal(document.body);
125
-
126
- if (rootNodeOrBody.adoptedStyleSheets) {
127
- expect(rootNodeOrBody.adoptedStyleSheets).to.include(overlayShadowDomStyle.styleSheet);
128
- }
129
- document.body.removeChild(shadowHost);
130
- });
131
-
132
- it('does not add same stylesheet twice', async () => {
133
- // @ts-ignore
134
- if (!document.body.adoptedStyleSheets) {
135
- return;
136
- }
137
-
138
- new OverlayController({ ...withLocalTestConfig() });
139
- // @ts-ignore
140
- const amountOfStylesheetsAfterOneInit = document.body.adoptedStyleSheets.length;
141
-
142
- new OverlayController({ ...withLocalTestConfig() });
143
- // @ts-ignore
144
- expect(document.body.adoptedStyleSheets.length).to.equal(amountOfStylesheetsAfterOneInit);
96
+ expect(spy).to.have.been.called;
97
+ cleanupShadowHost();
145
98
  });
146
99
  });
147
100
 
@@ -1515,6 +1468,22 @@ describe('OverlayController', () => {
1515
1468
  expect(ctrl.contentWrapperNode.classList.contains('overlays__overlay-container--top-right'));
1516
1469
  expect(ctrl.isShown).to.be.true;
1517
1470
  });
1471
+
1472
+ it('disables backdrop when switching to hasBackrop "false"', async () => {
1473
+ const ctrl = new OverlayController({
1474
+ ...withLocalTestConfig(),
1475
+ contentNode: /** @type {HTMLElement} */ (await fixture(html`<div>content1</div>`)),
1476
+ hasBackdrop: true,
1477
+ });
1478
+ await ctrl.show(); // Popper adds inline styles
1479
+ expect(ctrl.backdropNode).not.to.be.undefined;
1480
+ expect(Array.from(ctrl.backdropNode.classList)).to.include('overlays__backdrop--visible');
1481
+
1482
+ ctrl.updateConfig({
1483
+ hasBackdrop: false,
1484
+ });
1485
+ expect(Array.from(ctrl.backdropNode.classList)).to.not.include('overlays__backdrop--visible');
1486
+ });
1518
1487
  });
1519
1488
 
1520
1489
  describe('Accessibility', () => {
@@ -2,7 +2,7 @@
2
2
  import { expect, fixture, fixtureSync } from '@open-wc/testing';
3
3
  import { html } from 'lit/static-html.js';
4
4
  import { OverlayController } from '@lion/ui/overlays.js';
5
- import { normalizeTransformStyle } from './utils-tests/local-positioning-helpers.js';
5
+ import { normalizeTransformStyle } from '../test-helpers/normalizeTransformStyle.js';
6
6
 
7
7
  /**
8
8
  * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig
@@ -0,0 +1,177 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { css } from 'lit';
3
+ import sinon from 'sinon';
4
+ import {
5
+ adoptStyle,
6
+ adoptStyles,
7
+ serializeConstructableStylesheet,
8
+ _adoptStyleUtils,
9
+ } from '../../src/utils/adopt-styles.js';
10
+ import { createShadowHost } from '../../test-helpers/createShadowHost.js';
11
+
12
+ function mockNoSupportAdoptedStylesheets() {
13
+ _adoptStyleUtils.supportsAdoptingStyleSheets = false;
14
+ }
15
+
16
+ function restoreMockNoSupportAdoptedStylesheets() {
17
+ _adoptStyleUtils.supportsAdoptingStyleSheets = true;
18
+ }
19
+
20
+ describe('adoptStyle()', () => {
21
+ it('adds a stylesheet from a CSSResult to the shadowRoot', async () => {
22
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
23
+ const myCssResult = css``;
24
+ const root = /** @type {ShadowRoot} */ (shadowHost.shadowRoot);
25
+
26
+ adoptStyle(root, myCssResult);
27
+ expect(root.adoptedStyleSheets).to.include(myCssResult.styleSheet);
28
+
29
+ cleanupShadowHost();
30
+ });
31
+
32
+ it('adds a stylesheet from a CSSStyleSheet to the shadowRoot', async () => {
33
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
34
+ const myCSSStyleSheet = new CSSStyleSheet();
35
+ const root = /** @type {ShadowRoot} */ (shadowHost.shadowRoot);
36
+
37
+ adoptStyle(root, myCSSStyleSheet);
38
+ expect(root.adoptedStyleSheets).to.include(myCSSStyleSheet);
39
+
40
+ cleanupShadowHost();
41
+ });
42
+
43
+ it('does not add same stylesheet twice', async () => {
44
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
45
+ const myCssResult = css``;
46
+ const root = /** @type {ShadowRoot} */ (shadowHost.shadowRoot);
47
+
48
+ adoptStyle(root, myCssResult);
49
+ const amountOfStylesheetsAfterOneInit = root.adoptedStyleSheets.length;
50
+ adoptStyle(root, myCssResult);
51
+ expect(root.adoptedStyleSheets.length).to.equal(amountOfStylesheetsAfterOneInit);
52
+
53
+ cleanupShadowHost();
54
+ });
55
+
56
+ it('works as well when document is the root', async () => {
57
+ const myCssResult = css``;
58
+ const root = document;
59
+
60
+ adoptStyle(root, myCssResult);
61
+ expect(root.adoptedStyleSheets).to.include(myCssResult.styleSheet);
62
+ });
63
+
64
+ describe('Teardown', () => {
65
+ it('removes stylesheets from the shadowRoot', async () => {
66
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
67
+ const myCssResult = css``;
68
+ const root = /** @type {ShadowRoot} */ (shadowHost.shadowRoot);
69
+
70
+ adoptStyle(root, myCssResult);
71
+ expect(root.adoptedStyleSheets).to.include(myCssResult.styleSheet);
72
+ adoptStyle(root, myCssResult, { teardown: true });
73
+ expect(root.adoptedStyleSheets).to.not.include(myCssResult.styleSheet);
74
+
75
+ const myCSSStyleSheet = new CSSStyleSheet();
76
+ adoptStyle(root, myCSSStyleSheet);
77
+ expect(root.adoptedStyleSheets).to.include(myCSSStyleSheet);
78
+ adoptStyle(root, myCSSStyleSheet, { teardown: true });
79
+ expect(root.adoptedStyleSheets).to.not.include(myCSSStyleSheet);
80
+
81
+ cleanupShadowHost();
82
+ });
83
+ });
84
+
85
+ describe('Fallback when adoptedStyleSheets are not supported', () => {
86
+ beforeEach(() => {
87
+ mockNoSupportAdoptedStylesheets();
88
+ });
89
+
90
+ afterEach(() => {
91
+ restoreMockNoSupportAdoptedStylesheets();
92
+ });
93
+
94
+ it('adds a "traditional" stylesheet to the shadowRoot', async () => {
95
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
96
+ const myCssResult = css`
97
+ .check {
98
+ color: blue;
99
+ }
100
+ `;
101
+ const root = /** @type {ShadowRoot} */ (shadowHost.shadowRoot);
102
+
103
+ adoptStyle(root, myCssResult);
104
+ const sheets = Array.from(root.querySelectorAll('style'));
105
+ const lastAddedSheet = sheets[sheets.length - 1];
106
+ expect(lastAddedSheet.textContent).to.equal(myCssResult.cssText);
107
+
108
+ cleanupShadowHost();
109
+ });
110
+
111
+ it('adds a "traditional" stylesheet to the body', async () => {
112
+ mockNoSupportAdoptedStylesheets();
113
+
114
+ const myCssResult = css`
115
+ .check {
116
+ color: blue;
117
+ }
118
+ `;
119
+ const root = document;
120
+ adoptStyle(root, myCssResult);
121
+
122
+ const sheets = Array.from(document.body.querySelectorAll('style'));
123
+ const lastAddedSheet = sheets[sheets.length - 1];
124
+ expect(lastAddedSheet.textContent).to.equal(myCssResult.cssText);
125
+ restoreMockNoSupportAdoptedStylesheets();
126
+ });
127
+
128
+ describe('Teardown', () => {
129
+ it('removes a "traditional" stylesheet from the shadowRoot', async () => {
130
+ const { shadowHost, cleanupShadowHost } = createShadowHost();
131
+ const myCssResult = css`
132
+ .check {
133
+ color: blue;
134
+ }
135
+ `;
136
+ const root = /** @type {ShadowRoot} */ (shadowHost.shadowRoot);
137
+
138
+ adoptStyle(root, myCssResult);
139
+ const sheets1 = Array.from(root.querySelectorAll('style'));
140
+ const lastAddedSheet1 = sheets1[sheets1.length - 1];
141
+ expect(lastAddedSheet1.textContent).to.equal(myCssResult.cssText);
142
+ adoptStyle(root, myCssResult, { teardown: true });
143
+ const sheets2 = Array.from(root.querySelectorAll('style'));
144
+ const lastAddedSheet2 = sheets2[sheets2.length - 1];
145
+ expect(lastAddedSheet2?.textContent).to.not.equal(myCssResult.cssText);
146
+
147
+ const myCSSStyleSheet = new CSSStyleSheet();
148
+ myCSSStyleSheet.insertRule('.check { color: blue; }');
149
+
150
+ adoptStyle(root, myCSSStyleSheet);
151
+ const sheets3 = Array.from(root.querySelectorAll('style'));
152
+ const lastAddedSheet3 = sheets3[sheets3.length - 1];
153
+ expect(lastAddedSheet3.textContent).to.equal(
154
+ serializeConstructableStylesheet(myCSSStyleSheet),
155
+ );
156
+ adoptStyle(root, myCSSStyleSheet, { teardown: true });
157
+ const sheets4 = Array.from(root.querySelectorAll('style'));
158
+ const lastAddedSheet4 = sheets4[sheets4.length - 1];
159
+ expect(lastAddedSheet4?.textContent).to.not.equal(myCSSStyleSheet);
160
+
161
+ cleanupShadowHost();
162
+ });
163
+ });
164
+ });
165
+ });
166
+
167
+ describe('adoptStyles()', () => {
168
+ it('calls "adoptStyle" for all entries in CSSResult|CSSStylesheet[]', async () => {
169
+ const spy = sinon.spy(_adoptStyleUtils, 'adoptStyle');
170
+
171
+ const myCssResult = css``;
172
+ const myCSSStyleSheet = new CSSStyleSheet();
173
+
174
+ adoptStyles(document, [myCssResult, myCSSStyleSheet]);
175
+ expect(spy).to.have.been.calledTwice;
176
+ });
177
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Useful in tests when no need for wc
3
+ */
4
+ export function createShadowHost() {
5
+ const shadowHost = document.createElement('div');
6
+ shadowHost.attachShadow({ mode: 'open' });
7
+ /** @type {ShadowRoot} */ (shadowHost.shadowRoot).innerHTML = `<slot></slot>`;
8
+ document.body.appendChild(shadowHost);
9
+
10
+ return {
11
+ shadowHost,
12
+ cleanupShadowHost: () => {
13
+ document.body.removeChild(shadowHost);
14
+ },
15
+ };
16
+ }
@@ -67,14 +67,11 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
67
67
  `;
68
68
  }
69
69
 
70
+ /** @type {SlotsMap} */
70
71
  get slots() {
71
72
  return {
72
73
  ...super.slots,
73
- invoker: () => {
74
- const invokerEl = this.createScopedElement('lion-select-invoker');
75
- invokerEl.setAttribute('data-tag-name', 'lion-select-invoker');
76
- return invokerEl;
77
- },
74
+ invoker: () => html`<lion-select-invoker></lion-select-invoker>`,
78
75
  };
79
76
  }
80
77
 
@@ -88,6 +85,15 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
88
85
  );
89
86
  }
90
87
 
88
+ /**
89
+ * @protected
90
+ * @configure FocusMixin
91
+ */
92
+ // @ts-ignore
93
+ get _focusableNode() {
94
+ return this._invokerNode;
95
+ }
96
+
91
97
  /**
92
98
  * @configure ListboxMixin
93
99
  * @protected
@@ -262,7 +268,7 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
262
268
 
263
269
  /** @protected */
264
270
  _onFormElementsChanged() {
265
- this.singleOption = this.formElements.length === 1;
271
+ this.singleOption = this.formElements.length === 1 && !this.hasNoDefaultSelected;
266
272
  this._invokerNode.singleOption = this.singleOption;
267
273
  }
268
274
 
@@ -336,6 +342,8 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
336
342
  _defineOverlayConfig() {
337
343
  return {
338
344
  ...withDropdownConfig(),
345
+ // Needs more advanced behavior (see `__invokerOnClick`)
346
+ visibilityTriggerFunction: undefined,
339
347
  };
340
348
  }
341
349
 
@@ -49,10 +49,21 @@ describe('lion-select-rich', () => {
49
49
  expect(document.activeElement === document.body).to.be.true;
50
50
  const { _labelNode, _invokerNode } = getSelectRichMembers(el);
51
51
  _labelNode.click();
52
-
53
52
  expect(document.activeElement === _invokerNode).to.be.true;
54
53
  });
55
54
 
55
+ it('has an attribute focused when focused', async () => {
56
+ const el = await fixture(html` <lion-select-rich label="foo"> </lion-select-rich> `);
57
+
58
+ el.focus();
59
+ await el.updateComplete;
60
+ expect(el.hasAttribute('focused')).to.be.true;
61
+
62
+ el.blur();
63
+ await el.updateComplete;
64
+ expect(el.hasAttribute('focused')).to.be.false;
65
+ });
66
+
56
67
  it('checks the first enabled option', async () => {
57
68
  const el = await fixture(html`
58
69
  <lion-select-rich>
@@ -400,6 +411,19 @@ describe('lion-select-rich', () => {
400
411
  expect(el.singleOption).to.be.false;
401
412
  expect(_invokerNode.singleOption).to.be.false;
402
413
  });
414
+
415
+ it('should have singleOption only if one option and no hasNoDefaultSelected', async () => {
416
+ const el = await fixture(html`
417
+ <lion-select-rich has-no-default-selected>
418
+ <lion-option .choiceValue=${10}>Item 1</lion-option>
419
+ </lion-select-rich>
420
+ `);
421
+
422
+ const { _invokerNode } = getSelectRichMembers(el);
423
+
424
+ expect(el.singleOption).to.be.false;
425
+ expect(_invokerNode.singleOption).to.be.false;
426
+ });
403
427
  });
404
428
 
405
429
  describe('interaction-mode', () => {
@@ -32,6 +32,10 @@ export class LionTooltip extends ArrowMixin(OverlayMixin(LitElement)) {
32
32
  :host([hidden]) {
33
33
  display: none;
34
34
  }
35
+
36
+ ::slotted([slot='content']) {
37
+ width: max-content;
38
+ }
35
39
  `,
36
40
  ];
37
41
  }
@@ -54,9 +58,19 @@ export class LionTooltip extends ArrowMixin(OverlayMixin(LitElement)) {
54
58
  /** @protected */
55
59
  // eslint-disable-next-line class-methods-use-this
56
60
  _defineOverlayConfig() {
61
+ const superCfg = super._defineOverlayConfig();
62
+ const tooltipCfg = withTooltipConfig({ invokerRelation: this.invokerRelation });
57
63
  return /** @type {OverlayConfig} */ ({
58
- ...super._defineOverlayConfig(),
59
- ...withTooltipConfig({ invokerRelation: this.invokerRelation }),
64
+ ...superCfg,
65
+ ...tooltipCfg,
66
+ popperConfig: {
67
+ ...(superCfg.popperConfig || {}),
68
+ ...(tooltipCfg.popperConfig || {}),
69
+ modifiers: [
70
+ ...(superCfg.popperConfig?.modifiers || []),
71
+ ...(tooltipCfg.popperConfig?.modifiers || []),
72
+ ],
73
+ },
60
74
  });
61
75
  }
62
76
  }
@@ -227,8 +227,7 @@ describe('lion-tooltip', () => {
227
227
  expect(el._arrowNode).to.be.displayed;
228
228
  });
229
229
 
230
- // TODO: promise for dynamic import popper does not resolve?
231
- it.skip('makes sure positioning of the arrow is correct', async () => {
230
+ it('makes sure positioning of the arrow is correct', async () => {
232
231
  const el = /** @type {LionTooltip} */ (
233
232
  await fixture(html`
234
233
  <lion-tooltip