-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(textarea): introduce isClearable #3477
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
Changes from all commits
6c2d9d2
de5b54e
6f92cdb
e471830
36b4540
bc6f329
41a29ff
bc18964
ac31ed0
815193a
9ff14d5
1ce8a2b
ecd3fa2
7e1bd65
989b309
590587f
7eabb9f
65f4074
073599e
ac1ff15
6295aad
811f214
b3dcdd6
a1fe23d
7b136d9
80eead7
d57ed70
a457246
a4c2310
c2b16d4
e2c276a
8bd6f64
ade96c0
7812fce
d6d8711
8a2487c
05df8ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| "@nextui-org/input": patch | ||
| "@nextui-org/shared-icons": patch | ||
| "@nextui-org/theme": patch | ||
| --- | ||
|
|
||
| introduce `isClearable` to Textarea component (#2348, #2112) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import {Textarea} from "@nextui-org/react"; | ||
|
|
||
| export default function App() { | ||
| return ( | ||
| <Textarea | ||
| isClearable | ||
| className="max-w-xs" | ||
| defaultValue="junior@nextui.org" | ||
| label="Email" | ||
| placeholder="Enter your email" | ||
| variant="bordered" | ||
| // eslint-disable-next-line no-console | ||
| onClear={() => console.log("textarea cleared")} | ||
| /> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import App from "./clear-button.raw.jsx?raw"; | ||
|
|
||
| const react = { | ||
| "/App.jsx": App, | ||
| }; | ||
|
|
||
| export default { | ||
| ...react, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,6 +92,12 @@ You can use the `value` and `onValueChange` properties to control the input valu | |
| > **Note**: NextUI `Textarea` also supports native events like `onChange`, useful for form libraries | ||
| > such as [Formik](https://formik.org/) and [React Hook Form](https://react-hook-form.com/). | ||
|
|
||
| ### Clear Button | ||
|
|
||
| If you pass the `isClearable` property to the textarea, it will have a clear button at the | ||
| end of the textarea, it will be visible when the textarea has a value. | ||
|
|
||
| <CodeDemo title="Clear Button" files={textareaContent.clearButton} /> | ||
|
Comment on lines
+95
to
+100
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @IsDyh01 Please move this above the description example. |
||
| ## Slots | ||
|
|
||
| - **base**: Input wrapper, it handles alignment, placement, and general appearance. | ||
|
|
@@ -100,6 +106,7 @@ You can use the `value` and `onValueChange` properties to control the input valu | |
| - **input**: The textarea input element. | ||
| - **description**: The description of the textarea. | ||
| - **errorMessage**: The error message of the textarea. | ||
| - **headerWrapper**: Wraps the `label` and the `clearButton`. | ||
|
|
||
| <Spacer y={4} /> | ||
|
|
||
|
|
@@ -163,6 +170,7 @@ You can use the `value` and `onValueChange` properties to control the input valu | |
| | isRequired | `boolean` | Whether user input is required on the textarea before form submission. | `false` | | ||
| | isReadOnly | `boolean` | Whether the textarea can be selected but not changed by the user. | | | ||
| | isDisabled | `boolean` | Whether the textarea is disabled. | `false` | | ||
| | isClearable | `boolean` | Whether the textarea should have a clear button. | `false` | | ||
| | isInvalid | `boolean` | Whether the textarea is invalid. | `false` | | ||
| | validationState | `valid` \| `invalid` | Whether the textarea should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | | ||
| | disableAutosize | `boolean` | Whether the textarea auto vertically resize should be disabled. | `false` | | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import * as React from "react"; | ||
| import {render} from "@testing-library/react"; | ||
| import userEvent from "@testing-library/user-event"; | ||
|
|
||
| import {Textarea} from "../src"; | ||
|
|
||
| describe("Textarea", () => { | ||
|
IsDyh01 marked this conversation as resolved.
|
||
| it("should clear the value and onClear is triggered", async () => { | ||
| const onClear = jest.fn(); | ||
|
|
||
| const ref = React.createRef<HTMLTextAreaElement>(); | ||
|
|
||
| const {getByRole} = render( | ||
| <Textarea | ||
| ref={ref} | ||
| isClearable | ||
| defaultValue="junior@nextui.org" | ||
| label="test textarea" | ||
| 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); | ||
| }); | ||
|
IsDyh01 marked this conversation as resolved.
|
||
|
|
||
| it("should disable clear button when isReadOnly is true", async () => { | ||
| const onClear = jest.fn(); | ||
|
|
||
| const ref = React.createRef<HTMLTextAreaElement>(); | ||
|
|
||
| const {getByRole} = render( | ||
| <Textarea | ||
| ref={ref} | ||
| isClearable | ||
| isReadOnly | ||
| defaultValue="readOnly test for clear button" | ||
| label="test textarea" | ||
| onClear={onClear} | ||
| />, | ||
| ); | ||
|
|
||
| const clearButton = getByRole("button")!; | ||
|
|
||
| expect(clearButton).not.toBeNull(); | ||
|
|
||
| const user = userEvent.setup(); | ||
|
|
||
| await user.click(clearButton); | ||
|
|
||
| expect(onClear).toHaveBeenCalledTimes(0); | ||
| }); | ||
|
IsDyh01 marked this conversation as resolved.
|
||
|
|
||
| it("should appear clear button when just define onClear but not define isClearable", async () => { | ||
| const onClear = jest.fn(); | ||
|
|
||
| const ref = React.createRef<HTMLTextAreaElement>(); | ||
|
|
||
| const {getByRole} = render( | ||
| <Textarea | ||
| ref={ref} | ||
| defaultValue="junior@nextui.org" | ||
| label="test textarea" | ||
| onClear={onClear} | ||
| />, | ||
| ); | ||
|
|
||
| const clearButton = getByRole("button"); | ||
|
|
||
| expect(clearButton).not.toBeNull(); | ||
| }); | ||
|
IsDyh01 marked this conversation as resolved.
|
||
| }); | ||
|
IsDyh01 marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -371,7 +371,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML | |
| "data-has-start-content": dataAttr(hasStartContent), | ||
| "data-has-end-content": dataAttr(!!endContent), | ||
| className: slots.input({ | ||
| class: clsx(classNames?.input, isFilled ? "is-filled" : ""), | ||
| class: clsx(classNames?.input, isFilled ? "is-filled" : "", isMultiline ? "pe-0" : ""), | ||
| }), | ||
| ...mergeProps( | ||
| focusProps, | ||
|
|
@@ -414,7 +414,11 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML | |
| "data-focus-visible": dataAttr(isFocusVisible), | ||
| "data-focus": dataAttr(isFocused), | ||
| className: slots.inputWrapper({ | ||
| class: clsx(classNames?.inputWrapper, isFilled ? "is-filled" : ""), | ||
| class: clsx( | ||
| classNames?.inputWrapper, | ||
| isFilled ? "is-filled" : "", | ||
| isMultiline ? "flex-col items-start gap-0" : "", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @IsDyh01 Tailwind classes should be exclusively defined in the theme package, specifically in the e.g. |
||
| ), | ||
| }), | ||
| ...mergeProps(props, hoverProps), | ||
| onClick: (e) => { | ||
|
|
@@ -518,13 +522,29 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML | |
| "aria-label": "clear input", | ||
| "data-slot": "clear-button", | ||
| "data-focus-visible": dataAttr(isClearButtonFocusVisible), | ||
| className: slots.clearButton({class: clsx(classNames?.clearButton, props?.className)}), | ||
| className: slots.clearButton({ | ||
| class: clsx( | ||
| classNames?.clearButton, | ||
| props?.className, | ||
| isMultiline ? (isFilled ? "relative block opacity-100 p-0 -m-0 end-0" : "") : "", | ||
| ), | ||
| }), | ||
| ...mergeProps(clearPressProps, clearFocusProps), | ||
| }; | ||
| }, | ||
| [slots, isClearButtonFocusVisible, clearPressProps, clearFocusProps, classNames?.clearButton], | ||
| ); | ||
|
|
||
| const getHeaderWrapperProps: PropGetter = useCallback( | ||
| (props = {}) => { | ||
| return { | ||
| ...props, | ||
| className: slots.headerWrapper({class: clsx(classNames?.headerWrapper, props?.className)}), | ||
| }; | ||
| }, | ||
| [slots, classNames?.headerWrapper], | ||
| ); | ||
|
|
||
| return { | ||
| Component, | ||
| classNames, | ||
|
|
@@ -555,6 +575,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML | |
| getDescriptionProps, | ||
| getErrorMessageProps, | ||
| getClearButtonProps, | ||
| getHeaderWrapperProps, | ||
| }; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,8 @@ export * from "./info-circle"; | |
| export * from "./warning"; | ||
| export * from "./danger"; | ||
| export * from "./success"; | ||
| export * from "./trash"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @IsDyh01 Delete this icon since we're not using it |
||
|
|
||
| // sets | ||
| export * from "./bulk"; | ||
| export * from "./bold"; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import {IconSvgProps} from "./types"; | ||
|
|
||
| export const TrashIcon = (props: IconSvgProps) => ( | ||
| <svg | ||
| fill="none" | ||
| height="16" | ||
| viewBox="0 0 16 16" | ||
| width="16" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| {...props} | ||
| > | ||
| <path | ||
| d="M14.0071 3.8847L14.0087 3.88486C14.0648 3.8902 14.1093 3.93884 14.108 3.99818C14.0999 4.04945 14.0549 4.08666 14.0067 4.08666H14.0066H14.0064H14.0063H14.0062H14.0061H14.006H14.0059H14.0057H14.0056H14.0055H14.0054H14.0053H14.0052H14.005H14.0049H14.0048H14.0047H14.0046H14.0045H14.0044H14.0042H14.0041H14.004H14.0039H14.0038H14.0037H14.0036H14.0034H14.0033H14.0032H14.0031H14.003H14.0029H14.0028H14.0027H14.0025H14.0024H14.0023H14.0022H14.0021H14.002H14.0019H14.0018H14.0017H14.0015H14.0014H14.0013H14.0012H14.0011H14.001H14.0009H14.0008H14.0007H14.0005H14.0004H14.0003H14.0002H14.0001H14H13.9999H13.9998H13.9997H13.9996H13.9995H13.9993H13.9992H13.9991H13.999H13.9989H13.9988H13.9987H13.9986H13.9985H13.9984H13.9983H13.9982H13.9981H13.9979H13.9978H13.9977H13.9976H13.9975H13.9974H13.9973H13.9972H13.9971H13.997H13.9969H13.9968H13.9967H13.9966H13.9965H13.9964H13.9962H13.9961H13.996H13.9959H13.9958H13.9957H13.9956H13.9955H13.9954H13.9953H13.9952H13.9951H13.995H13.9949H13.9948H13.9947H13.9946H13.9945H13.9944H13.9943H13.9942H13.9941H13.9939H13.9938H13.9937H13.9936H13.9935H13.9934H13.9933H13.9932H13.9931H13.993H13.9929H13.9928H13.9927H13.9926H13.9925H13.9924H13.9923H13.9922H13.9921H13.992H13.9919H13.9918H13.9917H13.9916H13.9915H13.9914H13.9913H13.9912H13.9911H13.991H13.9909H13.9908H13.9907H13.9906H13.9905H13.9904H13.9903H13.9902H13.9901H13.99H13.9899H13.9898H13.9897H13.9896H13.9895H13.9894H13.9893H13.9892H13.9891H13.989H13.9889H13.9888H13.9887H13.9886H13.9885H13.9884H13.9883H13.9882H13.9881H13.988H13.9879H13.9878H13.9877H13.9876H13.9875H13.9874H13.9873H13.9872H13.9871H13.987H13.9869H13.9868H13.9867H13.9866H13.9865H13.9864H13.9863H13.9862H13.9861H13.986H13.9859H13.9858H13.9857H13.9856H13.9855H13.9854H13.9853H13.9852H13.9851H13.985H13.9849H13.9848H13.9847H13.9846H13.9845H13.9844H13.9843H13.9842H13.9841H13.984H13.9839H13.9838H13.9837H13.9836H13.9835H13.9834H13.9833H13.9832H13.9831H13.983H13.9829H13.9828H13.9827H13.9826H13.9825H13.9824H13.9823H13.9823H13.9822H13.9821H13.982H13.9819H13.9818H13.9817H13.9816H13.9815H13.9814H13.9813H13.9812H13.9811H13.981H13.9809H13.9808H13.9807H13.9806H13.9805H13.9804H13.9803H13.9802H13.9801H13.98H13.9799H13.9798H13.9797H13.9796H13.9795H13.9794H13.9793H13.9792H13.9791H13.979H13.9789H13.9788H13.9787H13.9786H13.9785H13.9784H13.9783H13.9782H13.9781H13.9781H13.978H13.9779H13.9778H13.9777H13.9776H13.9775H13.9774H13.9773H13.9772H13.9771H13.977H13.9769H13.9768H13.9767H13.9766H13.9765H13.9764H13.9763H13.9762H13.9761H13.976H13.9759H13.9758H13.9757H13.9756H13.9755H13.9754H13.9753H13.9752H13.9751H13.975H13.9749H13.9748H13.9747H13.9746H13.9745H13.9744H13.9743H13.9742H13.9741H13.974H13.9739H13.9738H13.9737H13.9736H13.9735H13.9734H13.9733H13.9733C10.4314 3.7333 6.88973 3.59909 3.36755 3.94858C3.36742 3.94859 3.36729 3.9486 3.36717 3.94862L2.00875 4.0818C2.00861 4.08181 2.00848 4.08182 2.00835 4.08183C1.94717 4.08747 1.90326 4.04725 1.89811 3.99449C1.8929 3.9411 1.93182 3.89052 1.99126 3.88486L1.99237 3.88475L3.35187 3.75147C3.35194 3.75146 3.35201 3.75145 3.35208 3.75145C3.96457 3.69218 4.57811 3.65256 5.19909 3.6125L5.51656 3.59202L5.56806 3.27809L5.708 2.42509C5.76399 2.08568 5.81767 1.81209 5.98475 1.60624C6.12965 1.42771 6.42122 1.23333 7.12 1.23333H8.86667C9.56355 1.23333 9.85752 1.43445 10.0046 1.61915C10.1739 1.83176 10.2261 2.10927 10.2786 2.43103L10.2785 2.43103L10.2789 2.4334L10.42 3.26694V3.61839L10.7978 3.63938C11.8728 3.6991 12.9403 3.77868 14.0071 3.8847Z" | ||
| fill="#A1A1AA" | ||
| stroke="#A1A1AA" | ||
| strokeWidth="0.8" | ||
| /> | ||
| <path | ||
| d="M12.2134 5.56667C12.3292 5.56667 12.4438 5.61405 12.5285 5.70064C12.6088 5.78957 12.6542 5.90963 12.6474 6.03693C12.6474 6.03722 12.6473 6.03752 12.6473 6.03782L12.2342 12.8669C12.1959 13.3993 12.1525 13.8463 11.9087 14.1798C11.69 14.4792 11.2352 14.7733 10.14 14.7733H5.86004C4.76563 14.7733 4.31057 14.4779 4.09155 14.1776C3.8475 13.843 3.80414 13.3957 3.76583 12.8668L3.35272 6.03052C3.35271 6.03034 3.3527 6.03016 3.35269 6.02998C3.34597 5.90978 3.39108 5.78807 3.47441 5.69749C3.54714 5.61844 3.66453 5.56667 3.7867 5.56667H12.2134ZM6.8867 12.2333H9.1067C9.60095 12.2333 10.0067 11.8276 10.0067 11.3333C10.0067 10.8391 9.60095 10.4333 9.1067 10.4333H6.8867C6.39246 10.4333 5.9867 10.8391 5.9867 11.3333C5.9867 11.8276 6.39246 12.2333 6.8867 12.2333ZM6.33337 9.56667H9.6667C10.161 9.56667 10.5667 9.16092 10.5667 8.66667C10.5667 8.17242 10.161 7.76667 9.6667 7.76667H6.33337C5.83912 7.76667 5.43337 8.17242 5.43337 8.66667C5.43337 9.16092 5.83912 9.56667 6.33337 9.56667Z" | ||
| fill="#A1A1AA" | ||
| stroke="#A1A1AA" | ||
| strokeWidth="0.8" | ||
| /> | ||
| </svg> | ||
| ); |
Uh oh!
There was an error while loading. Please reload this page.