Files
web/packages/design-system/lib/components/InputNew/Input.stories.tsx
Rasmus Langvad ca6cc5ab6c Merged in feat/SW-3636-storybook-structure (pull request #3309)
feat(SW-3636): Storybook structure

* New sections in Storybook sidebar

* Group Storybook content files and add token files for spacing, border radius and shadows


Approved-by: Joakim Jäderberg
2025-12-08 12:35:14 +00:00

417 lines
10 KiB
TypeScript

import type { Meta, StoryObj } from '@storybook/nextjs-vite'
import { expect } from 'storybook/test'
import { Input } from './Input'
import { TextField } from 'react-aria-components'
import { MaterialIcon } from '../Icons/MaterialIcon'
const meta: Meta<typeof Input> = {
title: 'Core Components/Input (New)',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
component: ({ isInvalid, validationState, ...props }) => (
<TextField isInvalid={isInvalid} data-validation-state={validationState}>
<Input {...props} data-validation-state={validationState} />
</TextField>
),
argTypes: {
label: {
control: 'text',
description: 'The label text displayed for the input field',
table: {
type: { summary: 'string' },
},
},
labelPosition: {
control: 'select',
options: ['floating', 'top'],
description: 'Position of the label relative to the input',
table: {
type: { summary: "'floating' | 'top'" },
defaultValue: { summary: "'floating'" },
},
},
placeholder: {
control: 'text',
description: 'Placeholder text shown when input is empty',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
},
},
required: {
control: 'boolean',
description: 'Whether the input is required',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
disabled: {
control: 'boolean',
description: 'Whether the input is disabled',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
showClearContentIcon: {
control: 'boolean',
description: 'Whether the clear content icon is shown',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
},
}
export default meta
type Story = StoryObj<typeof Input>
export const Default: Story = {
args: {
label: 'Label',
name: 'foo',
required: false,
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).not.toBeDisabled()
expect(textbox).toHaveValue('')
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('Hello World')
await userEvent.clear(textbox)
expect(textbox).toHaveValue('')
},
}
export const WithIconsFloatingLabel: Story = {
args: {
label: 'Label',
name: 'foo',
value: 'Value',
leftIcon: <MaterialIcon icon="sell" />,
rightIcon: <MaterialIcon icon="lock" />,
showClearContentIcon: true,
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).not.toBeDisabled()
},
}
export const WithIconsTopLabel: Story = {
args: {
label: 'Label',
name: 'foo',
value: 'Value',
labelPosition: 'top',
leftIcon: <MaterialIcon icon="email" />,
showClearContentIcon: true,
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).not.toBeDisabled()
},
}
export const WithIconsAndClearIconTopLabel: Story = {
args: {
label: 'Label',
name: 'foo',
value: 'Value',
labelPosition: 'top',
leftIcon: <MaterialIcon icon="person" />,
rightIcon: <MaterialIcon icon="email" />,
showClearContentIcon: true,
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).not.toBeDisabled()
},
}
export const Filled: Story = {
args: {
label: 'Label',
name: 'foo',
value: 'Value',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).not.toBeDisabled()
},
}
export const Error: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
isInvalid: true,
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveAttribute('aria-invalid', 'true')
expect(textbox).not.toBeDisabled()
},
}
export const ErrorFilled: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
isInvalid: true,
value: 'Value',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).toHaveAttribute('aria-invalid', 'true')
expect(textbox).not.toBeDisabled()
},
}
export const Disabled: Story = {
args: {
label: 'Label',
name: 'foo',
disabled: true,
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('')
expect(textbox).toBeDisabled()
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('')
},
}
export const DisabledFilled: Story = {
args: {
label: 'Label',
name: 'foo',
disabled: true,
value: 'Value',
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).toBeDisabled()
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('Value')
},
}
export const WarningDefault: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
validationState: 'warning',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
// data-validation-state is on the parent label element, not the input
const container = textbox.closest('[data-validation-state]')
expect(container?.getAttribute('data-validation-state')).toBe('warning')
expect(textbox).not.toBeDisabled()
},
}
export const WarningFilled: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
validationState: 'warning',
value: 'Value',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
// data-validation-state is on the parent label element, not the input
const container = textbox.closest('[data-validation-state]')
expect(container?.getAttribute('data-validation-state')).toBe('warning')
expect(textbox).not.toBeDisabled()
},
}
export const DefaultTop: Story = {
args: {
label: 'Label',
placeholder: 'Label',
name: 'foo',
required: false,
labelPosition: 'top',
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).not.toBeDisabled()
expect(textbox).toHaveValue('')
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('Hello World')
await userEvent.clear(textbox)
expect(textbox).toHaveValue('')
},
}
export const FilledTop: Story = {
args: {
label: 'Label',
name: 'foo',
value: 'Value',
labelPosition: 'top',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).not.toBeDisabled()
},
}
export const ErrorTop: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
isInvalid: true,
placeholder: 'Label',
labelPosition: 'top',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveAttribute('aria-invalid', 'true')
expect(textbox).not.toBeDisabled()
},
}
export const ErrorFilledTop: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
isInvalid: true,
value: 'Value',
labelPosition: 'top',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).toHaveAttribute('aria-invalid', 'true')
expect(textbox).not.toBeDisabled()
},
}
export const DisabledTop: Story = {
args: {
label: 'Label',
name: 'foo',
disabled: true,
labelPosition: 'top',
placeholder: 'Label',
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('')
expect(textbox).toBeDisabled()
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('')
},
}
export const DisabledFilledTop: Story = {
args: {
label: 'Label',
name: 'foo',
disabled: true,
value: 'Value',
labelPosition: 'top',
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).toBeDisabled()
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('Value')
},
}
export const WarningDefaultTop: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
validationState: 'warning',
labelPosition: 'top',
placeholder: 'Label',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
// data-validation-state is on the parent label element, not the input
const container = textbox.closest('[data-validation-state]')
expect(container?.getAttribute('data-validation-state')).toBe('warning')
expect(textbox).not.toBeDisabled()
},
}
export const WarningFilledTop: Story = {
args: {
label: 'Label',
name: 'foo',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
validationState: 'warning',
value: 'Value',
labelPosition: 'top',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
// data-validation-state is on the parent label element, not the input
const container = textbox.closest('[data-validation-state]')
expect(container?.getAttribute('data-validation-state')).toBe('warning')
expect(textbox).not.toBeDisabled()
},
}