@lion/ui 0.6.1 → 0.7.1

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 (166) 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/helpers/src/SbActionLogger.js +1 -0
  4. package/components/input-amount/src/parsers.js +11 -3
  5. package/components/input-amount/test/formatters.test.js +11 -0
  6. package/components/input-amount/test/parsers.test.js +32 -4
  7. package/components/input-date/src/LionInputDate.js +1 -1
  8. package/components/input-tel-dropdown/src/LionInputTelDropdown.js +1 -13
  9. package/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js +2 -0
  10. package/components/input-tel-dropdown/types/index.ts +1 -0
  11. package/components/localize/src/date/getDateFormatBasedOnLocale.js +4 -3
  12. package/components/localize/src/date/parseDate.js +7 -2
  13. package/components/localize/src/date/utils/sanitizedDateTimeFormat.js +3 -2
  14. package/components/localize/src/number/formatNumber.js +1 -1
  15. package/components/localize/test/date/getDateFormatBasedOnLocale.test.js +6 -2
  16. package/components/localize/test/date/parseDate.test.js +8 -1
  17. package/components/localize/test/number/formatNumber.test.js +9 -0
  18. package/components/overlays/test/local-positioning.test.js +13 -21
  19. package/components/overlays/test/utils-tests/contain-focus.test.js +2 -2
  20. package/components/overlays/test/utils-tests/deep-contains.test.js +3 -3
  21. package/components/overlays/test/utils-tests/get-focusable-elements.test.js +1 -1
  22. package/components/select-rich/src/LionSelectRich.js +8 -1
  23. package/components/select-rich/test/lion-select-rich-interaction.test.js +27 -41
  24. package/components/select-rich/test/lion-select-rich.test.js +16 -14
  25. package/components/select-rich/test-helpers/getSelectRichMembers.js +18 -0
  26. package/custom-elements.json +531 -463
  27. package/dist-types/components/core/src/browserDetection.d.ts +3 -3
  28. package/dist-types/components/core/src/types.d.ts +1 -1
  29. package/dist-types/components/core/src/types.d.ts.map +1 -1
  30. package/dist-types/components/core/types/DisabledMixinTypes.d.ts +1 -1
  31. package/dist-types/components/core/types/DisabledMixinTypes.d.ts.map +1 -1
  32. package/dist-types/components/core/types/DisabledWithTabIndexMixinTypes.d.ts +1 -1
  33. package/dist-types/components/core/types/DisabledWithTabIndexMixinTypes.d.ts.map +1 -1
  34. package/dist-types/components/core/types/SlotMixinTypes.d.ts +4 -4
  35. package/dist-types/components/core/types/SlotMixinTypes.d.ts.map +1 -1
  36. package/dist-types/components/form-core/src/registration/FormControlsCollection.d.ts.map +1 -1
  37. package/dist-types/components/form-core/types/FocusMixinTypes.d.ts +1 -1
  38. package/dist-types/components/form-core/types/FocusMixinTypes.d.ts.map +1 -1
  39. package/dist-types/components/form-core/types/FormControlMixinTypes.d.ts +2 -2
  40. package/dist-types/components/form-core/types/FormControlMixinTypes.d.ts.map +1 -1
  41. package/dist-types/components/form-core/types/FormatMixinTypes.d.ts +2 -2
  42. package/dist-types/components/form-core/types/FormatMixinTypes.d.ts.map +1 -1
  43. package/dist-types/components/form-core/types/InteractionStateMixinTypes.d.ts +2 -2
  44. package/dist-types/components/form-core/types/InteractionStateMixinTypes.d.ts.map +1 -1
  45. package/dist-types/components/form-core/types/NativeTextFieldMixinTypes.d.ts +1 -1
  46. package/dist-types/components/form-core/types/NativeTextFieldMixinTypes.d.ts.map +1 -1
  47. package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts +1 -1
  48. package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts.map +1 -1
  49. package/dist-types/components/form-core/types/choice-group/ChoiceInputMixinTypes.d.ts +1 -1
  50. package/dist-types/components/form-core/types/choice-group/ChoiceInputMixinTypes.d.ts.map +1 -1
  51. package/dist-types/components/form-core/types/choice-group/CustomChoiceGroupMixinTypes.d.ts +1 -1
  52. package/dist-types/components/form-core/types/choice-group/CustomChoiceGroupMixinTypes.d.ts.map +1 -1
  53. package/dist-types/components/form-core/types/form-group/FormGroupMixinTypes.d.ts +1 -1
  54. package/dist-types/components/form-core/types/form-group/FormGroupMixinTypes.d.ts.map +1 -1
  55. package/dist-types/components/form-core/types/registration/FormRegisteringMixinTypes.d.ts +1 -1
  56. package/dist-types/components/form-core/types/registration/FormRegisteringMixinTypes.d.ts.map +1 -1
  57. package/dist-types/components/form-core/types/registration/FormRegistrarMixinTypes.d.ts +1 -1
  58. package/dist-types/components/form-core/types/registration/FormRegistrarMixinTypes.d.ts.map +1 -1
  59. package/dist-types/components/form-core/types/registration/FormRegistrarPortalMixinTypes.d.ts +1 -1
  60. package/dist-types/components/form-core/types/registration/FormRegistrarPortalMixinTypes.d.ts.map +1 -1
  61. package/dist-types/components/form-core/types/utils/SyncUpdatableMixinTypes.d.ts +2 -2
  62. package/dist-types/components/form-core/types/utils/SyncUpdatableMixinTypes.d.ts.map +1 -1
  63. package/dist-types/components/form-core/types/validate/ValidateMixinTypes.d.ts +5 -5
  64. package/dist-types/components/form-core/types/validate/ValidateMixinTypes.d.ts.map +1 -1
  65. package/dist-types/components/form-core/types/validate/validate.d.ts +5 -5
  66. package/dist-types/components/form-core/types/validate/validate.d.ts.map +1 -1
  67. package/dist-types/components/helpers/src/SbActionLogger.d.ts.map +1 -1
  68. package/dist-types/components/input-amount/src/parsers.d.ts.map +1 -1
  69. package/dist-types/components/input-date/src/LionInputDate.d.ts.map +1 -1
  70. package/dist-types/components/input-file/types/input-file.d.ts +4 -4
  71. package/dist-types/components/input-file/types/input-file.d.ts.map +1 -1
  72. package/dist-types/components/input-stepper/src/LionInputStepper.d.ts +0 -1
  73. package/dist-types/components/input-stepper/src/LionInputStepper.d.ts.map +1 -1
  74. package/dist-types/components/input-tel/types/index.d.ts +1 -1
  75. package/dist-types/components/input-tel/types/index.d.ts.map +1 -1
  76. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
  77. package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.d.ts.map +1 -1
  78. package/dist-types/components/input-tel-dropdown/types/index.d.ts +6 -5
  79. package/dist-types/components/input-tel-dropdown/types/index.d.ts.map +1 -1
  80. package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts +1 -1
  81. package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts.map +1 -1
  82. package/dist-types/components/localize/src/date/getDateFormatBasedOnLocale.d.ts +2 -1
  83. package/dist-types/components/localize/src/date/getDateFormatBasedOnLocale.d.ts.map +1 -1
  84. package/dist-types/components/localize/src/date/parseDate.d.ts +3 -1
  85. package/dist-types/components/localize/src/date/parseDate.d.ts.map +1 -1
  86. package/dist-types/components/localize/src/date/utils/sanitizedDateTimeFormat.d.ts +2 -1
  87. package/dist-types/components/localize/src/date/utils/sanitizedDateTimeFormat.d.ts.map +1 -1
  88. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts +4 -4
  89. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts.map +1 -1
  90. package/dist-types/components/overlays/src/utils/adopt-styles.d.ts +6 -8
  91. package/dist-types/components/overlays/src/utils/adopt-styles.d.ts.map +1 -1
  92. package/dist-types/components/overlays/types/ArrowMixinTypes.d.ts +1 -1
  93. package/dist-types/components/overlays/types/ArrowMixinTypes.d.ts.map +1 -1
  94. package/dist-types/components/overlays/types/OverlayConfig.d.ts +1 -1
  95. package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
  96. package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +1 -1
  97. package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
  98. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +1 -0
  99. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  100. package/dist-types/components/select-rich/test/lion-select-rich-interaction.test.d.ts.map +1 -1
  101. package/dist-types/components/select-rich/test/lion-select-rich.test.d.ts.map +1 -1
  102. package/dist-types/components/select-rich/test-helpers/getSelectRichMembers.d.ts +19 -0
  103. package/dist-types/components/select-rich/test-helpers/getSelectRichMembers.d.ts.map +1 -0
  104. package/dist-types/exports/select-rich-test-helpers.d.ts +2 -0
  105. package/dist-types/exports/select-rich-test-helpers.d.ts.map +1 -0
  106. package/dist-types/tsconfig.tsbuildinfo +1 -1
  107. package/docs/components/checkbox-group/overview.md +2 -2
  108. package/docs/components/collapsible/overview.md +1 -1
  109. package/docs/components/combobox/overview.md +1 -1
  110. package/docs/components/combobox/src/gh-combobox/gh-combobox.js +1 -1
  111. package/docs/components/combobox/src/google-combobox/google-combobox.js +1 -1
  112. package/docs/components/combobox/use-cases.md +1 -1
  113. package/docs/components/dialog/overview.md +1 -1
  114. package/docs/components/fieldset/overview.md +5 -5
  115. package/docs/components/form/overview.md +3 -3
  116. package/docs/components/icon/use-cases.md +1 -1
  117. package/docs/components/input/overview.md +1 -1
  118. package/docs/components/input-amount/overview.md +4 -4
  119. package/docs/components/input-date/overview.md +5 -5
  120. package/docs/components/input-datepicker/overview.md +5 -5
  121. package/docs/components/input-email/overview.md +2 -2
  122. package/docs/components/input-file/overview.md +1 -1
  123. package/docs/components/input-iban/overview.md +2 -2
  124. package/docs/components/input-range/overview.md +2 -2
  125. package/docs/components/input-stepper/overview.md +1 -1
  126. package/docs/components/input-tel/overview.md +1 -1
  127. package/docs/components/input-tel/use-cases.md +6 -6
  128. package/docs/components/input-tel-dropdown/overview.md +1 -1
  129. package/docs/components/input-tel-dropdown/src/intl-input-tel-dropdown.js +1 -0
  130. package/docs/components/listbox/use-cases.md +1 -1
  131. package/docs/components/radio-group/overview.md +3 -3
  132. package/docs/components/select/use-cases.md +1 -1
  133. package/docs/components/select-rich/use-cases.md +1 -1
  134. package/docs/components/tabs/examples.md +1 -1
  135. package/docs/fundamentals/index.md +4 -4
  136. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  137. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  138. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  139. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  140. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  141. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  142. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  143. package/docs/fundamentals/rationales/accessibility.md +1 -1
  144. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  145. package/docs/fundamentals/systems/core/overview.md +1 -1
  146. package/docs/fundamentals/systems/form/assets/h-output.js +1 -0
  147. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  148. package/docs/fundamentals/systems/form/interaction-states.md +6 -0
  149. package/docs/fundamentals/systems/form/model-value.md +10 -1
  150. package/docs/fundamentals/systems/form/overview.md +28 -28
  151. package/docs/fundamentals/systems/form/validate.md +2 -2
  152. package/docs/fundamentals/systems/localize/overview.md +4 -4
  153. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  154. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  155. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  156. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  157. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  158. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  159. package/docs/guides/how-to/create-a-form.md +4 -4
  160. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  161. package/docs/guides/index.md +8 -8
  162. package/docs/guides/principles/index.md +5 -5
  163. package/docs/guides/principles/styling.md +1 -1
  164. package/exports/select-rich-test-helpers.js +1 -0
  165. package/package.json +1 -1
  166. 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;
@@ -126,6 +126,7 @@ export class SbActionLogger extends LitElement {
126
126
 
127
127
  constructor() {
128
128
  super();
129
+ // eslint-disable-next-line wc/no-constructor-attributes
129
130
  this.title = 'Action Logger';
130
131
  this.simple = false;
131
132
  this.__logCounter = 0;
@@ -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
  });
@@ -21,7 +21,7 @@ export class LionInputDate extends LocalizeMixin(LionInput) {
21
21
  /**
22
22
  * @param {string} value
23
23
  */
24
- this.parser = value => (value === '' ? undefined : parseDate(value));
24
+ this.parser = value => (value === '' ? undefined : parseDate(value, this.formatOptions));
25
25
  this.formatter = formatDate;
26
26
  this.defaultValidators.push(new IsDate());
27
27
  // Just explicitly make clear we shouldn't use type 'date'
@@ -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;
@@ -3,9 +3,10 @@ import { splitDate } from './utils/splitDate.js';
3
3
 
4
4
  /**
5
5
  * To compute the localized date format
6
+ * @param {string} [locale]
6
7
  * @returns {string}
7
8
  */
8
- export function getDateFormatBasedOnLocale() {
9
+ export function getDateFormatBasedOnLocale(locale) {
9
10
  /**
10
11
  *
11
12
  * @param {ArrayLike.<string>} dateParts
@@ -35,9 +36,9 @@ export function getDateFormatBasedOnLocale() {
35
36
  date.setDate(20);
36
37
  date.setMonth(11);
37
38
  date.setFullYear(2012);
38
-
39
+ const options = locale ? { locale } : {};
39
40
  // Strange characters added by IE11 need to be taken into account here
40
- const formattedDate = sanitizedDateTimeFormat(date);
41
+ const formattedDate = sanitizedDateTimeFormat(date, options);
41
42
 
42
43
  // For Dutch locale, dateParts would match: [ 1:'20', 2:'-', 3:'12', 4:'-', 5:'2012' ]
43
44
  const dateParts = splitDate(formattedDate);
@@ -2,6 +2,10 @@ import { getDateFormatBasedOnLocale } from './getDateFormatBasedOnLocale.js';
2
2
  import { getLocalizeManager } from '../getLocalizeManager.js';
3
3
  import { addLeadingZero } from './utils/addLeadingZero.js';
4
4
 
5
+ /**
6
+ * @typedef {import('../../types/LocalizeMixinTypes.js').FormatDateOptions} FormatDateOptions
7
+ */
8
+
5
9
  /**
6
10
  * @param {function} fn
7
11
  */
@@ -26,15 +30,16 @@ const memoizedGetDateFormatBasedOnLocale = memoize(getDateFormatBasedOnLocale);
26
30
  * To parse a date into the right format
27
31
  *
28
32
  * @param {string} dateString
33
+ * @param {FormatDateOptions} [options] Intl options are available
29
34
  * @returns {Date | undefined}
30
35
  */
31
- export function parseDate(dateString) {
36
+ export function parseDate(dateString, options) {
32
37
  const localizeManager = getLocalizeManager();
33
38
 
34
39
  const stringToParse = addLeadingZero(dateString);
35
40
  let parsedString;
36
41
 
37
- switch (memoizedGetDateFormatBasedOnLocale(localizeManager.locale)) {
42
+ switch (memoizedGetDateFormatBasedOnLocale(options?.locale || localizeManager.locale)) {
38
43
  case 'day-month-year':
39
44
  parsedString = `${stringToParse.slice(6, 10)}/${stringToParse.slice(
40
45
  3,
@@ -5,9 +5,10 @@ import { clean } from './clean.js';
5
5
  * To sanitize a date from IE11 handling
6
6
  *
7
7
  * @param {Date} date
8
+ * @param {import('../../../types/LocalizeMixinTypes.js').FormatDateOptions} [options] Intl options are available
8
9
  * @returns {string}
9
10
  */
10
- export function sanitizedDateTimeFormat(date) {
11
- const fDate = formatDate(date);
11
+ export function sanitizedDateTimeFormat(date, options) {
12
+ const fDate = formatDate(date, options);
12
13
  return clean(fDate);
13
14
  }
@@ -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;
@@ -7,15 +7,19 @@ import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
7
7
 
8
8
  describe('getDateFormatBasedOnLocale()', () => {
9
9
  const localizeManager = getLocalizeManager();
10
-
11
10
  beforeEach(() => {
12
11
  localizeTearDown();
13
12
  });
14
13
 
15
- it('returns the positions of day, month and year', async () => {
14
+ it('returns the positions of day, month and year with locale set globally', async () => {
16
15
  localizeManager.locale = 'en-GB';
17
16
  expect(getDateFormatBasedOnLocale()).to.equal('day-month-year');
18
17
  localizeManager.locale = 'en-US';
19
18
  expect(getDateFormatBasedOnLocale()).to.equal('month-day-year');
20
19
  });
20
+
21
+ it('returns the positions of day, month and year with given locale', async () => {
22
+ expect(getDateFormatBasedOnLocale('en-GB')).to.equal('day-month-year');
23
+ expect(getDateFormatBasedOnLocale('en-US')).to.equal('month-day-year');
24
+ });
21
25
  });
@@ -51,13 +51,20 @@ describe('parseDate()', () => {
51
51
  expect(equalsDate(parseDate('14.12.1976 r.'), new Date('1976/12/14'))).to.equal(true);
52
52
  });
53
53
 
54
- it('handles different locales', () => {
54
+ it('handles different locales globally set', () => {
55
55
  localizeManager.locale = 'en-GB';
56
56
  expect(equalsDate(parseDate('31-12-1976'), new Date('1976/12/31'))).to.equal(true);
57
57
  localizeManager.locale = 'en-US';
58
58
  expect(equalsDate(parseDate('12-31-1976'), new Date('1976/12/31'))).to.equal(true);
59
59
  });
60
60
 
61
+ it('handles different locales as option', () => {
62
+ const optionEnGb = { locale: 'en-GB' };
63
+ expect(equalsDate(parseDate('31-12-1976', optionEnGb), new Date('1976/12/31'))).to.equal(true);
64
+ const optionEnUs = { locale: 'en-US' };
65
+ expect(equalsDate(parseDate('12-31-1976', optionEnUs), new Date('1976/12/31'))).to.equal(true);
66
+ });
67
+
61
68
  it('handles timezones of the browser and parsed date correctly', () => {
62
69
  const referenceDate = new Date('2020/01/30');
63
70
  localizeManager.locale = 'nl-NL';
@@ -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
 
@@ -485,6 +485,13 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
485
485
  return;
486
486
  }
487
487
 
488
+ this._isHandlingUserInput = true;
489
+ setTimeout(() => {
490
+ // Since we can't control when subclasses are done handling keyboard input, we
491
+ // schedule a timeout to reset _isHandlingUserInput
492
+ this._isHandlingUserInput = false;
493
+ });
494
+
488
495
  const { key } = ev;
489
496
  switch (key) {
490
497
  case 'ArrowUp':