@lion/ui 0.6.1 → 0.7.0

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 (91) hide show
  1. package/components/core/src/browserDetection.js +27 -17
  2. package/components/form-core/test-suites/form-group/FormGroupMixin.suite.js +1 -1
  3. package/components/input-amount/src/parsers.js +11 -3
  4. package/components/input-amount/test/formatters.test.js +11 -0
  5. package/components/input-amount/test/parsers.test.js +32 -4
  6. package/components/input-tel-dropdown/src/LionInputTelDropdown.js +1 -13
  7. package/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js +2 -0
  8. package/components/input-tel-dropdown/types/index.ts +1 -0
  9. package/components/localize/src/number/formatNumber.js +1 -1
  10. package/components/localize/test/number/formatNumber.test.js +9 -0
  11. package/components/overlays/test/local-positioning.test.js +13 -21
  12. package/components/overlays/test/utils-tests/contain-focus.test.js +2 -2
  13. package/components/overlays/test/utils-tests/deep-contains.test.js +3 -3
  14. package/components/overlays/test/utils-tests/get-focusable-elements.test.js +1 -1
  15. package/components/select-rich/src/LionSelectRich.js +1 -1
  16. package/components/select-rich/test/lion-select-rich-interaction.test.js +16 -40
  17. package/components/select-rich/test/lion-select-rich.test.js +16 -14
  18. package/components/select-rich/test-helpers/getSelectRichMembers.js +18 -0
  19. package/custom-elements.json +50 -7
  20. package/dist-types/components/core/src/browserDetection.d.ts +3 -3
  21. package/dist-types/components/input-amount/src/parsers.d.ts.map +1 -1
  22. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
  23. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.d.ts.map +1 -1
  24. package/dist-types/components/input-tel-dropdown/types/index.d.ts +1 -0
  25. package/dist-types/components/input-tel-dropdown/types/index.d.ts.map +1 -1
  26. package/dist-types/components/select-rich/test/lion-select-rich-interaction.test.d.ts.map +1 -1
  27. package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts.map +1 -1
  28. package/dist-types/components/select-rich/test-helpers/getSelectRichMembers.d.ts +19 -0
  29. package/dist-types/components/select-rich/test-helpers/getSelectRichMembers.d.ts.map +1 -0
  30. package/dist-types/exports/select-rich-test-helpers.d.ts +2 -0
  31. package/dist-types/exports/select-rich-test-helpers.d.ts.map +1 -0
  32. package/dist-types/tsconfig.tsbuildinfo +1 -1
  33. package/docs/components/checkbox-group/overview.md +2 -2
  34. package/docs/components/collapsible/overview.md +1 -1
  35. package/docs/components/combobox/overview.md +1 -1
  36. package/docs/components/combobox/src/gh-combobox/gh-combobox.js +1 -1
  37. package/docs/components/combobox/src/google-combobox/google-combobox.js +1 -1
  38. package/docs/components/combobox/use-cases.md +1 -1
  39. package/docs/components/dialog/overview.md +1 -1
  40. package/docs/components/fieldset/overview.md +5 -5
  41. package/docs/components/form/overview.md +3 -3
  42. package/docs/components/icon/use-cases.md +1 -1
  43. package/docs/components/input/overview.md +1 -1
  44. package/docs/components/input-amount/overview.md +4 -4
  45. package/docs/components/input-date/overview.md +3 -3
  46. package/docs/components/input-datepicker/overview.md +5 -5
  47. package/docs/components/input-email/overview.md +2 -2
  48. package/docs/components/input-file/overview.md +1 -1
  49. package/docs/components/input-iban/overview.md +2 -2
  50. package/docs/components/input-range/overview.md +2 -2
  51. package/docs/components/input-stepper/overview.md +1 -1
  52. package/docs/components/input-tel/overview.md +1 -1
  53. package/docs/components/input-tel/use-cases.md +6 -6
  54. package/docs/components/input-tel-dropdown/overview.md +1 -1
  55. package/docs/components/input-tel-dropdown/src/intl-input-tel-dropdown.js +1 -0
  56. package/docs/components/listbox/use-cases.md +1 -1
  57. package/docs/components/radio-group/overview.md +3 -3
  58. package/docs/components/select/use-cases.md +1 -1
  59. package/docs/components/select-rich/use-cases.md +1 -1
  60. package/docs/components/tabs/examples.md +1 -1
  61. package/docs/fundamentals/index.md +4 -4
  62. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  63. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  64. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  65. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  66. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  67. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  68. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  69. package/docs/fundamentals/rationales/accessibility.md +1 -1
  70. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  71. package/docs/fundamentals/systems/core/overview.md +1 -1
  72. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  73. package/docs/fundamentals/systems/form/interaction-states.md +6 -0
  74. package/docs/fundamentals/systems/form/model-value.md +10 -1
  75. package/docs/fundamentals/systems/form/overview.md +28 -28
  76. package/docs/fundamentals/systems/form/validate.md +2 -2
  77. package/docs/fundamentals/systems/localize/overview.md +4 -4
  78. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  79. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  80. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  81. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  82. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  83. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  84. package/docs/guides/how-to/create-a-form.md +4 -4
  85. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  86. package/docs/guides/index.md +8 -8
  87. package/docs/guides/principles/index.md +5 -5
  88. package/docs/guides/principles/styling.md +1 -1
  89. package/exports/select-rich-test-helpers.js +1 -0
  90. package/package.json +1 -1
  91. package/select-rich-test-helpers.js +1 -0
@@ -1,17 +1,27 @@
1
+ import { isServer } from 'lit';
2
+
1
3
  /**
2
4
  * From https://stackoverflow.com/questions/4565112/javascript-how-to-find-out-if-the-user-browser-is-chrome
3
- * @param {string} [flavor]
5
+ * @param {string} [flavor='google-chrome']
4
6
  */
5
7
  function checkChrome(flavor = 'google-chrome') {
6
- const isChromium = /** @type {window & { chrome?: boolean}} */ (window).chrome;
8
+ if (isServer) {
9
+ return flavor === 'google-chrome';
10
+ }
11
+
12
+ const isChromium = /** @type {window & { chrome?: boolean}} */ (globalThis).chrome;
13
+
7
14
  if (flavor === 'chromium') {
8
15
  return isChromium;
9
16
  }
10
- const winNav = window.navigator;
11
- const vendorName = winNav.vendor;
12
- const isOpera = typeof (/** @type {window & { opr?: boolean}} */ (window).opr) !== 'undefined';
13
- const isIEedge = winNav.userAgent.indexOf('Edge') > -1;
14
- const isIOSChrome = winNav.userAgent.match('CriOS');
17
+ const winNav = globalThis.navigator;
18
+ const vendorName = winNav?.vendor;
19
+ const isOpera =
20
+ typeof (/** @type {window & { opr?: boolean}} */ (globalThis).opr) !== 'undefined';
21
+ // @ts-ignore
22
+ const isIEedge = globalThis.userAgent?.indexOf('Edge') > -1;
23
+ // @ts-ignore
24
+ const isIOSChrome = globalThis.userAgent?.match('CriOS');
15
25
 
16
26
  if (flavor === 'ios') {
17
27
  return isIOSChrome;
@@ -31,18 +41,18 @@ function checkChrome(flavor = 'google-chrome') {
31
41
  }
32
42
 
33
43
  export const browserDetection = {
34
- isIE11: /Trident/.test(window.navigator.userAgent),
44
+ isIE11: /Trident/.test(globalThis.navigator?.userAgent),
35
45
  isChrome: checkChrome(),
36
46
  isIOSChrome: checkChrome('ios'),
37
47
  isChromium: checkChrome('chromium'),
38
- isFirefox: navigator.userAgent.toLowerCase().indexOf('firefox') > -1,
39
- isMac: navigator.appVersion.indexOf('Mac') !== -1,
40
- isIOS: /iPhone|iPad|iPod/i.test(navigator.userAgent),
48
+ isFirefox: globalThis.navigator?.userAgent.toLowerCase().indexOf('firefox') > -1,
49
+ isMac: globalThis.navigator?.appVersion?.indexOf('Mac') !== -1,
50
+ isIOS: /iPhone|iPad|iPod/i.test(globalThis.navigator?.userAgent),
41
51
  isMacSafari:
42
- navigator.vendor &&
43
- navigator.vendor.indexOf('Apple') > -1 &&
44
- navigator.userAgent &&
45
- navigator.userAgent.indexOf('CriOS') === -1 &&
46
- navigator.userAgent.indexOf('FxiOS') === -1 &&
47
- navigator.appVersion.indexOf('Mac') !== -1,
52
+ globalThis.navigator?.vendor &&
53
+ globalThis.navigator?.vendor.indexOf('Apple') > -1 &&
54
+ globalThis.navigator?.userAgent &&
55
+ globalThis.navigator?.userAgent.indexOf('CriOS') === -1 &&
56
+ globalThis.navigator?.userAgent.indexOf('FxiOS') === -1 &&
57
+ globalThis.navigator?.appVersion.indexOf('Mac') !== -1,
48
58
  };
@@ -666,7 +666,7 @@ export function runFormGroupMixinSuite(cfg = {}) {
666
666
 
667
667
  await triggerFocusFor(hobbyInputNode);
668
668
  await triggerFocusFor(genderInputNode);
669
- const button = /** @type {FormGroup} */ (await fixture(html`<button></button>`));
669
+ const button = /** @type {FormGroup} */ (await fixture(html`<button>Button</button>`));
670
670
  button.focus();
671
671
 
672
672
  expect(el.touched).to.be.true;
@@ -11,7 +11,8 @@ import { parseNumber, getFractionDigits } from '@lion/ui/localize-no-side-effect
11
11
  * @return {number} new value with rounded up decimals
12
12
  */
13
13
  function round(value, decimals) {
14
- if (typeof decimals === 'undefined') {
14
+ const numberContainsExponent = value?.toString().includes('e');
15
+ if (typeof decimals === 'undefined' || numberContainsExponent) {
15
16
  return Number(value);
16
17
  }
17
18
  return Number(`${Math.round(Number(`${value}e${decimals}`))}e-${decimals}`);
@@ -30,10 +31,17 @@ function round(value, decimals) {
30
31
  * @param {FormatOptions} [givenOptions] Locale Options
31
32
  */
32
33
  export function parseAmount(value, givenOptions) {
34
+ const unmatchedInput = value.match(/[^0-9,.\- ]/g);
35
+ // for the full paste behavior documentation:
36
+ // ./docs/components/input-amount/use-cases.md#paste-behavior
37
+ if (unmatchedInput && givenOptions?.mode !== 'pasted') {
38
+ return undefined;
39
+ }
40
+
33
41
  const number = parseNumber(value, givenOptions);
34
42
 
35
- if (typeof number !== 'number') {
36
- return number;
43
+ if (typeof number !== 'number' || Number.isNaN(number)) {
44
+ return undefined;
37
45
  }
38
46
 
39
47
  /** @type {FormatOptions} */
@@ -74,4 +74,15 @@ describe('formatAmount()', () => {
74
74
  localizeManager.locale = 'nl-NL';
75
75
  expect(formatAmount(12345678)).to.equal('12.345.678,00');
76
76
  });
77
+
78
+ // TODO: make it work with big numbers, e.g. make use of BigInt
79
+ it.skip('rounds up big numbers', async () => {
80
+ expect(formatAmount(1e21, { locale: 'en-GB', currency: 'EUR' })).to.equal(
81
+ '1,000,000,000,000,000,000,000.00',
82
+ );
83
+ // eslint-disable-next-line no-loss-of-precision
84
+ expect(formatAmount(12345678987654321.42, { locale: 'en-GB', currency: 'EUR' })).to.equal(
85
+ '12,345,678,987,654,321.42',
86
+ );
87
+ });
77
88
  });
@@ -1,11 +1,20 @@
1
1
  import { expect } from '@open-wc/testing';
2
- import { localize } from '@lion/ui/localize.js';
3
-
2
+ import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
3
+ import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
4
4
  import { parseAmount } from '@lion/ui/input-amount.js';
5
5
 
6
6
  describe('parseAmount()', async () => {
7
+ const localizeManager = getLocalizeManager();
8
+
9
+ beforeEach(() => {
10
+ localizeManager.locale = 'en-GB';
11
+ });
12
+
13
+ afterEach(() => {
14
+ localizeTearDown();
15
+ });
16
+
7
17
  it('with currency set to correct amount of decimals', async () => {
8
- localize.locale = 'en-GB';
9
18
  expect(
10
19
  parseAmount('1.015', {
11
20
  currency: 'EUR',
@@ -29,7 +38,26 @@ describe('parseAmount()', async () => {
29
38
  });
30
39
 
31
40
  it('with no currency keeps all decimals', async () => {
32
- localize.locale = 'en-GB';
33
41
  expect(parseAmount('1.015')).to.equal(1.015);
34
42
  });
43
+
44
+ // TODO: make it work with big numbers, e.g. make use of BigInt
45
+ it.skip('rounds up big numbers', async () => {
46
+ // eslint-disable-next-line no-loss-of-precision
47
+ expect(parseAmount('999999999999999999999,42')).to.equal(999999999999999999999.42);
48
+ // eslint-disable-next-line no-loss-of-precision
49
+ expect(parseAmount('12,345,678,987,654,321.42')).to.equal(12345678987654321.42);
50
+ });
51
+
52
+ it('returns undefined if an invalid value is entered', async () => {
53
+ expect(parseAmount('foo')).to.equal(undefined);
54
+ expect(parseAmount('foo1')).to.equal(undefined);
55
+ expect(parseAmount('EUR 1,50')).to.equal(undefined);
56
+ expect(parseAmount('--1')).to.equal(undefined);
57
+ });
58
+
59
+ it('ignores letters when "pasted" mode used', async () => {
60
+ expect(parseAmount('foo1', { mode: 'pasted' })).to.equal(1);
61
+ expect(parseAmount('EUR 1,50', { mode: 'pasted' })).to.equal(1.5);
62
+ });
35
63
  });
@@ -100,6 +100,7 @@ export class LionInputTelDropdown extends LionInputTel {
100
100
  localizeManager.msg('lion-input-tel:suggestedCountries'),
101
101
  },
102
102
  },
103
+ input: this._inputNode,
103
104
  };
104
105
 
105
106
  return {
@@ -357,19 +358,6 @@ export class LionInputTelDropdown extends LionInputTel {
357
358
  }
358
359
  }
359
360
  }
360
-
361
- // Put focus on text box
362
- //
363
- // A LionSelectRich with interactionMode set on windows/linux
364
- // will set each item on arrow key up/down to activeElement
365
- // which causes the focus to jump every time to the inputNode
366
- const overlayController = dropdownElement._overlayCtrl;
367
- // @ts-ignore interactionMode only exists on LionSelectRich not on HTMLSelectElement
368
- if (overlayController?.isShown && dropdownElement.interactionMode !== 'windows/linux') {
369
- setTimeout(() => {
370
- this._inputNode.focus();
371
- });
372
- }
373
361
  }
374
362
 
375
363
  /**
@@ -152,6 +152,8 @@ export function runInputTelDropdownSuite({ klass } = { klass: LionInputTelDropdo
152
152
  props: { style: 'height: 100%;' },
153
153
  ref: { value: dropdownNode },
154
154
  },
155
+ // @ts-expect-error [allow-protected]
156
+ input: el._inputNode,
155
157
  },
156
158
  }),
157
159
  );
@@ -36,6 +36,7 @@ export type TemplateDataForDropdownInputTel = {
36
36
  };
37
37
  labels: { selectCountry: string };
38
38
  };
39
+ input: HTMLInputElement;
39
40
  };
40
41
  data: {
41
42
  activeRegion: string | undefined;
@@ -26,7 +26,7 @@ export function formatNumber(number, options = /** @type {FormatOptions} */ ({})
26
26
  }
27
27
  let printNumberOfParts = '';
28
28
  // update numberOfParts because there may be some parts added
29
- const numberOfParts = formattedToParts && formattedToParts.length;
29
+ const numberOfParts = formattedToParts ? formattedToParts.length : 0;
30
30
  for (let i = 0; i < numberOfParts; i += 1) {
31
31
  const part = /** @type {FormatNumberPart} */ (formattedToParts[i]);
32
32
  printNumberOfParts += part.value;
@@ -246,6 +246,15 @@ Please specify .groupSeparator / .decimalSeparator on the formatOptions object t
246
246
  });
247
247
  });
248
248
 
249
+ // TODO: make it work with big numbers, e.g. make use of BigInt
250
+ it.skip('can handle big numbers', async () => {
251
+ expect(formatNumber(1e21)).to.equal('1,000,000,000,000,000,000,000');
252
+ // eslint-disable-next-line no-loss-of-precision
253
+ expect(formatNumber(999999999999999999999.42)).to.equal('999,999,999,999,999,999,999.42');
254
+ // eslint-disable-next-line no-loss-of-precision
255
+ expect(formatNumber(12345678987654321.42)).to.equal('12,345,678,987,654,321.42');
256
+ });
257
+
249
258
  describe('normalization', () => {
250
259
  describe('en-GB', () => {
251
260
  it('supports basics', () => {
@@ -75,7 +75,7 @@ describe('Local Positioning', () => {
75
75
  ),
76
76
  invokerNode: /** @type {HTMLElement} */ (
77
77
  fixtureSync(html`
78
- <div role="button" style="width: 20px; height: 10px; background: orange;"></div>
78
+ <div role="button" style="width: 20px; height: 10px; background: orange;">Button</div>
79
79
  `)
80
80
  ),
81
81
  });
@@ -104,11 +104,9 @@ describe('Local Positioning', () => {
104
104
  ),
105
105
  invokerNode: /** @type {HTMLElement} */ (
106
106
  fixtureSync(html`
107
- <div
108
- role="button"
109
- style="width: 100px; height: 20px;"
110
- @click=${() => ctrl.show()}
111
- ></div>
107
+ <div role="button" style="width: 100px; height: 20px;" @click=${() => ctrl.show()}>
108
+ Button
109
+ </div>
112
110
  `)
113
111
  ),
114
112
  });
@@ -129,11 +127,9 @@ describe('Local Positioning', () => {
129
127
  ),
130
128
  invokerNode: /** @type {HTMLElement} */ (
131
129
  fixtureSync(html`
132
- <div
133
- role="button"
134
- style="width: 100px; height: 20px;"
135
- @click=${() => ctrl.show()}
136
- ></div>
130
+ <div role="button" style="width: 100px; height: 20px;" @click=${() => ctrl.show()}>
131
+ Button
132
+ </div>
137
133
  `)
138
134
  ),
139
135
  popperConfig: {
@@ -185,11 +181,9 @@ describe('Local Positioning', () => {
185
181
  ),
186
182
  invokerNode: /** @type {HTMLElement} */ (
187
183
  fixtureSync(html`
188
- <div
189
- role="button"
190
- style="width: 100px; height: 20px;"
191
- @click=${() => ctrl.show()}
192
- ></div>
184
+ <div role="button" style="width: 100px; height: 20px;" @click=${() => ctrl.show()}>
185
+ Button
186
+ </div>
193
187
  `)
194
188
  ),
195
189
  popperConfig: {
@@ -257,11 +251,9 @@ describe('Local Positioning', () => {
257
251
  ),
258
252
  invokerNode: /** @type {HTMLElement} */ (
259
253
  fixtureSync(html`
260
- <div
261
- role="button"
262
- style="width: 100px; height: 20px;"
263
- @click=${() => ctrl.show()}
264
- ></div>
254
+ <div role="button" style="width: 100px; height: 20px;" @click=${() => ctrl.show()}>
255
+ Button
256
+ </div>
265
257
  `)
266
258
  ),
267
259
  popperConfig: {
@@ -25,7 +25,7 @@ function simulateTabInWindow(elToRecieveFocus) {
25
25
 
26
26
  const interactionElementsNode = renderLitAsNode(html`
27
27
  <div>
28
- <button id="el1"></button>
28
+ <button id="el1">Button</button>
29
29
  <a id="el2" href="#">foo</a>
30
30
  <div id="el3" tabindex="0"></div>
31
31
  <input id="el4" />
@@ -50,7 +50,7 @@ const lightDomAutofocusTemplate = html`
50
50
  <button id="outside-1">outside 1</button>
51
51
 
52
52
  <div id="rootElement">
53
- <button id="el1"></button>
53
+ <button id="el1">Button</button>
54
54
  <a id="el2" href="#">foo</a>
55
55
  <div id="el3" tabindex="0"></div>
56
56
  <input id="el4" autofocus />
@@ -17,7 +17,7 @@ describe('deepContains()', () => {
17
17
  await fixture(html`
18
18
  <div id="light">
19
19
  ${shadowElement}
20
- <button id="light-el-1"></button>
20
+ <button id="light-el-1">Button</button>
21
21
  </div>
22
22
  `)
23
23
  );
@@ -65,7 +65,7 @@ describe('deepContains()', () => {
65
65
  await fixture(html`
66
66
  <div id="light">
67
67
  ${shadowElement} ${shadowElement2}
68
- <button id="light-el-1"></button>
68
+ <button id="light-el-1">Button</button>
69
69
  </div>
70
70
  `)
71
71
  );
@@ -101,7 +101,7 @@ describe('deepContains()', () => {
101
101
  await fixture(html`
102
102
  <div id="light">
103
103
  ${shadowElement}
104
- <button id="light-el-1"></button>
104
+ <button id="light-el-1">Button</button>
105
105
  </div>
106
106
  `)
107
107
  );
@@ -135,7 +135,7 @@ describe('getFocusableElements()', () => {
135
135
  class extends LitElement {
136
136
  render() {
137
137
  return html`
138
- <button id="el-a-1"></button>
138
+ <button id="el-a-1">Button</button>
139
139
 
140
140
  <element-b marker="marker-1"></element-b>
141
141
 
@@ -477,7 +477,7 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
477
477
  */
478
478
  // TODO: rename to _onKeyUp in v1
479
479
  __onKeyUp(ev) {
480
- if (this.disabled) {
480
+ if (this.disabled || this.readOnly) {
481
481
  return;
482
482
  }
483
483
 
@@ -7,6 +7,7 @@ import '@lion/ui/define/lion-option.js';
7
7
 
8
8
  import '@lion/ui/define/lion-listbox.js';
9
9
  import '@lion/ui/define/lion-select-rich.js';
10
+ import { getSelectRichMembers } from '@lion/ui/select-rich-test-helpers.js';
10
11
 
11
12
  /**
12
13
  * @typedef {import('../src/LionSelectRich.js').LionSelectRich} LionSelectRich
@@ -23,32 +24,6 @@ function mimicKeyPress(el, key, code = '') {
23
24
  el.dispatchEvent(new KeyboardEvent('keyup', { key, code }));
24
25
  }
25
26
 
26
- /**
27
- * @param {LionSelectRich} lionSelectEl
28
- */
29
- function getNodes(lionSelectEl) {
30
- const {
31
- // @ts-ignore protected members allowed in test
32
- _invokerNode: invoker,
33
- // @ts-ignore protected members allowed in test
34
- _feedbackNode: feedback,
35
- // @ts-ignore protected members allowed in test
36
- _labelNode: label,
37
- // @ts-ignore protected members allowed in test
38
- _helpTextNode: helpText,
39
- // @ts-ignore protected members allowed in test
40
- _listboxNode: listbox,
41
- // @ts-ignore protected members allowed in test
42
- } = lionSelectEl;
43
- return {
44
- invoker,
45
- feedback,
46
- label,
47
- helpText,
48
- listbox,
49
- };
50
- }
51
-
52
27
  describe('lion-select-rich interactions', () => {
53
28
  describe('Interaction mode', () => {
54
29
  it('autodetects interactionMode if not defined', async () => {
@@ -187,8 +162,8 @@ describe('lion-select-rich interactions', () => {
187
162
  </lion-select-rich>
188
163
  `)
189
164
  );
190
- const { invoker } = getNodes(el);
191
- expect(invoker.tabIndex).to.equal(-1);
165
+ const { _invokerNode } = getSelectRichMembers(el);
166
+ expect(_invokerNode.tabIndex).to.equal(-1);
192
167
  });
193
168
 
194
169
  it('cannot be opened via click if disabled', async () => {
@@ -199,8 +174,9 @@ describe('lion-select-rich interactions', () => {
199
174
  </lion-select-rich>
200
175
  `)
201
176
  );
202
- const { invoker } = getNodes(el);
203
- invoker.click();
177
+ const { _invokerNode, _overlayCtrl } = getSelectRichMembers(el);
178
+ _invokerNode.click();
179
+ await _overlayCtrl._showComplete;
204
180
  expect(el.opened).to.be.false;
205
181
  });
206
182
 
@@ -212,11 +188,11 @@ describe('lion-select-rich interactions', () => {
212
188
  </lion-select-rich>
213
189
  `)
214
190
  );
215
- const { invoker } = getNodes(el);
216
- expect(invoker.hasAttribute('disabled')).to.be.true;
191
+ const { _invokerNode } = getSelectRichMembers(el);
192
+ expect(_invokerNode.hasAttribute('disabled')).to.be.true;
217
193
  el.removeAttribute('disabled');
218
194
  await el.updateComplete;
219
- expect(invoker.hasAttribute('disabled')).to.be.false;
195
+ expect(_invokerNode.hasAttribute('disabled')).to.be.false;
220
196
  });
221
197
  });
222
198
 
@@ -232,10 +208,10 @@ describe('lion-select-rich interactions', () => {
232
208
  </lion-select-rich>
233
209
  `)
234
210
  );
235
- const { invoker } = getNodes(el);
211
+ const { _invokerNode } = getSelectRichMembers(el);
236
212
  expect(el.touched).to.be.false;
237
- await triggerFocusFor(invoker);
238
- await triggerBlurFor(invoker);
213
+ await triggerFocusFor(_invokerNode);
214
+ await triggerBlurFor(_invokerNode);
239
215
  expect(el.touched).to.be.true;
240
216
  });
241
217
  });
@@ -252,21 +228,21 @@ describe('lion-select-rich interactions', () => {
252
228
  </lion-select-rich>
253
229
  `)
254
230
  );
255
- const { invoker } = getNodes(el);
231
+ const { _invokerNode } = getSelectRichMembers(el);
256
232
  const options = el.formElements;
257
233
  await el.feedbackComplete;
258
234
  await el.updateComplete;
259
- expect(invoker.getAttribute('aria-invalid')).to.equal('false');
235
+ expect(_invokerNode.getAttribute('aria-invalid')).to.equal('false');
260
236
 
261
237
  options[0].checked = true;
262
238
  await el.feedbackComplete;
263
239
  await el.updateComplete;
264
- expect(invoker.getAttribute('aria-invalid')).to.equal('true');
240
+ expect(_invokerNode.getAttribute('aria-invalid')).to.equal('true');
265
241
 
266
242
  options[1].checked = true;
267
243
  await el.feedbackComplete;
268
244
  await el.updateComplete;
269
- expect(invoker.getAttribute('aria-invalid')).to.equal('false');
245
+ expect(_invokerNode.getAttribute('aria-invalid')).to.equal('false');
270
246
  });
271
247
  });
272
248
  });
@@ -1,6 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
2
  import { LionOption } from '@lion/ui/listbox.js';
3
- import { getListboxMembers } from '@lion/ui/listbox-test-helpers.js';
4
3
  import { OverlayController } from '@lion/ui/overlays.js';
5
4
  import { mimicClick } from '@lion/ui/overlays-test-helpers.js';
6
5
  import { LionSelectInvoker, LionSelectRich } from '@lion/ui/select-rich.js';
@@ -17,6 +16,7 @@ import {
17
16
  nextFrame,
18
17
  unsafeStatic,
19
18
  } from '@open-wc/testing';
19
+ import { getSelectRichMembers } from '@lion/ui/select-rich-test-helpers.js';
20
20
 
21
21
  /**
22
22
  * @typedef {import('../../listbox/src/LionOptions.js').LionOptions} LionOptions
@@ -24,19 +24,6 @@ import {
24
24
  * @typedef {import('../../form-core/types/FormControlMixinTypes.js').FormControlHost} FormControlHost
25
25
  */
26
26
 
27
- /**
28
- * @param { LionSelectRich } el
29
- */
30
- function getSelectRichMembers(el) {
31
- const obj = getListboxMembers(el);
32
- // eslint-disable-next-line no-return-assign
33
- return {
34
- ...obj,
35
- // @ts-ignore [allow-protected] in test
36
- ...{ _invokerNode: el._invokerNode, _overlayCtrl: el._overlayCtrl },
37
- };
38
- }
39
-
40
27
  /**
41
28
  * @typedef {import('lit').TemplateResult} TemplateResult
42
29
  */
@@ -329,6 +316,21 @@ describe('lion-select-rich', () => {
329
316
  expect(elSingleoption.opened).to.be.false;
330
317
  });
331
318
 
319
+ it('stays closed with [ArrowUp] or [ArrowDown] key in readonly mode', async () => {
320
+ const elReadOnly = await fixture(html`
321
+ <lion-select-rich readonly>
322
+ <lion-option .choiceValue=${10}>Item 1</lion-option>
323
+ <lion-option .choiceValue=${20} checked>Item 2</lion-option>
324
+ </lion-select-rich>
325
+ `);
326
+
327
+ elReadOnly.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown' }));
328
+ expect(elReadOnly.opened).to.be.false;
329
+
330
+ elReadOnly.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowUp' }));
331
+ expect(elReadOnly.opened).to.be.false;
332
+ });
333
+
332
334
  it('sets inheritsReferenceWidth to min by default', async () => {
333
335
  const el = await fixture(html`
334
336
  <lion-select-rich name="favoriteColor" label="Favorite color">
@@ -0,0 +1,18 @@
1
+ import { getListboxMembers } from '@lion/ui/listbox-test-helpers.js';
2
+
3
+ /**
4
+ * @typedef {import('../src/LionSelectRich.js').LionSelectRich} LionSelectRich
5
+ */
6
+
7
+ /**
8
+ * @param { LionSelectRich } el
9
+ */
10
+ export function getSelectRichMembers(el) {
11
+ const obj = getListboxMembers(el);
12
+ // eslint-disable-next-line no-return-assign
13
+ return {
14
+ ...obj,
15
+ // @ts-ignore [allow-protected] in test
16
+ ...{ _invokerNode: el._invokerNode, _overlayCtrl: el._overlayCtrl },
17
+ };
18
+ }