diff --git a/.changeset/unlucky-mice-ring.md b/.changeset/unlucky-mice-ring.md new file mode 100644 index 0000000000..74543e16b5 --- /dev/null +++ b/.changeset/unlucky-mice-ring.md @@ -0,0 +1,10 @@ +--- +"sit-onyx": major +--- + +feat(OnyxStepper): update OnyxStepper API + +- remove property `step`, use `stepSize` instead +- remove property `stripStep`, use `validStepSize` instead. User inputs will no longer be manipulated, instead an error will be shown +- changed logic of `precision` property. Now determined numbers of decimal places to show. Is no longer the default value of `stepSize` property. +- fix bug that decimal value is not displayed correctly when `precision` is not set diff --git a/apps/demo-app/src/components/form-demo/FormDemo.vue b/apps/demo-app/src/components/form-demo/FormDemo.vue index da8fb78c5b..6741dea560 100644 --- a/apps/demo-app/src/components/form-demo/FormDemo.vue +++ b/apps/demo-app/src/components/form-demo/FormDemo.vue @@ -127,7 +127,7 @@ const radioOptions: RadioButtonOption[] = [ :minlength="5" required /> - + - - { }); }); -test("should emit events", async ({ mount, makeAxeBuilder }) => { +test("should emit events", async ({ mount }) => { const events = { - updateModelValue: [] as string[], + updateModelValue: [] as number[], }; // ARRANGE @@ -254,12 +254,6 @@ test("should emit events", async ({ mount, makeAxeBuilder }) => { updateModelValue: [], }); - // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); - - // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - const inputElement = component.getByLabel("Label"); // ACT @@ -290,13 +284,10 @@ test("should have aria-label if label is hidden", async ({ mount, makeAxeBuilder await expect(component.getByLabel("Test label")).toBeAttached(); }); -test("should increment/decrement value by one on counter button click", async ({ - mount, - makeAxeBuilder, -}) => { +test("should increment/decrement value on counter button click", async ({ mount }) => { // ARRANGE const on = { - "update:modelValue": (newValue) => { + "update:modelValue": (newValue?: number) => { component.update({ props: { modelValue: newValue, @@ -310,39 +301,29 @@ test("should increment/decrement value by one on counter button click", async ({ props: { label: "Test label", style: "width: 12rem;", + stepSize: 2, }, on, }); const input = component.getByLabel("Test label"); - const incrementButton = component.getByLabel("Increment by 1"); - const decrementButton = component.getByLabel("Decrement by 1"); - - // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); + const incrementButton = component.getByLabel("Increment by 2"); + const decrementButton = component.getByLabel("Decrement by 2"); // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - await expect(component.getByLabel("Test label")).toBeAttached(); - - await input.click(); - await input.fill("0"); - await expect(input).toHaveValue("0"); + await expect(input).toHaveValue(""); await incrementButton.click(); - await expect(input).toHaveValue("1"); + await expect(input).toHaveValue("2"); await decrementButton.click(); await expect(input).toHaveValue("0"); }); -test("should increment/decrement value by step on counter button click", async ({ - mount, - makeAxeBuilder, -}) => { +test("should not allow entering value over the max value that has been set", async ({ mount }) => { // ARRANGE const on = { - "update:modelValue": (newValue) => { + "update:modelValue": (newValue?: number) => { component.update({ props: { modelValue: newValue, @@ -356,40 +337,28 @@ test("should increment/decrement value by step on counter button click", async ( props: { label: "Test label", style: "width: 12rem;", + max: 3, stepSize: 2, }, on, }); const input = component.getByLabel("Test label"); - const addButton = component.getByLabel("Increment"); - const substractButton = component.getByLabel("Decrement by 2"); - - // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); + const addButton = component.getByLabel("Increment by 2"); // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - await expect(component.getByLabel("Test label")).toBeAttached(); - - await input.click(); - await input.fill("0"); - await expect(input).toHaveValue("0"); - await addButton.click(); await expect(input).toHaveValue("2"); - await substractButton.click(); - await expect(input).toHaveValue("0"); + await addButton.click(); + await expect(input).toHaveValue("3"); + await expect(addButton).toBeDisabled(); }); -test("should not allow entering value over the max value that has been set", async ({ - mount, - makeAxeBuilder, -}) => { +test("should not allow entering value lower the min value that has been set", async ({ mount }) => { // ARRANGE const on = { - "update:modelValue": (newValue) => { + "update:modelValue": (newValue?: number) => { component.update({ props: { modelValue: newValue, @@ -403,41 +372,34 @@ test("should not allow entering value over the max value that has been set", asy props: { label: "Test label", style: "width: 12rem;", - max: 2, + min: 2, + stepSize: 2, + modelValue: 5, }, on, }); const input = component.getByLabel("Test label"); - const addButton = component.getByLabel("Increment by 1"); - - // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); + const substractButton = component.getByLabel("Decrement by 2"); // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - await expect(component.getByLabel("Test label")).toBeAttached(); - - await input.click(); - await input.fill("0"); - await expect(input).toHaveValue("0"); - - await addButton.click(); - await expect(input).toHaveValue("1"); + await substractButton.click(); + await expect(input).toHaveValue("3"); - await addButton.click(); + await substractButton.click(); await expect(input).toHaveValue("2"); - - await expect(addButton).toBeDisabled(); + await expect(substractButton).toBeDisabled(); }); -test("should not allow entering value lower the min value that has been set", async ({ +test("Should correctly display decimal places according to the defined precision", async ({ mount, - makeAxeBuilder, }) => { + const modelValueUpdates = [] as (number | undefined)[]; + // ARRANGE const on = { - "update:modelValue": (newValue) => { + "update:modelValue": (newValue?: number) => { + modelValueUpdates.push(newValue); component.update({ props: { modelValue: newValue, @@ -451,77 +413,56 @@ test("should not allow entering value lower the min value that has been set", as props: { label: "Test label", style: "width: 12rem;", - min: 2, - modelValue: 4, + precision: 2, + modelValue: 1, }, on, }); const input = component.getByLabel("Test label"); - const substractButton = component.getByLabel("Decrement by 1"); - - // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - await expect(component.getByLabel("Test label")).toBeAttached(); + await expect(input).toHaveValue("1.00"); - await substractButton.click(); - await expect(input).toHaveValue("3"); + // ACT + await input.fill("3.1"); + await input.blur(); - await substractButton.click(); - await expect(input).toHaveValue("2"); + // ASSERT + await expect(input).toHaveValue("3.10"); + expect(modelValueUpdates).toStrictEqual([3.1]); - await expect(substractButton).toBeDisabled(); -}); + // ACT + await input.fill("3.106"); + await input.blur(); -test("Should display the same number of decimal places as the smallest possible step", async ({ - mount, - makeAxeBuilder, -}) => { - // ARRANGE - const on = { - "update:modelValue": (newValue) => { - component.update({ - props: { - modelValue: newValue, - }, - on, - }); - }, - }; + // ASSERT + await expect(input).toHaveValue("3.11"); + expect(modelValueUpdates).toStrictEqual([3.1, 3.11]); - const component = await mount(OnyxStepper, { - props: { - label: "Test label", - style: "width: 12rem;", - precision: 0.01, - }, - on, - }); + // ACT + await component.update({ props: { precision: 1 }, on }); - const input = component.locator("input"); + // ASSERT + await expect(input).toHaveValue("3.1"); // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); + await component.update({ props: { precision: -1 }, on }); + await input.fill("6"); + await input.blur(); // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - - await input.fill("1"); - await input.dispatchEvent("change"); - await expect(input).toHaveValue("1.00"); + await expect(input).toHaveValue("10"); + expect(modelValueUpdates).toStrictEqual([3.1, 3.11, 10]); }); -test("Should display an error if the value is not a multiple of the precision", async ({ +test("Should display an error if the value is not a multiple of validStepSize", async ({ page, mount, - makeAxeBuilder, }) => { // ARRANGE const on = { - "update:modelValue": (newValue: number) => { + "update:modelValue": (newValue?: number) => { component.update({ props: { modelValue: newValue, @@ -536,7 +477,7 @@ test("Should display an error if the value is not a multiple of the precision", label: "Test label", style: "width: 12rem;", modelValue: 1, - precision: 0.5, + validStepSize: 0.5, }, on, }); @@ -545,56 +486,20 @@ test("Should display an error if the value is not a multiple of the precision", const errorMessage = component.locator(".onyx-form-element__error-message"); // ACT - const accessibilityScanResults = await makeAxeBuilder().analyze(); - - // ASSERT - expect(accessibilityScanResults.violations).toEqual([]); - await input.fill("1"); await page.keyboard.press("Enter"); + // ASSERT await expect(errorMessage).toBeHidden(); - await page.keyboard.press("Enter"); + // ACT await input.fill("3.6"); await page.keyboard.press("Enter"); + // ASSERT await expect(errorMessage).toBeVisible(); -}); - -test("Should revert to the last valid input if the current input is invalid in stripStep mode", async ({ - page, - mount, -}) => { - // ARRANGE - const on = { - "update:modelValue": (newValue: number) => { - component.update({ - props: { - modelValue: newValue, - }, - on, - }); - }, - }; - - const component = await mount(OnyxStepper, { - props: { - label: "Test label", - style: "width: 12rem;", - precision: 0.5, - stripStep: true, - }, - on, - }); - - const input = component.locator("input"); - - await input.fill("1"); - await page.keyboard.press("Enter"); - await expect(input).toHaveValue("1.0"); - await page.keyboard.press("Enter"); - await input.fill("1.6"); - await page.keyboard.press("Enter"); - await expect(input).toHaveValue("1.0"); + await expect(errorMessage).toContainText("Invalid number"); + await expect(errorMessage).toContainText( + "Please enter a valid number, that is a multiple of 0.5.", + ); }); diff --git a/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.stories.ts b/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.stories.ts index 815d97a4e1..1584deffce 100644 --- a/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.stories.ts +++ b/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.stories.ts @@ -28,6 +28,17 @@ export const Default = { }, } satisfies Story; +/** + * This example shows a stepper with precision two always show two decimal places. + */ +export const Precision = { + args: { + label: "Currency", + modelValue: 5, + precision: 2, + }, +} satisfies Story; + /** * This example shows the stepper with a placeholder. */ diff --git a/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.vue b/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.vue index 5c2855be10..cdb11020b0 100644 --- a/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.vue +++ b/packages/sit-onyx/src/components/OnyxStepper/OnyxStepper.vue @@ -1,36 +1,38 @@