diff --git a/.changeset/shiny-plants-sleep.md b/.changeset/shiny-plants-sleep.md new file mode 100644 index 0000000000..a1d9133c6e --- /dev/null +++ b/.changeset/shiny-plants-sleep.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[input-stepper] add parseNumber and formatNumber to format the value based on locale diff --git a/docs/components/input-stepper/use-cases.md b/docs/components/input-stepper/use-cases.md index 93a33862f6..76d8e88dde 100644 --- a/docs/components/input-stepper/use-cases.md +++ b/docs/components/input-stepper/use-cases.md @@ -48,3 +48,24 @@ Use `min` and `max` attribute to specify a range. value="200" > ``` + +### Formatting + +Just like with the `input-amount` you can add the `formatOptions` to format the numbers to your preferences, to a different locale or adjust the amount of fractions. + +```js preview-story +export const formatting = () => { + const format = { locale: 'nl-NL' }; + return html` + + `; +}; +``` diff --git a/packages/ui/components/input-stepper/src/LionInputStepper.js b/packages/ui/components/input-stepper/src/LionInputStepper.js index 8895fb94e5..8dc50d8647 100644 --- a/packages/ui/components/input-stepper/src/LionInputStepper.js +++ b/packages/ui/components/input-stepper/src/LionInputStepper.js @@ -1,5 +1,5 @@ import { html, css, render } from 'lit'; -import { LocalizeMixin } from '@lion/ui/localize-no-side-effects.js'; +import { formatNumber, LocalizeMixin, parseNumber } from '@lion/ui/localize-no-side-effects.js'; import { LionInput } from '@lion/ui/input.js'; import { IsNumber, MinNumber, MaxNumber } from '@lion/ui/form-core.js'; import { localizeNamespaceLoader } from './localizeNamespaceLoader.js'; @@ -52,7 +52,7 @@ export class LionInputStepper extends LocalizeMixin(LionInput) { * @returns {number} */ get currentValue() { - return parseFloat(this.value) || 0; + return this.modelValue || 0; } get _inputNode() { @@ -62,7 +62,8 @@ export class LionInputStepper extends LocalizeMixin(LionInput) { constructor() { super(); /** @param {string} modelValue */ - this.parser = modelValue => parseFloat(modelValue); + this.parser = parseNumber; + this.formatter = formatNumber; this.min = Infinity; this.max = Infinity; this.step = 1; @@ -229,7 +230,7 @@ export class LionInputStepper extends LocalizeMixin(LionInput) { const { step, min, max } = this.values; const newValue = this.currentValue + step; if (newValue <= max || max === Infinity) { - this.value = newValue < min && min !== Infinity ? `${min}` : `${newValue}`; + this.modelValue = newValue < min && min !== Infinity ? `${min}` : `${newValue}`; this.__toggleSpinnerButtonsState(); this._proxyInputEvent(); } @@ -243,7 +244,7 @@ export class LionInputStepper extends LocalizeMixin(LionInput) { const { step, min, max } = this.values; const newValue = this.currentValue - step; if (newValue >= min || min === Infinity) { - this.value = newValue > max && max !== Infinity ? `${max}` : `${newValue}`; + this.modelValue = newValue > max && max !== Infinity ? `${max}` : `${newValue}`; this.__toggleSpinnerButtonsState(); this._proxyInputEvent(); } diff --git a/packages/ui/components/input-stepper/test/lion-input-stepper-integrations.test.js b/packages/ui/components/input-stepper/test/lion-input-stepper-integrations.test.js new file mode 100644 index 0000000000..b2d2674649 --- /dev/null +++ b/packages/ui/components/input-stepper/test/lion-input-stepper-integrations.test.js @@ -0,0 +1,20 @@ +import { + runInteractionStateMixinSuite, + runFormatMixinSuite, +} from '@lion/ui/form-core-test-suites.js'; + +import '@lion/ui/define/lion-input-stepper.js'; + +const tagString = 'lion-input-stepper'; + +describe(' integrations', () => { + runInteractionStateMixinSuite({ + tagString, + allowedModelValueTypes: [Number], + }); + + runFormatMixinSuite({ + tagString, + modelValueType: Number, + }); +}); diff --git a/packages/ui/components/input-stepper/test/lion-input-stepper.test.js b/packages/ui/components/input-stepper/test/lion-input-stepper.test.js index 26406b6e07..e33294f0b7 100644 --- a/packages/ui/components/input-stepper/test/lion-input-stepper.test.js +++ b/packages/ui/components/input-stepper/test/lion-input-stepper.test.js @@ -1,6 +1,7 @@ import { expect, fixture as _fixture, nextFrame } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; import sinon from 'sinon'; +import { formatNumber } from '@lion/ui/localize-no-side-effects.js'; import '@lion/ui/define/lion-input-stepper.js'; /** @@ -49,6 +50,50 @@ describe('', () => { }); }); + describe('Formatter', () => { + it('uses formatNumber for formatting', async () => { + const el = await fixture(defaultInputStepper); + expect(el.formatter).to.equal(formatNumber); + }); + + it('formatNumber uses locale provided in formatOptions', async () => { + let el = await fixture(html` + + `); + expect(el.formattedValue).to.equal('1,234.56'); + el = await fixture(html` + + `); + expect(el.formattedValue).to.equal('1.234,56'); + }); + + it('supports overriding decimalSeparator in formatOptions', async () => { + const el = await fixture( + html``, + ); + expect(el.formattedValue).to.equal('12.34'); + }); + + it('supports overriding groupSeparator in formatOptions', async () => { + const el = await fixture( + html``, + ); + expect(el.formattedValue).to.equal('1,234.56'); + }); + }); + describe('User interaction', () => { it('should increment the value to 1 on [ArrowUp]', async () => { const el = await fixture(defaultInputStepper); @@ -71,7 +116,7 @@ describe('', () => { expect(el.value).to.equal(''); el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); await el.updateComplete; - expect(el.value).to.equal('-1'); + expect(el.value).to.equal('−1'); }); it('should increment the value to minValue on [ArrowDown] if value is below min', async () => { @@ -95,7 +140,7 @@ describe('', () => { expect(el.value).to.equal(''); const decrementButton = el.querySelector('[slot=prefix]'); decrementButton?.dispatchEvent(new Event('click')); - expect(el.value).to.equal('-1'); + expect(el.value).to.equal('−1'); }); it('fires one "user-input-changed" event on + button click', async () => { @@ -143,7 +188,7 @@ describe('', () => { decrementButton?.dispatchEvent(new Event('focus')); decrementButton?.dispatchEvent(new Event('click')); decrementButton?.dispatchEvent(new Event('blur')); - expect(el.value).to.equal('-1'); + expect(el.value).to.equal('−1'); expect(blurSpy.calledOnce).to.be.true; expect(el.touched).to.be.true;