Skip to content

Commit 2443f9a

Browse files
committed
fix(form): Maintain Floating Label for Invalid Numbers
Browsers will try to prevent invalid characters from being entered into an `<input type="number" />`, but they are unable to prevent numbers like: `"--00"`, `"0-0"`, or `"123-"`. When these types of numbers are entered, the input actually reports the value as the empty string instead of the current text value which caused the floating label to cover the input when blurred.
1 parent 23d92dd commit 2443f9a

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

packages/form/src/text-field/__tests__/TextField.tsx

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import { render } from "@testing-library/react";
2+
import { fireEvent, render } from "@testing-library/react";
33

44
import { TextField } from "../TextField";
55

@@ -19,4 +19,93 @@ describe("TextField", () => {
1919
expect(container).toMatchSnapshot();
2020
expect(document.getElementById("field")).toHaveAttribute("disabled");
2121
});
22+
23+
it("should correctly call the onChange event", () => {
24+
const onChange = jest.fn();
25+
const { getByRole } = render(
26+
<TextField id="field" label="Label" onChange={onChange} />
27+
);
28+
const field = getByRole("textbox");
29+
expect(onChange).not.toBeCalled();
30+
31+
fireEvent.change(field, { target: { value: "2" } });
32+
expect(onChange).toBeCalledTimes(1);
33+
});
34+
35+
it("should add the inactive floating label state when a number text field is blurred while containing an invalid value", () => {
36+
const { getByRole, getByText } = render(
37+
<TextField id="text-field" label="Label" type="number" defaultValue="" />
38+
);
39+
40+
const field = getByRole("spinbutton") as HTMLInputElement;
41+
const label = getByText("Label");
42+
expect(field).toHaveAttribute("value", "");
43+
expect(label.className).not.toContain("rmd-floating-label--active");
44+
expect(label.className).not.toContain("rmd-floating-label--inactive");
45+
46+
fireEvent.focus(field);
47+
expect(label.className).toContain("rmd-floating-label--active");
48+
expect(label.className).not.toContain("rmd-floating-label--inactive");
49+
50+
fireEvent.change(field, { target: { value: "123" } });
51+
expect(label.className).toContain("rmd-floating-label--active");
52+
expect(label.className).not.toContain("rmd-floating-label--inactive");
53+
54+
// TODO: Look into writing real browser tests since this isn't implemented in JSDOM
55+
Object.defineProperty(field.validity, "badInput", {
56+
writable: true,
57+
value: true,
58+
});
59+
expect(field.validity.badInput).toBe(true);
60+
fireEvent.change(field, {
61+
target: { value: "123-" },
62+
});
63+
expect(field.validity.badInput).toBe(true);
64+
expect(label.className).toContain("rmd-floating-label--active");
65+
expect(label.className).not.toContain("rmd-floating-label--inactive");
66+
67+
fireEvent.blur(field);
68+
expect(label.className).toContain("rmd-floating-label--active");
69+
expect(label.className).toContain("rmd-floating-label--inactive");
70+
});
71+
72+
it("should not add the inactive floating label state when a non-number type has a badInput validity", () => {
73+
const { getByRole, getByText } = render(
74+
<TextField id="text-field" label="Label" type="url" defaultValue="" />
75+
);
76+
77+
const field = getByRole("textbox") as HTMLInputElement;
78+
const label = getByText("Label");
79+
expect(field).toHaveAttribute("value", "");
80+
expect(label.className).not.toContain("rmd-floating-label--active");
81+
expect(label.className).not.toContain("rmd-floating-label--inactive");
82+
83+
fireEvent.focus(field);
84+
expect(label.className).toContain("rmd-floating-label--active");
85+
expect(label.className).not.toContain("rmd-floating-label--inactive");
86+
87+
// TODO: Look into writing real browser tests since this isn't implemented in JSDOM
88+
Object.defineProperty(field.validity, "badInput", {
89+
writable: true,
90+
value: true,
91+
});
92+
fireEvent.change(field, { target: { value: "123" } });
93+
expect(field.validity.badInput).toBe(true);
94+
expect(label.className).toContain("rmd-floating-label--active");
95+
expect(label.className).not.toContain("rmd-floating-label--inactive");
96+
97+
fireEvent.blur(field);
98+
expect(field.validity.badInput).toBe(true);
99+
expect(label.className).toContain("rmd-floating-label--active");
100+
expect(label.className).toContain("rmd-floating-label--inactive");
101+
102+
fireEvent.focus(field);
103+
expect(label.className).toContain("rmd-floating-label--active");
104+
expect(label.className).not.toContain("rmd-floating-label--inactive");
105+
106+
fireEvent.change(field, { target: { value: "" } });
107+
fireEvent.blur(field);
108+
expect(label.className).not.toContain("rmd-floating-label--active");
109+
expect(label.className).not.toContain("rmd-floating-label--inactive");
110+
});
22111
});

packages/form/src/text-field/useValuedState.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ export function useValuedState<T extends TextElement>({
4343

4444
if (event.currentTarget.value.length > 0) {
4545
enable();
46-
} else {
46+
} else if (
47+
event.currentTarget.getAttribute("type") !== "number" ||
48+
!event.currentTarget.validity.badInput
49+
) {
4750
disable();
4851
}
4952
},

0 commit comments

Comments
 (0)