Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(input): isClearable & add test cases to input #2796

Merged
merged 8 commits into from
Apr 22, 2024
Merged
5 changes: 5 additions & 0 deletions .changeset/gold-dolphins-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nextui-org/input": patch
---

Fixes the isClearable function in the input component (#2791)
wingkwong marked this conversation as resolved.
Show resolved Hide resolved
29 changes: 29 additions & 0 deletions packages/components/input/__tests__/input.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from "react";
import {render} from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import {Input} from "../src";

Expand Down Expand Up @@ -116,4 +117,32 @@ describe("Input", () => {

expect(ref.current?.value)?.toBe(value);
});

it("should clear the value and onClear is triggered", async () => {
const onClear = jest.fn();

const ref = React.createRef<HTMLInputElement>();

const {getByRole} = render(
<Input
ref={ref}
isClearable
defaultValue="junior@nextui.org"
label="test input"
onClear={onClear}
/>,
);

const clearButton = getByRole("button");

expect(clearButton).not.toBeNull();

const user = userEvent.setup();

await user.click(clearButton);

expect(ref.current?.value)?.toBe("");

expect(onClear).toHaveBeenCalledTimes(1);
});
});
4 changes: 3 additions & 1 deletion packages/components/input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@nextui-org/react-utils": "workspace:*",
"@nextui-org/shared-icons": "workspace:*",
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/use-safe-layout-effect": "workspace:*",
"@react-aria/focus": "^3.16.2",
"@react-aria/interactions": "^3.21.1",
"@react-aria/textfield": "^3.14.3",
Expand All @@ -57,7 +58,8 @@
"@nextui-org/system": "workspace:*",
"clean-package": "2.2.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"react-hook-form": "^7.51.3"
},
"clean-package": "../../../clean-package.config.json"
}
12 changes: 11 additions & 1 deletion packages/components/input/src/use-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {InputVariantProps, SlotsToClasses, InputSlots} from "@nextui-org/th
import type {AriaTextFieldOptions} from "@react-aria/textfield";

import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect";
import {AriaTextFieldProps} from "@react-types/textfield";
import {useFocusRing} from "@react-aria/focus";
import {input} from "@nextui-org/theme";
Expand Down Expand Up @@ -143,6 +144,15 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
domRef.current?.focus();
}, [setInputValue, onClear]);

// if we use `react-hook-form`, it will set the input value using the ref in register
// i.e. setting ref.current.value to something which is uncontrolled
// hence, sync the state with `ref.current.value`
useSafeLayoutEffect(() => {
if (!domRef.current) return;

setInputValue(domRef.current.value);
}, [domRef.current]);

const {
labelProps,
inputProps,
Expand All @@ -156,7 +166,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
...originalProps,
validationBehavior: "native",
autoCapitalize: originalProps.autoCapitalize as AutoCapitalize,
value: domRef?.current?.value ?? inputValue,
value: inputValue,
"aria-label": safeAriaLabel(
originalProps?.["aria-label"],
originalProps?.label,
Expand Down
41 changes: 41 additions & 0 deletions packages/components/input/stories/input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CloseFilledIcon,
} from "@nextui-org/shared-icons";
import {button} from "@nextui-org/theme";
import {useForm} from "react-hook-form";

import {Input, InputProps, useInput} from "../src";

Expand Down Expand Up @@ -474,6 +475,38 @@ const CustomWithHooksTemplate = (args: InputProps) => {
);
};

const WithReactHookFormTemplate = (args: InputProps) => {
const {
register,
formState: {errors},
handleSubmit,
} = useForm({
defaultValues: {
withDefaultValue: "wkw",
withoutDefaultValue: "",
requiredField: "",
},
});

const onSubmit = (data: any) => {
// eslint-disable-next-line no-console
console.log(data);
alert("Submitted value: " + JSON.stringify(data));
wingkwong marked this conversation as resolved.
Show resolved Hide resolved
};

return (
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
<Input isClearable label="With default value" {...register("withDefaultValue")} />
<Input {...args} label="Without default value" {...register("withoutDefaultValue")} />
<Input {...args} label="Required" {...register("requiredField", {required: true})} />
{errors.requiredField && <span className="text-danger">This field is required</span>}
<button className={button({class: "w-fit"})} type="submit">
Submit
</button>
</form>
);
};

export const Default = {
render: MirrorTemplate,

Expand Down Expand Up @@ -706,3 +739,11 @@ export const CustomWithHooks = {
),
},
};

export const WithReactHookForm = {
render: WithReactHookFormTemplate,

args: {
...defaultProps,
},
};
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.