Skip to content

Commit

Permalink
fix(form): Maintain Floating Label for Invalid Numbers
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mlaursen committed Nov 22, 2020
1 parent 23d92dd commit 2443f9a
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 2 deletions.
91 changes: 90 additions & 1 deletion packages/form/src/text-field/__tests__/TextField.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { render } from "@testing-library/react";
import { fireEvent, render } from "@testing-library/react";

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

Expand All @@ -19,4 +19,93 @@ describe("TextField", () => {
expect(container).toMatchSnapshot();
expect(document.getElementById("field")).toHaveAttribute("disabled");
});

it("should correctly call the onChange event", () => {
const onChange = jest.fn();
const { getByRole } = render(
<TextField id="field" label="Label" onChange={onChange} />
);
const field = getByRole("textbox");
expect(onChange).not.toBeCalled();

fireEvent.change(field, { target: { value: "2" } });
expect(onChange).toBeCalledTimes(1);
});

it("should add the inactive floating label state when a number text field is blurred while containing an invalid value", () => {
const { getByRole, getByText } = render(
<TextField id="text-field" label="Label" type="number" defaultValue="" />
);

const field = getByRole("spinbutton") as HTMLInputElement;
const label = getByText("Label");
expect(field).toHaveAttribute("value", "");
expect(label.className).not.toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

fireEvent.focus(field);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

fireEvent.change(field, { target: { value: "123" } });
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

// TODO: Look into writing real browser tests since this isn't implemented in JSDOM
Object.defineProperty(field.validity, "badInput", {
writable: true,
value: true,
});
expect(field.validity.badInput).toBe(true);
fireEvent.change(field, {
target: { value: "123-" },
});
expect(field.validity.badInput).toBe(true);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

fireEvent.blur(field);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).toContain("rmd-floating-label--inactive");
});

it("should not add the inactive floating label state when a non-number type has a badInput validity", () => {
const { getByRole, getByText } = render(
<TextField id="text-field" label="Label" type="url" defaultValue="" />
);

const field = getByRole("textbox") as HTMLInputElement;
const label = getByText("Label");
expect(field).toHaveAttribute("value", "");
expect(label.className).not.toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

fireEvent.focus(field);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

// TODO: Look into writing real browser tests since this isn't implemented in JSDOM
Object.defineProperty(field.validity, "badInput", {
writable: true,
value: true,
});
fireEvent.change(field, { target: { value: "123" } });
expect(field.validity.badInput).toBe(true);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

fireEvent.blur(field);
expect(field.validity.badInput).toBe(true);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).toContain("rmd-floating-label--inactive");

fireEvent.focus(field);
expect(label.className).toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");

fireEvent.change(field, { target: { value: "" } });
fireEvent.blur(field);
expect(label.className).not.toContain("rmd-floating-label--active");
expect(label.className).not.toContain("rmd-floating-label--inactive");
});
});
5 changes: 4 additions & 1 deletion packages/form/src/text-field/useValuedState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ export function useValuedState<T extends TextElement>({

if (event.currentTarget.value.length > 0) {
enable();
} else {
} else if (
event.currentTarget.getAttribute("type") !== "number" ||
!event.currentTarget.validity.badInput
) {
disable();
}
},
Expand Down

0 comments on commit 2443f9a

Please sign in to comment.