Skip to content

Commit

Permalink
fix(input): isClearable & add test cases to input (#2796)
Browse files Browse the repository at this point in the history
* feat(input): add @nextui-org/use-safe-layout-effect

* fix(input): use useSafeLayoutEffect to handle react-hook-form case

* feat(input): add isClearable test case

* feat(input): add react-hook-form to dev dependencies for storybook

* feat(input): add WithReactHookForm to input storybook

* feat(changeset): fixes isClearable function in input

* chore(changeset): update changeset message

* refactor(input): revise input test
  • Loading branch information
wingkwong committed Apr 22, 2024
1 parent 1cc5215 commit 3552353
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 2 deletions.
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)
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));
};

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.

0 comments on commit 3552353

Please sign in to comment.