feat: add textarea component (#17465)

This adds the shadcn textarea component along with storybook stories

Figma:
https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=1949-13239&t=NSt5S88hAsE4Q9di-1

<img width="612" alt="Screenshot 2025-04-18 at 12 16 57"
src="https://github.com/user-attachments/assets/dcd0281f-5d80-4047-9ba4-e456290ceb61"
/>
This commit is contained in:
Jaayden Halko
2025-04-18 10:43:07 -04:00
committed by GitHub
parent 6e0e29af13
commit 41b2165b47
2 changed files with 125 additions and 0 deletions
@@ -0,0 +1,99 @@
import type { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";
import { useState } from "react";
import { Textarea } from "./Textarea";
const meta: Meta<typeof Textarea> = {
title: "components/Textarea",
component: Textarea,
args: {},
argTypes: {
value: {
control: "text",
description: "The controlled value of the textarea",
},
defaultValue: {
control: "text",
description: "The default value when initially rendered",
},
disabled: {
control: "boolean",
description:
"When true, prevents the user from interacting with the textarea",
},
placeholder: {
control: "text",
description: "Placeholder text displayed when the textarea is empty",
},
rows: {
control: "number",
description: "The number of rows to display",
},
},
};
export default meta;
type Story = StoryObj<typeof Textarea>;
export const WithPlaceholder: Story = {
args: {
placeholder: "Enter your message here...",
},
};
export const Disabled: Story = {
args: {
disabled: true,
placeholder: "Placeholder",
},
};
export const WithDefaultValue: Story = {
args: {
defaultValue: "This is some default text in the textarea.",
},
};
export const Large: Story = {
args: {
rows: 8,
placeholder: "Placeholder: A larger textarea with more rows",
},
};
const ControlledTextarea = () => {
const [value, setValue] = useState("This is a controlled textarea.");
return (
<div className="space-y-2">
<Textarea
value={value}
placeholder="Type something..."
onChange={(e) => setValue(e.target.value)}
/>
<div className="text-sm text-content-secondary">
Character count: {value.length}
</div>
</div>
);
};
export const Controlled: Story = {
render: () => <ControlledTextarea />,
};
export const TypeText: Story = {
args: {
placeholder: "Type something here...",
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const textarea = canvas.getByRole("textbox");
await userEvent.type(
textarea,
"Hello, this is some example text being typed into the textarea!",
);
expect(textarea).toHaveValue(
"Hello, this is some example text being typed into the textarea!",
);
},
};
+26
View File
@@ -0,0 +1,26 @@
/**
* Copied from shadc/ui on 04/18/2025
* @see {@link https://ui.shadcn.com/docs/components/textarea}
*/
import * as React from "react";
import { cn } from "utils/cn";
export const Textarea = React.forwardRef<
HTMLTextAreaElement,
React.ComponentProps<"textarea">
>(({ className, ...props }, ref) => {
return (
<textarea
className={cn(
`flex min-h-[60px] w-full px-3 py-2 text-sm shadow-sm text-content-primary
rounded-md border border-border bg-transparent placeholder:text-content-secondary
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-content-link
disabled:cursor-not-allowed disabled:opacity-50 disabled:text-content-disabled md:text-sm`,
className,
)}
ref={ref}
{...props}
/>
);
});