feat(SW-3685): Add new TextArea and FormTextArea components * Add new TextArea and FormTextArea components * Update example form with description * Merge branch 'master' into feat/3685-new-textarea-component * Formatting new files with new prettier config * Added custom controls for the text area story Approved-by: Linus Flood
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import { describe, expect, it, vi, afterEach } from "vitest"
|
|
import { render, screen, fireEvent, cleanup } from "@testing-library/react"
|
|
import userEvent from "@testing-library/user-event"
|
|
import { TextArea } from "./TextArea"
|
|
import { TextField } from "react-aria-components"
|
|
|
|
afterEach(() => {
|
|
cleanup()
|
|
})
|
|
|
|
// Wrap TextArea in TextField for proper React Aria context
|
|
const renderTextArea = (props: React.ComponentProps<typeof TextArea>) => {
|
|
return render(
|
|
<TextField>
|
|
<TextArea {...props} />
|
|
</TextField>
|
|
)
|
|
}
|
|
|
|
// Render TextArea standalone for testing its own behavior
|
|
const renderTextAreaStandalone = (
|
|
props: React.ComponentProps<typeof TextArea>
|
|
) => {
|
|
return render(<TextArea {...props} />)
|
|
}
|
|
|
|
describe("TextArea", () => {
|
|
describe("props", () => {
|
|
it("applies required attribute", () => {
|
|
renderTextArea({ label: "Description", required: true })
|
|
expect(screen.getByRole("textbox")).toHaveProperty("required", true)
|
|
})
|
|
|
|
it("applies placeholder when provided", () => {
|
|
renderTextArea({ label: "Description", placeholder: "Enter description" })
|
|
expect(screen.getByRole("textbox").getAttribute("placeholder")).toBe(
|
|
"Enter description"
|
|
)
|
|
})
|
|
|
|
it("applies empty placeholder by default", () => {
|
|
renderTextArea({ label: "Description" })
|
|
expect(screen.getByRole("textbox").getAttribute("placeholder")).toBe("")
|
|
})
|
|
|
|
it("applies custom id", () => {
|
|
renderTextAreaStandalone({ label: "Description", id: "custom-id" })
|
|
expect(screen.getByRole("textbox").getAttribute("id")).toBe("custom-id")
|
|
})
|
|
})
|
|
|
|
describe("label", () => {
|
|
it("renders label when provided", () => {
|
|
renderTextArea({ label: "Description" })
|
|
expect(screen.getByText("Description")).toBeTruthy()
|
|
})
|
|
|
|
it("does not render label when not provided", () => {
|
|
renderTextArea({})
|
|
expect(screen.queryByText("Description")).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe("controlled textarea", () => {
|
|
it("displays the controlled value", () => {
|
|
renderTextArea({
|
|
label: "Description",
|
|
value: "Some text content",
|
|
onChange: vi.fn(),
|
|
})
|
|
expect(screen.getByRole("textbox")).toHaveProperty(
|
|
"value",
|
|
"Some text content"
|
|
)
|
|
})
|
|
|
|
it("calls onChange when typing", async () => {
|
|
const onChange = vi.fn()
|
|
renderTextArea({ label: "Description", value: "", onChange })
|
|
|
|
const textarea = screen.getByRole("textbox")
|
|
await userEvent.type(textarea, "a")
|
|
|
|
expect(onChange).toHaveBeenCalled()
|
|
})
|
|
|
|
it("does not change value without onChange updating it", () => {
|
|
const onChange = vi.fn()
|
|
renderTextArea({ label: "Description", value: "initial", onChange })
|
|
|
|
const textarea = screen.getByRole("textbox")
|
|
fireEvent.change(textarea, { target: { value: "changed" } })
|
|
|
|
expect(textarea).toHaveProperty("value", "initial")
|
|
})
|
|
})
|
|
|
|
describe("ref forwarding", () => {
|
|
it("forwards ref to the textarea element", () => {
|
|
const ref = { current: null as HTMLTextAreaElement | null }
|
|
render(
|
|
<TextField>
|
|
<TextArea label="Description" ref={ref} />
|
|
</TextField>
|
|
)
|
|
expect(ref.current).toBeInstanceOf(HTMLTextAreaElement)
|
|
})
|
|
|
|
it("allows focusing via ref", () => {
|
|
const ref = { current: null as HTMLTextAreaElement | null }
|
|
render(
|
|
<TextField>
|
|
<TextArea label="Description" ref={ref} />
|
|
</TextField>
|
|
)
|
|
ref.current?.focus()
|
|
expect(document.activeElement).toBe(ref.current)
|
|
})
|
|
})
|
|
})
|