@lion/ui 0.22.0 → 0.23.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.
- package/README.md +35 -0
- package/components/combobox/src/LionCombobox.js +10 -2
- package/components/combobox/test/lion-combobox.test.js +31 -0
- package/components/form-core/src/choice-group/ChoiceGroupMixin.js +7 -0
- package/components/form-core/types/choice-group/ChoiceGroupMixinTypes.ts +5 -0
- package/components/input-amount/src/LionInputAmount.js +17 -6
- package/components/input-amount/src/formatters.js +5 -3
- package/components/input-amount/test/formatters.test.js +88 -74
- package/components/input-amount/test/lion-input-amount.test.js +51 -36
- package/components/input-tel/test/validators.test.js +9 -0
- package/components/listbox/src/ListboxMixin.js +19 -3
- package/components/listbox/types/ListboxMixinTypes.ts +7 -1
- package/components/menu/README.md +360 -0
- package/components/menu/src/InteractiveListMixin.js +867 -0
- package/components/menu/src/LionMenu.js +436 -0
- package/components/menu/src/MoreButtonMenuMixin.js +295 -0
- package/components/menu/src/MultiLevelListMixin.js +312 -0
- package/components/menu/src/utils/isInView.js +26 -0
- package/components/menu/src/utils/listItemInteractions.js +124 -0
- package/components/menu/test/InteractiveListMixin.test.js +3 -0
- package/components/menu/test/MoreButtonMenuMixin.test.js +4 -0
- package/components/menu/test/MultiLevelListMixin.test.js +3 -0
- package/components/menu/test/lion-menu-interactions.test.js +3 -0
- package/components/menu/test/lion-menu.test.js +14 -0
- package/components/menu/test-helpers/mimicKeyPress.js +9 -0
- package/components/menu/test-suites/InteractiveListMixin.suite.js +537 -0
- package/components/menu/test-suites/LionMenu.suite.js +114 -0
- package/components/menu/test-suites/LionMenuInteractions.suite.js +583 -0
- package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +372 -0
- package/components/menu/test-suites/MultiLevelListMixin.suite.js +309 -0
- package/components/menu/types/InteractiveListMixinTypes.ts +126 -0
- package/components/menu/types/MoreButtonMenuMixinTypes.ts +51 -0
- package/components/menu/types/MultiLevelListMixinTypes.ts +28 -0
- package/components/menu/types/OverlayWithListInvokerMixinTypes.ts +61 -0
- package/components/overlays/NEXT.md +49 -0
- package/components/overlays/src/OverlayController.js +304 -125
- package/components/overlays/src/OverlayMixin.js +81 -17
- package/components/overlays/src/utils/adopt-styles.js +1 -1
- package/components/overlays/src/utils/is-equal-config.js +15 -6
- package/components/overlays/test/OverlayController.test.js +223 -25
- package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
- package/components/overlays/types/ArrowMixinTypes.ts +1 -3
- package/components/overlays/types/OverlayConfig.ts +20 -5
- package/components/overlays/types/OverlayMixinTypes.ts +2 -0
- package/components/select-rich/src/LionSelectRich.js +12 -0
- package/components/select-rich/test/lion-select-rich-interaction.test.js +53 -0
- package/components/select-rich/test/lion-select-rich-upgrade.test.js +30 -0
- package/custom-elements.json +3120 -149
- package/dist-types/components/combobox/src/LionCombobox.d.ts +1 -1
- package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +6 -2
- package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
- package/dist-types/components/form-core/src/choice-group/ChoiceGroupMixin.d.ts.map +1 -1
- package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts +5 -0
- package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts.map +1 -1
- package/dist-types/components/input-amount/src/LionInputAmount.d.ts +3 -1
- package/dist-types/components/input-amount/src/LionInputAmount.d.ts.map +1 -1
- package/dist-types/components/input-amount/src/formatters.d.ts +6 -4
- package/dist-types/components/input-amount/src/formatters.d.ts.map +1 -1
- package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
- package/dist-types/components/listbox/src/ListboxMixin.d.ts.map +1 -1
- package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts +7 -1
- package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts.map +1 -1
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts +35 -0
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -0
- package/dist-types/components/menu/src/LionMenu.d.ts +129 -0
- package/dist-types/components/menu/src/LionMenu.d.ts.map +1 -0
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +8 -0
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +1 -0
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +7 -0
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +1 -0
- package/dist-types/components/menu/src/utils/isInView.d.ts +9 -0
- package/dist-types/components/menu/src/utils/isInView.d.ts.map +1 -0
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +48 -0
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +1 -0
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-menu.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-menu.test.d.ts.map +1 -0
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts +7 -0
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +9 -0
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts +5 -0
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/types/InteractiveListMixinTypes.d.ts +72 -0
- package/dist-types/components/menu/types/InteractiveListMixinTypes.d.ts.map +1 -0
- package/dist-types/components/menu/types/MoreButtonMenuMixinTypes.d.ts +30 -0
- package/dist-types/components/menu/types/MoreButtonMenuMixinTypes.d.ts.map +1 -0
- package/dist-types/components/menu/types/MultiLevelListMixinTypes.d.ts +16 -0
- package/dist-types/components/menu/types/MultiLevelListMixinTypes.d.ts.map +1 -0
- package/dist-types/components/menu/types/OverlayWithListInvokerMixinTypes.d.ts +35 -0
- package/dist-types/components/menu/types/OverlayWithListInvokerMixinTypes.d.ts.map +1 -0
- package/dist-types/components/overlays/src/OverlayController.d.ts +22 -5
- package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
- package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
- package/dist-types/components/overlays/src/utils/is-equal-config.d.ts.map +1 -1
- package/dist-types/components/overlays/types/ArrowMixinTypes.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayConfig.d.ts +12 -2
- package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +1 -0
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +13 -2
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
- package/dist-types/components/select-rich/test/lion-select-rich-interaction.test.d.ts.map +1 -1
- package/dist-types/components/select-rich/test/lion-select-rich-upgrade.test.d.ts +2 -0
- package/dist-types/components/select-rich/test/lion-select-rich-upgrade.test.d.ts.map +1 -0
- package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
- package/dist-types/exports/define/lion-item.d.ts +2 -0
- package/dist-types/exports/define/lion-item.d.ts.map +1 -0
- package/dist-types/exports/define/lion-menu.d.ts +2 -0
- package/dist-types/exports/define/lion-menu.d.ts.map +1 -0
- package/dist-types/exports/define/lion-menuitem.d.ts +2 -0
- package/dist-types/exports/define/lion-menuitem.d.ts.map +1 -0
- package/dist-types/exports/menu-test-suites.d.ts +4 -0
- package/dist-types/exports/menu-test-suites.d.ts.map +1 -0
- package/dist-types/exports/menu.d.ts +3 -0
- package/dist-types/exports/menu.d.ts.map +1 -0
- package/dist-types/exports/overlays.d.ts +6 -0
- package/dist-types/exports/overlays.d.ts.map +1 -1
- package/dist-types/tsconfig.tsbuildinfo +1 -1
- package/docs/components/checkbox-group/overview.md +2 -2
- package/docs/components/collapsible/overview.md +1 -1
- package/docs/components/combobox/api-table.md +53 -39
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/api-table.md +32 -6
- package/docs/components/dialog/overview.md +1 -1
- package/docs/components/fieldset/overview.md +5 -5
- package/docs/components/form/overview.md +3 -3
- package/docs/components/form/use-cases.md +2 -2
- package/docs/components/icon/use-cases.md +1 -1
- package/docs/components/input/overview.md +1 -1
- package/docs/components/input-amount/api-table.md +9 -7
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-amount/use-cases.md +15 -4
- package/docs/components/input-amount-dropdown/api-table.md +2 -0
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/api-table.md +63 -45
- package/docs/components/input-datepicker/overview.md +5 -5
- package/docs/components/input-email/overview.md +2 -2
- package/docs/components/input-file/overview.md +1 -1
- package/docs/components/input-iban/overview.md +2 -2
- package/docs/components/input-range/overview.md +2 -2
- package/docs/components/input-stepper/overview.md +1 -1
- package/docs/components/input-tel/overview.md +1 -1
- package/docs/components/input-tel/use-cases.md +6 -6
- package/docs/components/input-tel-dropdown/overview.md +1 -1
- package/docs/components/listbox/use-cases.md +1 -1
- package/docs/components/menu/api-table.md +44 -0
- package/docs/components/menu/applyMenuDemoStyles.js +173 -0
- package/docs/components/menu/extensions.md +262 -0
- package/docs/components/menu/index.md +13 -0
- package/docs/components/menu/migration.md +129 -0
- package/docs/components/menu/overview.md +448 -0
- package/docs/components/menu/src/DemoNavigationBar.js +441 -0
- package/docs/components/menu/src/DemoSpinButton.js +215 -0
- package/docs/components/menu/src/DemoToolbar.js +101 -0
- package/docs/components/menu/src/DemoTree.js +55 -0
- package/docs/components/menu/src/navigation-bar-types.js +41 -0
- package/docs/components/menu/use-cases.md +74 -0
- package/docs/components/radio-group/overview.md +3 -3
- package/docs/components/select/use-cases.md +1 -1
- package/docs/components/select-rich/api-table.md +51 -33
- package/docs/components/select-rich/use-cases.md +2 -2
- package/docs/components/tabs/examples.md +1 -1
- package/docs/components/tooltip/api-table.md +33 -7
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/providence-analytics/overview.md +1 -1
- package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
- package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
- package/docs/fundamentals/rationales/accessibility.md +1 -1
- package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
- package/docs/fundamentals/systems/core/overview.md +1 -1
- package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
- package/docs/fundamentals/systems/form/model-value.md +1 -1
- package/docs/fundamentals/systems/form/overview.md +28 -28
- package/docs/fundamentals/systems/form/validate.md +2 -2
- package/docs/fundamentals/systems/localize/overview.md +4 -4
- package/docs/fundamentals/systems/overlays/api-table.md +46 -43
- package/docs/fundamentals/systems/overlays/configuration.md +2 -2
- package/docs/fundamentals/systems/overlays/overview.md +5 -5
- package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
- package/docs/fundamentals/tools/helpers/overview.md +1 -1
- package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
- package/docs/guides/how-to/create-a-custom-field.md +5 -5
- package/docs/guides/how-to/create-a-form.md +4 -4
- package/docs/guides/how-to/extend-a-native-input.md +2 -2
- package/docs/guides/how-to/index.md +1 -1
- package/docs/guides/index.md +8 -8
- package/docs/guides/principles/index.md +5 -5
- package/docs/guides/principles/styling.md +1 -1
- package/exports/define/lion-item.js +3 -0
- package/exports/define/lion-menu.js +3 -0
- package/exports/define/lion-menuitem.js +3 -0
- package/exports/menu-test-suites.js +4 -0
- package/exports/menu.js +3 -0
- package/exports/overlays.js +6 -0
- package/menu-test-suites.js +4 -0
- package/menu.js +3 -0
- package/overlays.js +6 -0
- package/package.json +11 -4
- package/vendor/README.md +61 -0
- package/vendor/awesome-phonenumber-7.9.0-tlouisse.libphonenumber-9.0.41.tgz +0 -0
- package/vscode.html-custom-data.json +76 -8
- package/web-types.json +202 -17
package/README.md
CHANGED
|
@@ -112,6 +112,41 @@ They provide an unopinionated, white-label layer that can be extended to your ow
|
|
|
112
112
|
npm i @lion/ui
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
### Temporary phone-number metadata workaround
|
|
116
|
+
|
|
117
|
+
We temporarily vendor `awesome-phonenumber@7.9.0-tlouisse.libphonenumber-9.0.41`,
|
|
118
|
+
rebuilt from Google's **libphonenumber v9.0.41**, instead of the registry dependency.
|
|
119
|
+
This includes updated phone-number metadata, such as the French Guiana premium-rate
|
|
120
|
+
example `+594890123456`, which the previous `awesome-phonenumber@7.8.0` rejected.
|
|
121
|
+
We keep this workaround until the next upstream awesome-phonenumber release
|
|
122
|
+
incorporating the required metadata is available.
|
|
123
|
+
|
|
124
|
+
The artifact lives in `packages/ui/vendor/awesome-phonenumber-7.9.0-tlouisse.libphonenumber-9.0.41.tgz`.
|
|
125
|
+
`@lion/ui` uses a package-relative `file:vendor/...` dependency and bundles the
|
|
126
|
+
installed `awesome-phonenumber` package when published. This preserves normal
|
|
127
|
+
consumer installation and lazy imports without access to the Lion checkout.
|
|
128
|
+
The private root also declares the same tarball as a development dependency so
|
|
129
|
+
npm installs it during a clean workspace install rather than assuming the
|
|
130
|
+
workspace already contains its bundled dependencies. The legacy docs dependency
|
|
131
|
+
keeps its separate registry version.
|
|
132
|
+
The temporary `prepack` helper verifies the checksum and installs only the tarball
|
|
133
|
+
in an isolated temporary prefix using npm's offline mode (without running dependency
|
|
134
|
+
scripts or writing a lockfile). It copies that installed package into the local
|
|
135
|
+
bundle and removes the temporary prefix, because npm packing does not include
|
|
136
|
+
hoisted workspace bundles. Other Lion dependencies remain unchanged.
|
|
137
|
+
The original MIT license and Google's Apache-2.0 notices remain in the artifact
|
|
138
|
+
and bundled package. See [vendor provenance](https://github.com/ing-bank/lion/blob/4649554021224763994601dd8d202c511d35013e/packages/ui/vendor/README.md) for the
|
|
139
|
+
source commit, checksum, and reproducible packaging instructions.
|
|
140
|
+
|
|
141
|
+
Once the next upstream awesome-phonenumber release includes the required
|
|
142
|
+
metadata, restore the normal registry dependency in `packages/ui/package.json`,
|
|
143
|
+
remove `awesome-phonenumber` from `bundledDependencies`, remove the vendor asset
|
|
144
|
+
and its `files` entry, remove the root development dependency, and regenerate
|
|
145
|
+
`package-lock.json`. Remove the temporary `prepack` lifecycle and
|
|
146
|
+
`scripts/prepare-vendored-phone.js` helper as well. Keep the metadata
|
|
147
|
+
regression test and rerun the phone tests, typechecks, and packed-consumer install
|
|
148
|
+
check before removing this workaround documentation.
|
|
149
|
+
|
|
115
150
|
## How to use
|
|
116
151
|
|
|
117
152
|
### Extend a Web Component
|
|
@@ -13,6 +13,10 @@ const matchA11ySpanReverseFns = new WeakMap();
|
|
|
13
13
|
// TODO: make ListboxOverlayMixin that is shared between SelectRich and Combobox
|
|
14
14
|
// TODO: extract option matching based on 'typed character cache' and share that logic
|
|
15
15
|
// on Listbox or ListNavigationWithActiveDescendantMixin
|
|
16
|
+
// TODO: this component inherits `multipleChoice` (ChoiceGroupMixin) and `rotateKeyboardNavigation`
|
|
17
|
+
// (ListboxMixin) and reads both in many places. When those are renamed to `multiple` and `wrap`, move
|
|
18
|
+
// these usages to the new names in the same release; the deprecated aliases only exist for consumers.
|
|
19
|
+
// See the TODO at the top of ../../listbox/src/ListboxMixin.js.
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* @typedef {import('@lion/ui/listbox.js').LionOption} LionOption
|
|
@@ -1228,9 +1232,13 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
|
|
|
1228
1232
|
* @param {{phase?:string}} config
|
|
1229
1233
|
* @protected
|
|
1230
1234
|
*/
|
|
1231
|
-
// eslint-disable-next-line no-unused-vars
|
|
1232
1235
|
_syncToTextboxCondition(modelValue, oldModelValue, { phase } = {}) {
|
|
1233
|
-
return
|
|
1236
|
+
return (
|
|
1237
|
+
phase === 'overlay-close' ||
|
|
1238
|
+
this.autocomplete === 'both' ||
|
|
1239
|
+
this.autocomplete === 'inline' ||
|
|
1240
|
+
!this.focused
|
|
1241
|
+
);
|
|
1234
1242
|
}
|
|
1235
1243
|
|
|
1236
1244
|
/**
|
|
@@ -1392,6 +1392,37 @@ describe('lion-combobox', () => {
|
|
|
1392
1392
|
expect(el.opened).to.equal(true);
|
|
1393
1393
|
});
|
|
1394
1394
|
|
|
1395
|
+
it('syncs a manually checked selection on overlay close while the input remains focused', async () => {
|
|
1396
|
+
const el = /** @type {LionCombobox} */ (
|
|
1397
|
+
await fixture(html`
|
|
1398
|
+
<lion-combobox name="foo" autocomplete="list">
|
|
1399
|
+
<lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
|
|
1400
|
+
<lion-option .choiceValue="${'Chard'}">Chard</lion-option>
|
|
1401
|
+
</lion-combobox>
|
|
1402
|
+
`)
|
|
1403
|
+
);
|
|
1404
|
+
const { _inputNode } = getComboboxMembers(el);
|
|
1405
|
+
|
|
1406
|
+
_inputNode.focus();
|
|
1407
|
+
el.opened = true;
|
|
1408
|
+
await el.updateComplete;
|
|
1409
|
+
expect(el.opened).to.equal(true);
|
|
1410
|
+
expect(isActiveElement(_inputNode)).to.be.true;
|
|
1411
|
+
|
|
1412
|
+
el.setCheckedIndex(0);
|
|
1413
|
+
await el.updateComplete;
|
|
1414
|
+
|
|
1415
|
+
expect(el.modelValue).to.equal('Artichoke');
|
|
1416
|
+
expect(el.checkedIndex).to.equal(0);
|
|
1417
|
+
expect(_inputNode.value).to.equal('');
|
|
1418
|
+
|
|
1419
|
+
el.opened = false;
|
|
1420
|
+
await el.updateComplete;
|
|
1421
|
+
|
|
1422
|
+
expect(_inputNode.value).to.equal('Artichoke');
|
|
1423
|
+
expect(isActiveElement(_inputNode)).to.be.true;
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1395
1426
|
it('hides (and clears) overlay on [Escape]', async () => {
|
|
1396
1427
|
const el = /** @type {LionCombobox} */ (
|
|
1397
1428
|
await fixture(html`
|
|
@@ -30,6 +30,13 @@ const ChoiceGroupMixinImplementation = /** @type {ChoiceGroupMixin} */ (supercla
|
|
|
30
30
|
/** @type {any} */
|
|
31
31
|
static get properties() {
|
|
32
32
|
return {
|
|
33
|
+
// TODO: rename to `multiple` (the platform name, `<select multiple>`) so it matches the
|
|
34
|
+
// vocabulary the menu's InteractiveListMixin uses. Must be non-breaking for the released
|
|
35
|
+
// components that inherit it (listbox/select-rich/combobox, radio-group/checkbox-group): add
|
|
36
|
+
// `multiple` with its own attribute, keep `multipleChoice`/`multiple-choice` as a deprecated
|
|
37
|
+
// alias that delegates to it (dev-only deprecation warning) for a release, mark it
|
|
38
|
+
// `@deprecated` only then and remove the alias in the next major. Coordinate with the TODOs in
|
|
39
|
+
// packages/ui/components/listbox/src/ListboxMixin.js and combobox/src/LionCombobox.js.
|
|
33
40
|
multipleChoice: { type: Boolean, attribute: 'multiple-choice' },
|
|
34
41
|
};
|
|
35
42
|
}
|
|
@@ -7,6 +7,11 @@ import { FormRegistrarHost } from '../registration/FormRegistrarMixinTypes.js';
|
|
|
7
7
|
import { ChoiceInputHost } from './ChoiceInputMixinTypes.js';
|
|
8
8
|
|
|
9
9
|
export declare class ChoiceGroupHost extends FormControlHost {
|
|
10
|
+
/**
|
|
11
|
+
* TODO: rename to `multiple` (the platform name, `<select multiple>`), non-breaking: keep this
|
|
12
|
+
* property and the `multiple-choice` attribute as a deprecated alias for a release before removing
|
|
13
|
+
* them in a major. See the TODO in src/choice-group/ChoiceGroupMixin.js.
|
|
14
|
+
*/
|
|
10
15
|
multipleChoice: boolean;
|
|
11
16
|
get serializedValue(): string;
|
|
12
17
|
set serializedValue(value: string);
|
|
@@ -7,7 +7,7 @@ import { parseAmount } from './parsers.js';
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @typedef {import('../../form-core/types/FormatMixinTypes.js').FormatOptions} FormatOptions
|
|
10
|
-
* @typedef {FormatOptions & {locale?:string;currency:string|undefined}} AmountFormatOptions
|
|
10
|
+
* @typedef {FormatOptions & {locale?:string;currency:string|undefined;currencySymbol:string|undefined;}} AmountFormatOptions
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -25,6 +25,10 @@ export class LionInputAmount extends LocalizeMixin(LionInput) {
|
|
|
25
25
|
* reader users
|
|
26
26
|
*/
|
|
27
27
|
currency: String,
|
|
28
|
+
currencyLabel: {
|
|
29
|
+
type: String,
|
|
30
|
+
attribute: 'currency-label',
|
|
31
|
+
},
|
|
28
32
|
/**
|
|
29
33
|
* @desc the modelValue of the input-amount has the 'Number' type. This allows
|
|
30
34
|
* Application Developers to easily read from and write to this input or write custom
|
|
@@ -71,6 +75,7 @@ export class LionInputAmount extends LocalizeMixin(LionInput) {
|
|
|
71
75
|
this.formatter = formatAmount;
|
|
72
76
|
/** @type {string | undefined} */
|
|
73
77
|
this.currency = undefined;
|
|
78
|
+
this.currencyLabel = '';
|
|
74
79
|
/** @type {string | undefined} */
|
|
75
80
|
this.locale = undefined;
|
|
76
81
|
this.__currencyDisplayNodeIsConnected = true;
|
|
@@ -201,19 +206,25 @@ export class LionInputAmount extends LocalizeMixin(LionInput) {
|
|
|
201
206
|
|
|
202
207
|
/** @private */
|
|
203
208
|
__setCurrencyDisplayLabel() {
|
|
204
|
-
// TODO: (@erikkroes) for optimal a11y, abbreviations should be part of aria-label
|
|
205
|
-
// example, for a language switch with text 'en', an aria-label of 'english' is not
|
|
206
|
-
// sufficient, it should also contain the abbreviation.
|
|
207
209
|
if (this.__currencyDisplayNode) {
|
|
210
|
+
const label = this.currencyLabel || this.currency || '';
|
|
211
|
+
let currencyName = '';
|
|
212
|
+
if (this.currency) {
|
|
213
|
+
currencyName = getCurrencyName(this.currency, {});
|
|
214
|
+
}
|
|
208
215
|
this.__currencyDisplayNode.setAttribute(
|
|
209
216
|
'aria-label',
|
|
210
|
-
|
|
217
|
+
`${label} ${currencyName ? `(${currencyName})` : ''}`,
|
|
211
218
|
);
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
get __currencyLabel() {
|
|
216
|
-
|
|
223
|
+
let label = this.currencyLabel || '';
|
|
224
|
+
if (!label && this.currency) {
|
|
225
|
+
label = formatCurrencyLabel(this.currency, this._localizeManager.locale);
|
|
226
|
+
}
|
|
227
|
+
return label;
|
|
217
228
|
}
|
|
218
229
|
|
|
219
230
|
__reformat() {
|
|
@@ -5,17 +5,18 @@ import {
|
|
|
5
5
|
} from '@lion/ui/localize-no-side-effects.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @typedef {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberOptions}
|
|
8
|
+
* @typedef {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberOptions} FormatNumberOptions
|
|
9
|
+
* @typedef {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberPart} FormatNumberPart
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Formats a number considering the default fraction digits provided by Intl.
|
|
13
14
|
*
|
|
14
15
|
* @param {number} modelValue Number to format
|
|
15
|
-
* @param {
|
|
16
|
+
* @param {FormatNumberOptions} [givenOptions]
|
|
16
17
|
*/
|
|
17
18
|
export function formatAmount(modelValue, givenOptions) {
|
|
18
|
-
/** @type {
|
|
19
|
+
/** @type {FormatNumberOptions} */
|
|
19
20
|
const options = {
|
|
20
21
|
currency: 'EUR',
|
|
21
22
|
...givenOptions,
|
|
@@ -40,5 +41,6 @@ export function formatCurrencyLabel(currency, locale) {
|
|
|
40
41
|
if (currency === '') {
|
|
41
42
|
return '';
|
|
42
43
|
}
|
|
44
|
+
|
|
43
45
|
return normalizeCurrencyLabel(currency, locale);
|
|
44
46
|
}
|
|
@@ -3,86 +3,100 @@ import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
|
|
|
3
3
|
import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
|
|
4
4
|
|
|
5
5
|
import { formatAmount } from '@lion/ui/input-amount.js';
|
|
6
|
+
import { formatCurrencyLabel } from '../src/formatters.js';
|
|
6
7
|
|
|
7
|
-
describe('
|
|
8
|
-
|
|
8
|
+
describe('formatters', () => {
|
|
9
|
+
describe('formatAmount()', () => {
|
|
10
|
+
const localizeManager = getLocalizeManager();
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
localizeTearDown();
|
|
14
|
+
});
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
16
|
+
it('formats number with options', async () => {
|
|
17
|
+
expect(
|
|
18
|
+
formatAmount(12.345678, {
|
|
19
|
+
locale: 'en-GB',
|
|
20
|
+
minimumFractionDigits: 2,
|
|
21
|
+
maximumFractionDigits: 2,
|
|
22
|
+
}),
|
|
23
|
+
).to.equal('12.35');
|
|
24
|
+
expect(
|
|
25
|
+
formatAmount(12.345678, {
|
|
26
|
+
locale: 'nl-NL',
|
|
27
|
+
minimumFractionDigits: 2,
|
|
28
|
+
maximumFractionDigits: 2,
|
|
29
|
+
}),
|
|
30
|
+
).to.equal('12,35');
|
|
31
|
+
expect(
|
|
32
|
+
formatAmount(12345678, {
|
|
33
|
+
locale: 'en-GB',
|
|
34
|
+
minimumFractionDigits: 2,
|
|
35
|
+
maximumFractionDigits: 2,
|
|
36
|
+
}),
|
|
37
|
+
).to.equal('12,345,678.00');
|
|
38
|
+
expect(
|
|
39
|
+
formatAmount(12345678, {
|
|
40
|
+
locale: 'nl-NL',
|
|
41
|
+
minimumFractionDigits: 2,
|
|
42
|
+
maximumFractionDigits: 2,
|
|
43
|
+
}),
|
|
44
|
+
).to.equal('12.345.678,00');
|
|
45
|
+
expect(
|
|
46
|
+
formatAmount(12.345678, {
|
|
47
|
+
locale: 'en-GB',
|
|
48
|
+
minimumFractionDigits: 0,
|
|
49
|
+
maximumFractionDigits: 1,
|
|
50
|
+
}),
|
|
51
|
+
).to.equal('12.3');
|
|
52
|
+
expect(
|
|
53
|
+
formatAmount(12.345678, {
|
|
54
|
+
locale: 'en-GB',
|
|
55
|
+
minimumFractionDigits: 3,
|
|
56
|
+
maximumFractionDigits: 3,
|
|
57
|
+
}),
|
|
58
|
+
).to.equal('12.346');
|
|
59
|
+
expect(
|
|
60
|
+
formatAmount(-12.345678, {
|
|
61
|
+
locale: 'en-GB',
|
|
62
|
+
minimumFractionDigits: 2,
|
|
63
|
+
maximumFractionDigits: 2,
|
|
64
|
+
}),
|
|
65
|
+
).to.equal('−12.35');
|
|
66
|
+
});
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
it('formats the right amount of fraction digits for a certain currency', async () => {
|
|
69
|
+
expect(formatAmount(123456.78, { locale: 'en-GB', currency: 'EUR' })).to.equal('123,456.78');
|
|
70
|
+
expect(formatAmount(123456.78, { locale: 'en-GB', currency: 'JOD' })).to.equal('123,456.780');
|
|
71
|
+
});
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
it('fallbacks to global locale and EUR by default', async () => {
|
|
74
|
+
localizeManager.locale = 'en-GB';
|
|
75
|
+
expect(formatAmount(12345678)).to.equal('12,345,678.00');
|
|
76
|
+
localizeManager.locale = 'nl-NL';
|
|
77
|
+
expect(formatAmount(12345678)).to.equal('12.345.678,00');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// TODO: make it work with big numbers, e.g. make use of BigInt
|
|
81
|
+
it.skip('rounds up big numbers', async () => {
|
|
82
|
+
expect(formatAmount(1e21, { locale: 'en-GB', currency: 'EUR' })).to.equal(
|
|
83
|
+
'1,000,000,000,000,000,000,000.00',
|
|
84
|
+
);
|
|
85
|
+
// eslint-disable-next-line no-loss-of-precision
|
|
86
|
+
expect(formatAmount(12345678987654321.42, { locale: 'en-GB', currency: 'EUR' })).to.equal(
|
|
87
|
+
'12,345,678,987,654,321.42',
|
|
88
|
+
);
|
|
89
|
+
});
|
|
76
90
|
});
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'
|
|
86
|
-
);
|
|
92
|
+
describe('formatCurrencyLabel()', () => {
|
|
93
|
+
it('gives back the correct currency label', async () => {
|
|
94
|
+
expect(formatCurrencyLabel('EUR', 'en-GB')).to.equal('EUR');
|
|
95
|
+
expect(formatCurrencyLabel('USD', 'en-GB')).to.equal('USD');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('gives nothing back if no currency has been provided', async () => {
|
|
99
|
+
expect(formatCurrencyLabel('', 'en-GB')).to.equal('');
|
|
100
|
+
});
|
|
87
101
|
});
|
|
88
102
|
});
|
|
@@ -225,42 +225,57 @@ describe('<lion-input-amount>', () => {
|
|
|
225
225
|
expect(_inputNode.type).to.equal('text');
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
228
|
+
describe('currencyLabel', async () => {
|
|
229
|
+
it('shows no currency by default', async () => {
|
|
230
|
+
const el = await fixture(`<lion-input-amount></lion-input-amount>`);
|
|
231
|
+
expect(Array.from(el.children).find(child => child.slot === 'after')).to.be.undefined;
|
|
232
|
+
});
|
|
232
233
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
234
|
+
it('displays currency if provided', async () => {
|
|
235
|
+
const el = /** @type {LionInputAmount} */ (
|
|
236
|
+
await fixture(`<lion-input-amount currency="EUR"></lion-input-amount>`)
|
|
237
|
+
);
|
|
238
|
+
expect(
|
|
239
|
+
/** @type {HTMLElement[]} */ (Array.from(el.children)).find(child => child.slot === 'after')
|
|
240
|
+
?.innerText,
|
|
241
|
+
).to.equal('EUR');
|
|
242
|
+
});
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
244
|
+
it('displays correct currency for TRY if locale is tr-TR', async () => {
|
|
245
|
+
localize.locale = 'tr-TR';
|
|
246
|
+
const el = /** @type {LionInputAmount} */ (
|
|
247
|
+
await fixture(`<lion-input-amount currency="TRY"></lion-input-amount>`)
|
|
248
|
+
);
|
|
249
|
+
expect(
|
|
250
|
+
/** @type {HTMLElement[]} */ (Array.from(el.children)).find(child => child.slot === 'after')
|
|
251
|
+
?.innerText,
|
|
252
|
+
).to.equal('TL');
|
|
253
|
+
});
|
|
253
254
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
255
|
+
it('displays currency symbol if provided via formatOptions', async () => {
|
|
256
|
+
const el = /** @type {LionInputAmount} */ (
|
|
257
|
+
await fixture(
|
|
258
|
+
html`<lion-input-amount currency="EUR" currency-label="€"></lion-input-amount>`,
|
|
259
|
+
)
|
|
260
|
+
);
|
|
261
|
+
const currLabel = /** @type {HTMLElement[]} */ (Array.from(el.children)).find(
|
|
262
|
+
child => child.slot === 'after',
|
|
263
|
+
);
|
|
264
|
+
expect(currLabel?.innerText).to.equal('€');
|
|
265
|
+
expect(currLabel?.getAttribute('aria-label')).to.equal('€ (euros)');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('can update currency', async () => {
|
|
269
|
+
const el = /** @type {LionInputAmount} */ (
|
|
270
|
+
await fixture(`<lion-input-amount currency="EUR"></lion-input-amount>`)
|
|
271
|
+
);
|
|
272
|
+
el.currency = 'USD';
|
|
273
|
+
await el.updateComplete;
|
|
274
|
+
expect(
|
|
275
|
+
/** @type {HTMLElement[]} */ (Array.from(el.children)).find(child => child.slot === 'after')
|
|
276
|
+
?.innerText,
|
|
277
|
+
).to.equal('USD');
|
|
278
|
+
});
|
|
264
279
|
});
|
|
265
280
|
|
|
266
281
|
it('reformats on locale changes', async () => {
|
|
@@ -311,7 +326,7 @@ describe('<lion-input-amount>', () => {
|
|
|
311
326
|
child => child.slot === 'after',
|
|
312
327
|
);
|
|
313
328
|
expect(currLabel?.innerText).to.equal('EUR');
|
|
314
|
-
expect(currLabel?.getAttribute('aria-label')).to.equal('euros');
|
|
329
|
+
expect(currLabel?.getAttribute('aria-label')).to.equal('EUR (euros)');
|
|
315
330
|
});
|
|
316
331
|
|
|
317
332
|
it('sets currency label on the after element', async () => {
|
|
@@ -416,10 +431,10 @@ describe('<lion-input-amount>', () => {
|
|
|
416
431
|
const label = /** @type {HTMLElement[]} */ (Array.from(el.children)).find(
|
|
417
432
|
child => child.slot === 'after',
|
|
418
433
|
);
|
|
419
|
-
expect(label?.getAttribute('aria-label')).to.equal('euros');
|
|
434
|
+
expect(label?.getAttribute('aria-label')).to.equal('EUR (euros)');
|
|
420
435
|
el.currency = 'USD';
|
|
421
436
|
await el.updateComplete;
|
|
422
|
-
expect(label?.getAttribute('aria-label')).to.equal('US dollars');
|
|
437
|
+
expect(label?.getAttribute('aria-label')).to.equal('USD (US dollars)');
|
|
423
438
|
el.currency = 'PHP';
|
|
424
439
|
await el.updateComplete;
|
|
425
440
|
// TODO: Chrome Intl now thinks this should be pesos instead of pisos. They're probably right.
|
|
@@ -46,6 +46,15 @@ describe('PhoneNumber validation', () => {
|
|
|
46
46
|
expect(validator.execute('+31612345678', 'NL')).to.be.false;
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
it('accepts the French Guiana premium-rate example from libphonenumber v9.0.41', () => {
|
|
50
|
+
const validator = new PhoneNumber();
|
|
51
|
+
expect(validator.execute('+594890123456', 'GF')).to.be.false;
|
|
52
|
+
const parsed = PhoneUtilManager.PhoneUtil.parsePhoneNumber('+594890123456');
|
|
53
|
+
expect(parsed.valid).to.be.true;
|
|
54
|
+
expect(parsed.type).to.equal('premium-rate');
|
|
55
|
+
expect(parsed.regionCode).to.equal('GF');
|
|
56
|
+
});
|
|
57
|
+
|
|
49
58
|
it('handles validation via awesome-phonenumber', () => {
|
|
50
59
|
const validator = new PhoneNumber();
|
|
51
60
|
// @ts-ignore
|
|
@@ -6,9 +6,21 @@ import { ScopedElementsMixin } from '../../core/src/ScopedElementsMixin.js';
|
|
|
6
6
|
import { LionOptions } from './LionOptions.js';
|
|
7
7
|
import { moveUserProvidedDefaultSlottablesToTarget } from '../../core/src/SlotMixin.js';
|
|
8
8
|
|
|
9
|
-
// TODO:
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// TODO: make this mixin reuse the interactive-list implementation instead of its own copy of the
|
|
10
|
+
// navigation logic. The extraction this TODO used to ask for ("ListNavigationWithActiveDescendantMixin
|
|
11
|
+
// that can be reused in [role="menu"]") now exists as `InteractiveListMixin`, the base of the menu
|
|
12
|
+
// components (packages/ui/components/menu). Rebasing this mixin on it is also what makes the next step
|
|
13
|
+
// possible, without breaking consumers in between:
|
|
14
|
+
// - rename the api to the focusgroup/platform vocabulary the menu already speaks:
|
|
15
|
+
// `orientation` ('vertical'/'horizontal') -> `axis` ('block'/'inline'),
|
|
16
|
+
// `rotateKeyboardNavigation` -> `wrap`, `multipleChoice` -> `multiple`
|
|
17
|
+
// - map the new axis values to the ARIA ones where `aria-orientation` is set (block -> vertical,
|
|
18
|
+
// inline -> horizontal); ARIA does not know the focusgroup tokens
|
|
19
|
+
// - keep the old properties and attributes working in the same release: add the new property with
|
|
20
|
+
// its own attribute, turn the old one into a deprecated alias that delegates to it (with a
|
|
21
|
+
// dev-only deprecation warning) and mark it `@deprecated` in ListboxMixinTypes, then remove the
|
|
22
|
+
// aliases in the next major
|
|
23
|
+
// - `hasNoDefaultSelected` and `selectionFollowsFocus` can stay: the menu uses those names too
|
|
12
24
|
|
|
13
25
|
/**
|
|
14
26
|
* @typedef {import('../../form-core/types/FormControlMixinTypes.js').HTMLElementWithValue} HTMLElementWithValue
|
|
@@ -34,11 +46,15 @@ const ListboxMixinImplementation = /** @type {ListboxMixin} */ (superclass =>
|
|
|
34
46
|
/** @type {any} */
|
|
35
47
|
static get properties() {
|
|
36
48
|
return {
|
|
49
|
+
// TODO: rename to `axis` with the values 'block'|'inline' (see the TODO at the top of this
|
|
50
|
+
// file); keep `orientation`/`orientation="vertical"|"horizontal"` working as a deprecated alias
|
|
37
51
|
orientation: String,
|
|
38
52
|
selectionFollowsFocus: {
|
|
39
53
|
type: Boolean,
|
|
40
54
|
attribute: 'selection-follows-focus',
|
|
41
55
|
},
|
|
56
|
+
// TODO: rename to `wrap` (see the TODO at the top of this file); keep the old property and
|
|
57
|
+
// attribute working as a deprecated alias
|
|
42
58
|
rotateKeyboardNavigation: {
|
|
43
59
|
type: Boolean,
|
|
44
60
|
attribute: 'rotate-keyboard-navigation',
|
|
@@ -25,7 +25,13 @@ export declare class ListboxHost {
|
|
|
25
25
|
public rotateKeyboardNavigation: Boolean;
|
|
26
26
|
/**
|
|
27
27
|
* Informs screen reader and affects keyboard navigation.
|
|
28
|
-
* By default 'vertical'
|
|
28
|
+
* By default 'vertical'.
|
|
29
|
+
*
|
|
30
|
+
* TODO: rename to `axis: 'inline' | 'block'` (the vocabulary the menu's `InteractiveListMixin` uses),
|
|
31
|
+
* together with `rotateKeyboardNavigation` -> `wrap` and `multipleChoice` -> `multiple`. Must be
|
|
32
|
+
* non-breaking: the old properties and their attributes stay available as deprecated aliases for a
|
|
33
|
+
* release (dev-only deprecation warning), and only then get `@deprecated` here and disappear in a
|
|
34
|
+
* major. See the TODO at the top of src/ListboxMixin.js.
|
|
29
35
|
*/
|
|
30
36
|
public orientation: 'vertical' | 'horizontal';
|
|
31
37
|
|