Skip to content

Commit

Permalink
fix(number-field): value stuck on NaN (#370)
Browse files Browse the repository at this point in the history
* fix(number-field): value stuck on NaN

* fix(number-field): locale decimal value

Co-authored-by: =?UTF-8?q?Marius=20D=C3=B6rbandt?= <[email protected]>

* chore: prepare release `0.12.5`

---------

Co-authored-by: =?UTF-8?q?Marius=20D=C3=B6rbandt?= <[email protected]>
  • Loading branch information
jer3m01 and MariusDoe authored Mar 14, 2024
1 parent 5451a4d commit eae01e1
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 17 deletions.
10 changes: 10 additions & 0 deletions .changeset/orange-ducks-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@kobalte/core": patch
---

## v0.12.5 (March 14, 2024)

**Bug fixes**

- NumberField: value stuck on NaN ([#364](https://github.com/kobaltedev/kobalte/pull/370))
- NumberField: locale decimal value ([#369](https://github.com/kobaltedev/kobalte/pull/369))
2 changes: 1 addition & 1 deletion apps/docs/src/VERSIONS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ export const LATEST_CORE_CHANGELOG_URL = `/docs/changelog/${CORE_VERSIONS[0].rep
"-",
)}`;

export const LATEST_CORE_VERSION_NAME = "v0.12.4";
export const LATEST_CORE_VERSION_NAME = "v0.12.5";
7 changes: 7 additions & 0 deletions apps/docs/src/routes/docs/changelog/0-12-x.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# v0.12.x

## v0.12.5 (March 14, 2024)

**Bug fixes**

- NumberField: value stuck on NaN ([#364](https://github.com/kobaltedev/kobalte/pull/370))
- NumberField: locale decimal value ([#369](https://github.com/kobaltedev/kobalte/pull/369))

## v0.12.4 (March 9, 2024)

**New features**
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/number-field/number-field-hidden-input.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { callHandler, mergeRefs, visuallyHiddenStyles } from "@kobalte/utils";
import { ComponentProps, splitProps } from "solid-js";
import { ComponentProps, batch, splitProps } from "solid-js";

import { useFormControlContext } from "../form-control";
import { useNumberFieldContext } from "./number-field-context";
Expand Down Expand Up @@ -28,8 +28,10 @@ export function NumberFieldHiddenInput(props: NumberFieldHiddenInputProps) {
onChange={(e) => {
callHandler(e, local.onChange);
// enable form autofill
context.setValue((e.target as HTMLInputElement).value);
context.format();
batch(() => {
context.setValue((e.target as HTMLInputElement).value);
context.format();
});
}}
{...others}
/>
Expand Down
35 changes: 25 additions & 10 deletions packages/core/src/number-field/number-field-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@kobalte/utils";
import {
JSX,
batch,
createEffect,
createMemo,
createSignal,
Expand Down Expand Up @@ -164,15 +165,17 @@ export function NumberFieldRoot(props: NumberFieldRootProps) {
});

const parseRawValue = (value: string | number | undefined) =>
local.format
? numberParser().parse(String(value ?? ""))
local.format && typeof value !== "number"
? numberParser().parse(value ?? "")
: Number(value ?? "");

const [value, setValue] = createControllableSignal({
value: () => local.value,
defaultValue: () => local.defaultValue ?? local.rawValue,
onChange: (value) => {
local.onChange?.(String(value));
local.onChange?.(
typeof value === "number" ? numberFormatter().format(value) : value,
);
local.onRawValueChange?.(parseRawValue(value));
},
});
Expand All @@ -185,8 +188,6 @@ export function NumberFieldRoot(props: NumberFieldRootProps) {
() => ref,
() => {
setValue(local.defaultValue ?? "");
// @ts-ignore
setValue(local.defaultValue);
},
);

Expand Down Expand Up @@ -226,6 +227,7 @@ export function NumberFieldRoot(props: NumberFieldRootProps) {

if (Number.isNaN(rawValue)) {
if (hiddenInputRef()) hiddenInputRef()!.value = "";
local.onRawValueChange?.(rawValue);
return;
}

Expand Down Expand Up @@ -255,11 +257,21 @@ export function NumberFieldRoot(props: NumberFieldRootProps) {
hiddenInputRef,
setHiddenInputRef,
varyValue: (offset) => {
let rawValue = context.rawValue() || 0;
let rawValue = context.rawValue() ?? 0;
if (Number.isNaN(rawValue)) rawValue = 0;

context.setValue(rawValue + offset);
context.format();
batch(() => {
const decimals = Math.max(
local.formatOptions?.minimumFractionDigits ?? 0,
local.formatOptions?.maximumFractionDigits ?? 3,
);
const newValue = Number.parseFloat(
(rawValue + offset).toFixed(decimals),
);

context.setValue(newValue);
context.format();
});
},
};

Expand All @@ -268,8 +280,11 @@ export function NumberFieldRoot(props: NumberFieldRootProps) {
() => local.rawValue,
(rawValue) => {
if (rawValue !== context.rawValue()) {
setValue(rawValue ?? "");
context.format();
if (Number.isNaN(rawValue)) return;
batch(() => {
setValue(rawValue ?? "");
context.format();
});
}
},
{ defer: true },
Expand Down
69 changes: 66 additions & 3 deletions packages/core/src/number-field/number-field.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { installPointerEvent } from "@kobalte/tests";
import { render } from "@solidjs/testing-library";
import { fireEvent } from "@solidjs/testing-library";
import { fireEvent, render } from "@solidjs/testing-library";
import userEvent from "@testing-library/user-event";
import { expect, vi } from "vitest";

import * as NumberField from ".";
import { I18nProvider } from "../i18n";

describe("NumberField", () => {
it("can have a default value", async () => {
Expand Down Expand Up @@ -662,4 +661,68 @@ describe("NumberField", () => {

expect(document.activeElement).toBe(input);
});

it("formats decimal value correctly for locale", async () => {
const { getByRole } = render(() => (
<I18nProvider locale="de">
<NumberField.Root value={1.1}>
<NumberField.Input />
</NumberField.Root>
</I18nProvider>
));

const input = getByRole("spinbutton") as HTMLInputElement;

expect(input.value).toBe("1,1");
});

it("formats decimal rawValue correctly for locale", async () => {
const { getByRole } = render(() => (
<I18nProvider locale="de">
<NumberField.Root rawValue={1.1}>
<NumberField.Input />
</NumberField.Root>
</I18nProvider>
));

const input = getByRole("spinbutton") as HTMLInputElement;

expect(input.value).toBe("1,1");
});

it("increments decimal values correctly for locale", async () => {
const { getByRole, getByTestId } = render(() => (
<I18nProvider locale="de">
<NumberField.Root defaultValue={1.1}>
<NumberField.Input />
<NumberField.IncrementTrigger data-testId="trigger" />
</NumberField.Root>
</I18nProvider>
));

const input = getByRole("spinbutton") as HTMLInputElement;
const trigger = getByTestId("trigger") as HTMLButtonElement;

trigger.click();

expect(input.value).toBe("2,1");
});

it("decrements decimal values correctly for locale", async () => {
const { getByRole, getByTestId } = render(() => (
<I18nProvider locale="de">
<NumberField.Root defaultValue={1.1}>
<NumberField.Input />
<NumberField.DecrementTrigger data-testId="trigger" />
</NumberField.Root>
</I18nProvider>
));

const input = getByRole("spinbutton") as HTMLInputElement;
const trigger = getByTestId("trigger") as HTMLButtonElement;

trigger.click();

expect(input.value).toBe("0,1");
});
});

0 comments on commit eae01e1

Please sign in to comment.