@lion/ui 0.13.1 → 0.14.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 (113) hide show
  1. package/components/input-amount-dropdown/src/LionInputAmountDropdown.js +554 -0
  2. package/components/input-amount-dropdown/src/currencyUtil.js +324 -0
  3. package/components/input-amount-dropdown/src/formatters.js +24 -0
  4. package/components/input-amount-dropdown/src/localizeNamespaceLoader.js +7 -0
  5. package/components/input-amount-dropdown/src/parsers.js +23 -0
  6. package/components/input-amount-dropdown/src/serializers.js +24 -0
  7. package/components/input-amount-dropdown/src/validators.js +22 -0
  8. package/components/input-amount-dropdown/test/LionInputAmountDropdown.test.js +200 -0
  9. package/components/input-amount-dropdown/test-helpers/mimicUserChangingDropdown.js +22 -0
  10. package/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.js +321 -0
  11. package/components/input-amount-dropdown/translations/en.js +5 -0
  12. package/components/input-amount-dropdown/types/index.ts +234 -0
  13. package/components/input-tel/types/index.ts +6 -0
  14. package/components/localize/src/number/getFractionDigits.js +1 -1
  15. package/custom-elements.json +5479 -1421
  16. package/dist-types/components/input-amount-dropdown/src/LionInputAmountDropdown.d.ts +221 -0
  17. package/dist-types/components/input-amount-dropdown/src/LionInputAmountDropdown.d.ts.map +1 -0
  18. package/dist-types/components/input-amount-dropdown/src/currencyUtil.d.ts +24 -0
  19. package/dist-types/components/input-amount-dropdown/src/currencyUtil.d.ts.map +1 -0
  20. package/dist-types/components/input-amount-dropdown/src/formatters.d.ts +4 -0
  21. package/dist-types/components/input-amount-dropdown/src/formatters.d.ts.map +1 -0
  22. package/dist-types/components/input-amount-dropdown/src/localizeNamespaceLoader.d.ts +2 -0
  23. package/dist-types/components/input-amount-dropdown/src/localizeNamespaceLoader.d.ts.map +1 -0
  24. package/dist-types/components/input-amount-dropdown/src/parsers.d.ts +2 -0
  25. package/dist-types/components/input-amount-dropdown/src/parsers.d.ts.map +1 -0
  26. package/dist-types/components/input-amount-dropdown/src/serializers.d.ts +3 -0
  27. package/dist-types/components/input-amount-dropdown/src/serializers.d.ts.map +1 -0
  28. package/dist-types/components/input-amount-dropdown/src/validators.d.ts +12 -0
  29. package/dist-types/components/input-amount-dropdown/src/validators.d.ts.map +1 -0
  30. package/dist-types/components/input-amount-dropdown/test/LionInputAmountDropdown.test.d.ts +7 -0
  31. package/dist-types/components/input-amount-dropdown/test/LionInputAmountDropdown.test.d.ts.map +1 -0
  32. package/dist-types/components/input-amount-dropdown/test-helpers/mimicUserChangingDropdown.d.ts +12 -0
  33. package/dist-types/components/input-amount-dropdown/test-helpers/mimicUserChangingDropdown.d.ts.map +1 -0
  34. package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts +13 -0
  35. package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts.map +1 -0
  36. package/dist-types/components/input-amount-dropdown/translations/en.d.ts +7 -0
  37. package/dist-types/components/input-amount-dropdown/translations/en.d.ts.map +1 -0
  38. package/dist-types/components/input-amount-dropdown/types/index.d.ts +86 -0
  39. package/dist-types/components/input-amount-dropdown/types/index.d.ts.map +1 -0
  40. package/dist-types/components/input-tel/types/index.d.ts +1 -1
  41. package/dist-types/components/input-tel/types/index.d.ts.map +1 -1
  42. package/dist-types/components/localize/src/number/getFractionDigits.d.ts +1 -1
  43. package/dist-types/exports/define/lion-input-amount-dropdown.d.ts +2 -0
  44. package/dist-types/exports/define/lion-input-amount-dropdown.d.ts.map +1 -0
  45. package/dist-types/exports/input-amount-dropdown-test-helpers.d.ts +2 -0
  46. package/dist-types/exports/input-amount-dropdown-test-helpers.d.ts.map +1 -0
  47. package/dist-types/exports/input-amount-dropdown-test-suites.d.ts +2 -0
  48. package/dist-types/exports/input-amount-dropdown-test-suites.d.ts.map +1 -0
  49. package/dist-types/exports/input-amount-dropdown.d.ts +2 -0
  50. package/dist-types/exports/input-amount-dropdown.d.ts.map +1 -0
  51. package/dist-types/exports/types/input-amount-dropdown.d.ts +2 -0
  52. package/dist-types/exports/types/input-amount-dropdown.d.ts.map +1 -0
  53. package/dist-types/tsconfig.tsbuildinfo +1 -1
  54. package/docs/components/checkbox-group/overview.md +2 -2
  55. package/docs/components/collapsible/overview.md +1 -1
  56. package/docs/components/combobox/overview.md +1 -1
  57. package/docs/components/combobox/use-cases.md +1 -1
  58. package/docs/components/dialog/overview.md +1 -1
  59. package/docs/components/fieldset/overview.md +5 -5
  60. package/docs/components/form/overview.md +3 -3
  61. package/docs/components/form/use-cases.md +2 -2
  62. package/docs/components/icon/use-cases.md +1 -1
  63. package/docs/components/input/overview.md +1 -1
  64. package/docs/components/input-amount/overview.md +3 -3
  65. package/docs/components/input-amount-dropdown/api-table.md +210 -0
  66. package/docs/components/input-amount-dropdown/index.md +13 -0
  67. package/docs/components/input-amount-dropdown/use-cases.md +103 -0
  68. package/docs/components/input-date/overview.md +3 -3
  69. package/docs/components/input-datepicker/overview.md +5 -5
  70. package/docs/components/input-email/overview.md +2 -2
  71. package/docs/components/input-file/overview.md +1 -1
  72. package/docs/components/input-iban/overview.md +2 -2
  73. package/docs/components/input-range/overview.md +2 -2
  74. package/docs/components/input-stepper/overview.md +1 -1
  75. package/docs/components/input-tel/overview.md +1 -1
  76. package/docs/components/input-tel/use-cases.md +6 -6
  77. package/docs/components/input-tel-dropdown/overview.md +1 -1
  78. package/docs/components/listbox/use-cases.md +1 -1
  79. package/docs/components/radio-group/overview.md +3 -3
  80. package/docs/components/select/use-cases.md +1 -1
  81. package/docs/components/select-rich/use-cases.md +1 -1
  82. package/docs/components/tabs/examples.md +1 -1
  83. package/docs/fundamentals/index.md +4 -4
  84. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  85. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  86. package/docs/fundamentals/rationales/accessibility.md +1 -1
  87. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  88. package/docs/fundamentals/systems/core/overview.md +1 -1
  89. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  90. package/docs/fundamentals/systems/form/model-value.md +1 -1
  91. package/docs/fundamentals/systems/form/overview.md +28 -28
  92. package/docs/fundamentals/systems/form/validate.md +2 -2
  93. package/docs/fundamentals/systems/localize/overview.md +4 -4
  94. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  95. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  96. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  97. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  98. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  99. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  100. package/docs/guides/how-to/create-a-form.md +4 -4
  101. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  102. package/docs/guides/index.md +8 -8
  103. package/docs/guides/principles/index.md +5 -5
  104. package/docs/guides/principles/styling.md +1 -1
  105. package/exports/define/lion-input-amount-dropdown.js +3 -0
  106. package/exports/input-amount-dropdown-test-helpers.js +1 -0
  107. package/exports/input-amount-dropdown-test-suites.js +1 -0
  108. package/exports/input-amount-dropdown.js +1 -0
  109. package/exports/types/input-amount-dropdown.ts +9 -0
  110. package/input-amount-dropdown-test-helpers.js +1 -0
  111. package/input-amount-dropdown-test-suites.js +1 -0
  112. package/input-amount-dropdown.js +1 -0
  113. package/package.json +2 -1
@@ -0,0 +1,324 @@
1
+ /**
2
+ * This country to currency list was exported from Java.
3
+ * Java contains a country:currency map according to the i18n spec, but JS does not.
4
+ *
5
+ * @type {import("../types/index.js").countryToCurrencyList}
6
+ *
7
+ * @example
8
+ * // The following Java code can be used to export the countryToCurrencyList
9
+ *
10
+ * import java.util.Currency;
11
+ * import java.util.Locale;
12
+ *
13
+ * public class Main {
14
+ * public static void main(String[] args) {
15
+ * // get all ISO countries
16
+ * String[] ISOCountryCodes = Locale.getISOCountries();
17
+ *
18
+ * // loop over all the countries.
19
+ * for (String country : ISOCountryCodes) {
20
+ * try {
21
+ * // get the locale for said country
22
+ * Locale locale = new Locale("", country);
23
+ *
24
+ * // creates a Currency instance which has a bunch of standardized information.
25
+ * // from that class we can get the currency linked to the country.
26
+ * String currencyCode = Currency.getInstance(locale).getCurrencyCode();
27
+ *
28
+ * // prints to the console, which can be copied to update the map if changes occurred
29
+ * String output = country + ": '" + currencyCode + "',\n";
30
+ * System.out.print(output);
31
+ * } catch (Exception e) {
32
+ * }
33
+ * }
34
+ * }
35
+ * }
36
+ */
37
+ const countryToCurrencyList = {
38
+ AD: 'EUR',
39
+ AE: 'AED',
40
+ AF: 'AFN',
41
+ AG: 'XCD',
42
+ AI: 'XCD',
43
+ AL: 'ALL',
44
+ AM: 'AMD',
45
+ AO: 'AOA',
46
+ AR: 'ARS',
47
+ AS: 'USD',
48
+ AT: 'EUR',
49
+ AU: 'AUD',
50
+ AW: 'AWG',
51
+ AX: 'EUR',
52
+ AZ: 'AZN',
53
+ BA: 'BAM',
54
+ BB: 'BBD',
55
+ BD: 'BDT',
56
+ BE: 'EUR',
57
+ BF: 'XOF',
58
+ BG: 'BGN',
59
+ BH: 'BHD',
60
+ BI: 'BIF',
61
+ BJ: 'XOF',
62
+ BL: 'EUR',
63
+ BM: 'BMD',
64
+ BN: 'BND',
65
+ BO: 'BOB',
66
+ BQ: 'USD',
67
+ BR: 'BRL',
68
+ BS: 'BSD',
69
+ BT: 'BTN',
70
+ BV: 'NOK',
71
+ BW: 'BWP',
72
+ BY: 'BYN',
73
+ BZ: 'BZD',
74
+ CA: 'CAD',
75
+ CC: 'AUD',
76
+ CD: 'CDF',
77
+ CF: 'XAF',
78
+ CG: 'XAF',
79
+ CH: 'CHF',
80
+ CI: 'XOF',
81
+ CK: 'NZD',
82
+ CL: 'CLP',
83
+ CM: 'XAF',
84
+ CN: 'CNY',
85
+ CO: 'COP',
86
+ CR: 'CRC',
87
+ CU: 'CUP',
88
+ CV: 'CVE',
89
+ CW: 'XCG',
90
+ CX: 'AUD',
91
+ CY: 'EUR',
92
+ CZ: 'CZK',
93
+ DE: 'EUR',
94
+ DJ: 'DJF',
95
+ DK: 'DKK',
96
+ DM: 'XCD',
97
+ DO: 'DOP',
98
+ DZ: 'DZD',
99
+ EC: 'USD',
100
+ EE: 'EUR',
101
+ EG: 'EGP',
102
+ EH: 'MAD',
103
+ ER: 'ERN',
104
+ ES: 'EUR',
105
+ ET: 'ETB',
106
+ FI: 'EUR',
107
+ FJ: 'FJD',
108
+ FK: 'FKP',
109
+ FM: 'USD',
110
+ FO: 'DKK',
111
+ FR: 'EUR',
112
+ GA: 'XAF',
113
+ GB: 'GBP',
114
+ GD: 'XCD',
115
+ GE: 'GEL',
116
+ GF: 'EUR',
117
+ GG: 'GBP',
118
+ GH: 'GHS',
119
+ GI: 'GIP',
120
+ GL: 'DKK',
121
+ GM: 'GMD',
122
+ GN: 'GNF',
123
+ GP: 'EUR',
124
+ GQ: 'XAF',
125
+ GR: 'EUR',
126
+ GS: 'GBP',
127
+ GT: 'GTQ',
128
+ GU: 'USD',
129
+ GW: 'XOF',
130
+ GY: 'GYD',
131
+ HK: 'HKD',
132
+ HM: 'AUD',
133
+ HN: 'HNL',
134
+ HR: 'EUR',
135
+ HT: 'HTG',
136
+ HU: 'HUF',
137
+ ID: 'IDR',
138
+ IE: 'EUR',
139
+ IL: 'ILS',
140
+ IM: 'GBP',
141
+ IN: 'INR',
142
+ IO: 'USD',
143
+ IQ: 'IQD',
144
+ IR: 'IRR',
145
+ IS: 'ISK',
146
+ IT: 'EUR',
147
+ JE: 'GBP',
148
+ JM: 'JMD',
149
+ JO: 'JOD',
150
+ JP: 'JPY',
151
+ KE: 'KES',
152
+ KG: 'KGS',
153
+ KH: 'KHR',
154
+ KI: 'AUD',
155
+ KM: 'KMF',
156
+ KN: 'XCD',
157
+ KP: 'KPW',
158
+ KR: 'KRW',
159
+ KW: 'KWD',
160
+ KY: 'KYD',
161
+ KZ: 'KZT',
162
+ LA: 'LAK',
163
+ LB: 'LBP',
164
+ LC: 'XCD',
165
+ LI: 'CHF',
166
+ LK: 'LKR',
167
+ LR: 'LRD',
168
+ LS: 'LSL',
169
+ LT: 'EUR',
170
+ LU: 'EUR',
171
+ LV: 'EUR',
172
+ LY: 'LYD',
173
+ MA: 'MAD',
174
+ MC: 'EUR',
175
+ MD: 'MDL',
176
+ ME: 'EUR',
177
+ MF: 'EUR',
178
+ MG: 'MGA',
179
+ MH: 'USD',
180
+ MK: 'MKD',
181
+ ML: 'XOF',
182
+ MM: 'MMK',
183
+ MN: 'MNT',
184
+ MO: 'MOP',
185
+ MP: 'USD',
186
+ MQ: 'EUR',
187
+ MR: 'MRU',
188
+ MS: 'XCD',
189
+ MT: 'EUR',
190
+ MU: 'MUR',
191
+ MV: 'MVR',
192
+ MW: 'MWK',
193
+ MX: 'MXN',
194
+ MY: 'MYR',
195
+ MZ: 'MZN',
196
+ NA: 'NAD',
197
+ NC: 'XPF',
198
+ NE: 'XOF',
199
+ NF: 'AUD',
200
+ NG: 'NGN',
201
+ NI: 'NIO',
202
+ NL: 'EUR',
203
+ NO: 'NOK',
204
+ NP: 'NPR',
205
+ NR: 'AUD',
206
+ NU: 'NZD',
207
+ NZ: 'NZD',
208
+ OM: 'OMR',
209
+ PA: 'PAB',
210
+ PE: 'PEN',
211
+ PF: 'XPF',
212
+ PG: 'PGK',
213
+ PH: 'PHP',
214
+ PK: 'PKR',
215
+ PL: 'PLN',
216
+ PM: 'EUR',
217
+ PN: 'NZD',
218
+ PR: 'USD',
219
+ PS: 'ILS',
220
+ PT: 'EUR',
221
+ PW: 'USD',
222
+ PY: 'PYG',
223
+ QA: 'QAR',
224
+ RE: 'EUR',
225
+ RO: 'RON',
226
+ RS: 'RSD',
227
+ RU: 'RUB',
228
+ RW: 'RWF',
229
+ SA: 'SAR',
230
+ SB: 'SBD',
231
+ SC: 'SCR',
232
+ SD: 'SDG',
233
+ SE: 'SEK',
234
+ SG: 'SGD',
235
+ SH: 'SHP',
236
+ SI: 'EUR',
237
+ SJ: 'NOK',
238
+ SK: 'EUR',
239
+ SL: 'SLE',
240
+ SM: 'EUR',
241
+ SN: 'XOF',
242
+ SO: 'SOS',
243
+ SR: 'SRD',
244
+ SS: 'SSP',
245
+ ST: 'STN',
246
+ SV: 'SVC',
247
+ SX: 'XCG',
248
+ SY: 'SYP',
249
+ SZ: 'SZL',
250
+ TC: 'USD',
251
+ TD: 'XAF',
252
+ TF: 'EUR',
253
+ TG: 'XOF',
254
+ TH: 'THB',
255
+ TJ: 'TJS',
256
+ TK: 'NZD',
257
+ TL: 'USD',
258
+ TM: 'TMT',
259
+ TN: 'TND',
260
+ TO: 'TOP',
261
+ TR: 'TRY',
262
+ TT: 'TTD',
263
+ TV: 'AUD',
264
+ TW: 'TWD',
265
+ TZ: 'TZS',
266
+ UA: 'UAH',
267
+ UG: 'UGX',
268
+ UM: 'USD',
269
+ US: 'USD',
270
+ UY: 'UYU',
271
+ UZ: 'UZS',
272
+ VA: 'EUR',
273
+ VC: 'XCD',
274
+ VE: 'VES',
275
+ VG: 'USD',
276
+ VI: 'USD',
277
+ VN: 'VND',
278
+ VU: 'VUV',
279
+ WF: 'XPF',
280
+ WS: 'WST',
281
+ YE: 'YER',
282
+ YT: 'EUR',
283
+ ZA: 'ZAR',
284
+ ZM: 'ZMW',
285
+ ZW: 'ZWG',
286
+ };
287
+
288
+ /**
289
+ * Map containing all countries and its corresponding currency, following the i18n standard.
290
+ * @type {import("../types/index.js").RegionToCurrencyMap}
291
+ */
292
+ const countryToCurrencyMap = /** @type {import("../types/index.js").RegionToCurrencyMap} */ (
293
+ new Map(Object.entries(countryToCurrencyList))
294
+ );
295
+
296
+ /**
297
+ * Set with all possible currencies derived from the i18n standard.
298
+ * @type {import("../types/index.js").AllCurrenciesSet}
299
+ */
300
+ const allCurrencies = new Set(Object.values(countryToCurrencyList).sort());
301
+
302
+ /**
303
+ * Matches a currency symbol to a currency code, with a given locale based on Intl.numberFormat.
304
+ * @param {import("./LionInputAmountDropdown.js").CurrencyCode} currency
305
+ * @param {string} locale
306
+ * @returns {string}
307
+ */
308
+ const getCurrencySymbol = (currency, locale) => {
309
+ if (!locale || !currency) {
310
+ return '';
311
+ }
312
+
313
+ return (
314
+ new Intl.NumberFormat(locale, { style: 'currency', currency })
315
+ .formatToParts(1)
316
+ .find(x => x.type === 'currency')?.value || ''
317
+ );
318
+ };
319
+
320
+ export const currencyUtil = {
321
+ countryToCurrencyMap,
322
+ allCurrencies,
323
+ getCurrencySymbol,
324
+ };
@@ -0,0 +1,24 @@
1
+ import { formatAmount as _formatAmount } from '@lion/ui/input-amount.js';
2
+ import { currencyUtil } from './currencyUtil.js';
3
+
4
+ /**
5
+ * @typedef {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberOptions} FormatOptions
6
+ * @typedef {import('../types/index.js').AmountDropdownModelValue} AmountDropdownModelValue
7
+ */
8
+
9
+ /**
10
+ * Formats a number considering the default fraction digits provided by Intl.
11
+ *
12
+ * @param {import('../types/index.js').AmountDropdownModelValue} modelValue to format
13
+ * @param {FormatOptions} [givenOptions]
14
+ * @param {AmountDropdownModelValue|undefined} [context]
15
+ */
16
+ export const formatAmount = (modelValue, givenOptions, context) => {
17
+ // @ts-expect-error - cannot cast string to CurrencyCode outside a TS file
18
+ if (currencyUtil.allCurrencies.has(modelValue?.currency) && context) {
19
+ // TODO: better way of setting parent currency
20
+ context.currency = modelValue?.currency;
21
+ }
22
+ // @ts-expect-error - cannot cast string to CurrencyCode outside a TS file
23
+ return _formatAmount(modelValue?.amount, givenOptions);
24
+ };
@@ -0,0 +1,7 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ export const localizeNamespaceLoader = /** @param {string} locale */ locale => {
3
+ switch (locale) {
4
+ default:
5
+ return import('@lion/ui/input-amount-dropdown-translations/en.js');
6
+ }
7
+ };
@@ -0,0 +1,23 @@
1
+ import { parseAmount as _parseAmount } from '@lion/ui/input-amount.js';
2
+
3
+ /**
4
+ * Uses `parseAmount()` to parse a number string and return the best possible javascript number.
5
+ * Rounds up the number with the correct amount of decimals according to the currency.
6
+ *
7
+ * @example
8
+ * parseAmount('1,234.56', {currency: 'EUR'}); => { amount: 1234.56, currency: 'EUR' }
9
+ * parseAmount('1,234.56', {currency: 'JPY'}); => { amount: 1235, currency: 'JPY' }
10
+ * parseAmount('1,234.56', {currency: 'JOD'}); => { amount: 1234.560, currency: 'JOD' }
11
+ *
12
+ * @param {string} value Number to be parsed
13
+ * @param {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberOptions} [givenOptions] Locale Options
14
+ * @returns {import('../types/index.js').AmountDropdownModelValue}
15
+ */
16
+ export const parseAmount = (value, givenOptions) => {
17
+ const parsedAmount = _parseAmount(value, givenOptions);
18
+
19
+ return {
20
+ amount: parsedAmount,
21
+ currency: givenOptions?.currency,
22
+ };
23
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Serializes the modelValue to the international standard notation.
3
+ * @param {import("../types/index.js").AmountDropdownModelValue} modelValue
4
+ * @returns {string}
5
+ */
6
+ export const serializer = modelValue =>
7
+ /**
8
+ * There seems to be some debate on a common international notation vs localization.
9
+ * The unwritten standard is, e.g: EUR 123
10
+ */
11
+ `${modelValue?.currency} ${modelValue?.amount}`;
12
+
13
+ /**
14
+ * Deserializes the serializedValue back to modelValue.
15
+ * @param {string} serializedValue
16
+ * @returns {import("../types/index.js").AmountDropdownModelValue}
17
+ */
18
+ export const deserializer = serializedValue => {
19
+ const [currency, amount] = serializedValue.split(' ');
20
+ return {
21
+ currency,
22
+ amount,
23
+ };
24
+ };
@@ -0,0 +1,22 @@
1
+ import { IsNumber, Validator } from '@lion/ui/form-core.js';
2
+ import { currencyUtil } from './currencyUtil.js';
3
+
4
+ /**
5
+ * @typedef {import('../../form-core/types/validate/validate.js').FeedbackMessageData} FeedbackMessageData
6
+ */
7
+
8
+ export class CurrencyAndAmount extends Validator {
9
+ static validatorName = 'CurrencyAndAmount';
10
+
11
+ /**
12
+ * @param {import('../types/index.js').AmountDropdownModelValue} modelValue amount and currency symbol
13
+ */
14
+ // eslint-disable-next-line class-methods-use-this
15
+ execute(modelValue) {
16
+ // @ts-expect-error - cannot cast string to CurrencyCode outside a TS file
17
+ const validCurrencyCode = currencyUtil.allCurrencies.has(modelValue?.currency);
18
+ const isNumber = new IsNumber().execute(modelValue.amount);
19
+
20
+ return validCurrencyCode && isNumber;
21
+ }
22
+ }
@@ -0,0 +1,200 @@
1
+ import { mimicUserChangingDropdown } from '@lion/ui/input-amount-dropdown-test-helpers.js';
2
+ import { runInputAmountDropdownSuite } from '@lion/ui/input-amount-dropdown-test-suites.js';
3
+ import { LionInputAmountDropdown } from '@lion/ui/input-amount-dropdown.js';
4
+ import { aTimeout, expect, fixture } from '@open-wc/testing';
5
+ import { LionSelectRich } from '@lion/ui/select-rich.js';
6
+ import { repeat } from 'lit/directives/repeat.js';
7
+ import { LionOption } from '@lion/ui/listbox.js';
8
+ import { ref } from 'lit/directives/ref.js';
9
+ import { html } from 'lit';
10
+
11
+ import { ifDefined } from 'lit/directives/if-defined.js';
12
+ import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
13
+ import { ScopedElementsMixin } from '../../core/src/ScopedElementsMixin.js';
14
+ import '@lion/ui/define/lion-input-amount-dropdown.js';
15
+
16
+ /**
17
+ * @typedef {import('../types/index.js').TemplateDataForDropdownInputAmount} TemplateDataForDropdownInputAmount
18
+ * @typedef {HTMLSelectElement|HTMLElement & {modelValue:string}} DropdownElement
19
+ * @typedef {import('../types/index.js').RegionMeta} RegionMeta
20
+ * @typedef {import('lit').TemplateResult} TemplateResult
21
+ */
22
+
23
+ class WithFormControlInputAmountDropdown extends ScopedElementsMixin(LionInputAmountDropdown) {
24
+ /**
25
+ * @configure ScopedElementsMixin
26
+ */
27
+ static scopedElements = {
28
+ ...super.scopedElements,
29
+ 'lion-select-rich': LionSelectRich,
30
+ 'lion-option': LionOption,
31
+ };
32
+
33
+ static templates = {
34
+ ...(super.templates || {}),
35
+ /**
36
+ * @param {TemplateDataForDropdownInputAmount} templateDataForDropdown
37
+ */
38
+ dropdown: templateDataForDropdown => {
39
+ const { refs, data } = templateDataForDropdown;
40
+ // TODO: once spread directive available, use it per ref (like ref(refs?.dropdown?.ref))
41
+ return html`
42
+ <lion-select-rich
43
+ ${ref(refs?.dropdown?.ref)}
44
+ label="${refs?.dropdown?.labels?.country}"
45
+ label-sr-only
46
+ @model-value-changed="${refs?.dropdown?.listeners['model-value-changed']}"
47
+ style="${ifDefined(refs?.dropdown?.props?.style)}"
48
+ >
49
+ ${repeat(
50
+ data.regionMetaList,
51
+ regionMeta => regionMeta.currencyCode,
52
+ regionMeta => html`
53
+ <lion-option .choiceValue="${regionMeta.currencyCode}"> </lion-option>
54
+ `,
55
+ )}
56
+ </lion-select-rich>
57
+ `;
58
+ },
59
+ };
60
+ }
61
+
62
+ runInputAmountDropdownSuite();
63
+
64
+ describe('WithFormControlInputAmountDropdown', () => {
65
+ // @ts-expect-error
66
+ // Runs it for LionSelectRich, which uses .modelValue/@model-value-changed instead of .value/@change
67
+ runInputAmountDropdownSuite({ klass: WithFormControlInputAmountDropdown });
68
+
69
+ it('focuses the textbox right after selection if selected via opened dropdown if interaction-mode is mac', async () => {
70
+ class InputAmountDropdownMac extends LionInputAmountDropdown {
71
+ static templates = {
72
+ ...(super.templates || {}),
73
+ /**
74
+ * @param {TemplateDataForDropdownInputAmount} templateDataForDropdown
75
+ */
76
+ dropdown: templateDataForDropdown => {
77
+ const { refs, data } = templateDataForDropdown;
78
+ // TODO: once spread directive available, use it per ref (like ref(refs?.dropdown?.ref))
79
+ return html`
80
+ <lion-select-rich
81
+ ${ref(refs?.dropdown?.ref)}
82
+ label="${refs?.dropdown?.labels?.country}"
83
+ label-sr-only
84
+ @model-value-changed="${refs?.dropdown?.listeners['model-value-changed']}"
85
+ style="${ifDefined(refs?.dropdown?.props?.style)}"
86
+ interaction-mode="mac"
87
+ >
88
+ ${repeat(
89
+ data.regionMetaList,
90
+ regionMeta => regionMeta.currencyCode,
91
+ regionMeta => html`
92
+ <lion-option .choiceValue="${regionMeta.currencyCode}"> </lion-option>
93
+ `,
94
+ )}
95
+ </lion-select-rich>
96
+ `;
97
+ },
98
+ };
99
+ }
100
+ customElements.define('input-amount-dropdown-mac', InputAmountDropdownMac);
101
+ const el = /** @type {LionInputAmountDropdown} */ (
102
+ await fixture(html`
103
+ <input-amount-dropdown-mac
104
+ .allowedCurrencies="${['GBP', 'EUR']}"
105
+ .modelValue="{ currency: 'GBP', amount: '123' }"
106
+ ></input-amount-dropdown-mac>
107
+ `)
108
+ );
109
+ const dropdownElement = el.refs.dropdown.value;
110
+ // @ts-expect-error [allow-protected-in-tests]
111
+ if (dropdownElement?._overlayCtrl) {
112
+ // @ts-expect-error [allow-protected-in-tests]
113
+ dropdownElement._overlayCtrl.show();
114
+ mimicUserChangingDropdown(dropdownElement, 'EUR');
115
+ await el.updateComplete;
116
+ await aTimeout(0);
117
+ // @ts-expect-error [allow-protected-in-tests]
118
+ expect(isActiveElement(el._inputNode)).to.be.true;
119
+ }
120
+ });
121
+
122
+ it('does not focus the textbox right after selection if selected via opened dropdown if interaction-mode is windows/linux', async () => {
123
+ class InputAmountDropdownWindows extends LionInputAmountDropdown {
124
+ static templates = {
125
+ ...(super.templates || {}),
126
+ /**
127
+ * @param {TemplateDataForDropdownInputAmount} templateDataForDropdown
128
+ */
129
+ dropdown: templateDataForDropdown => {
130
+ const { refs, data } = templateDataForDropdown;
131
+ // TODO: once spread directive available, use it per ref (like ref(refs?.dropdown?.ref))
132
+ return html`
133
+ <lion-select-rich
134
+ ${ref(refs?.dropdown?.ref)}
135
+ label="${refs?.dropdown?.labels?.country}"
136
+ label-sr-only
137
+ @model-value-changed="${refs?.dropdown?.listeners['model-value-changed']}"
138
+ style="${ifDefined(refs?.dropdown?.props?.style)}"
139
+ interaction-mode="windows/linux"
140
+ >
141
+ ${repeat(
142
+ data.regionMetaList,
143
+ regionMeta => regionMeta.currencyCode,
144
+ regionMeta => html`
145
+ <lion-option .choiceValue="${regionMeta.currencyCode}"> </lion-option>
146
+ `,
147
+ )}
148
+ </lion-select-rich>
149
+ `;
150
+ },
151
+ };
152
+ }
153
+ customElements.define('input-amount-dropdown-windows', InputAmountDropdownWindows);
154
+ const el = /** @type {LionInputAmountDropdown} */ (
155
+ await fixture(html`
156
+ <input-amount-dropdown-windows
157
+ .allowedCurrencies="${['GBP', 'EUR']}"
158
+ .modelValue="{ currency: 'GBP', amount: '123' }"
159
+ ></input-amount-dropdown-windows>
160
+ `)
161
+ );
162
+ const dropdownElement = el.refs.dropdown.value;
163
+ // @ts-expect-error [allow-protected-in-tests]
164
+ if (dropdownElement?._overlayCtrl) {
165
+ // @ts-expect-error [allow-protected-in-tests]
166
+ dropdownElement._overlayCtrl.show();
167
+ mimicUserChangingDropdown(dropdownElement, 'EUR');
168
+ await el.updateComplete;
169
+ await aTimeout(0);
170
+ // @ts-expect-error [allow-protected-in-tests]
171
+ expect(isActiveElement(el._inputNode)).to.be.false;
172
+ }
173
+ });
174
+
175
+ describe('defaultValidators', () => {
176
+ it('without interaction are not called', async () => {
177
+ const el = /** @type {LionInputAmountDropdown} */ (
178
+ await fixture(html`
179
+ <lion-input-amount-dropdown .allowedCurrencies="${['EUR']}"></lion-input-amount-dropdown>
180
+ `)
181
+ );
182
+ await aTimeout(0);
183
+ expect(el.hasFeedbackFor).to.deep.equal([]);
184
+ });
185
+
186
+ it('with interaction are called', async () => {
187
+ const el = /** @type {LionInputAmountDropdown} */ (
188
+ await fixture(html`
189
+ <lion-input-amount-dropdown
190
+ .allowedCurrencies="${['GBP', 'EUR']}"
191
+ ></lion-input-amount-dropdown>
192
+ `)
193
+ );
194
+ el.modelValue = { currency: 'EUR' };
195
+ await aTimeout(0);
196
+ el.modelValue = { currency: 'EUR', amount: '' };
197
+ expect(el.hasFeedbackFor).to.deep.equal(['error']);
198
+ });
199
+ });
200
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @typedef {HTMLSelectElement|HTMLElement & {modelValue:string}} DropdownElement
3
+ */
4
+
5
+ /**
6
+ * @param {DropdownElement} dropdownEl
7
+ * @param {string} value
8
+ */
9
+ export function mimicUserChangingDropdown(dropdownEl, value) {
10
+ if ('modelValue' in dropdownEl) {
11
+ // eslint-disable-next-line no-param-reassign
12
+ dropdownEl.modelValue = value;
13
+ dropdownEl.dispatchEvent(
14
+ new CustomEvent('model-value-changed', { detail: { isTriggeredByUser: true } }),
15
+ );
16
+ } else {
17
+ // eslint-disable-next-line no-param-reassign
18
+ dropdownEl.value = value;
19
+ dropdownEl.dispatchEvent(new Event('change'));
20
+ dropdownEl.dispatchEvent(new Event('input'));
21
+ }
22
+ }