Merged in feat/use-new-input-component (pull request #3324)

feat(SW-3659): Use new input component

* Use new input component

* Update error formatter

* Merged master into feat/use-new-input-component

* Merged master into feat/use-new-input-component

* Merge branch 'master' into feat/use-new-input-component

* Merged master into feat/use-new-input-component

* Update Input stories

* Merge branch 'feat/use-new-input-component' of bitbucket.org:scandic-swap/web into feat/use-new-input-component

* Update Storybook logo

* Add some new demo icon input story

* Fix the clear content button position

* Fix broken password input icon

* Merged master into feat/use-new-input-component

* Merged master into feat/use-new-input-component

* Add aria-hidden to required asterisk

* Merge branch 'feat/use-new-input-component' of bitbucket.org:scandic-swap/web into feat/use-new-input-component

* Merge branch 'master' into feat/use-new-input-component


Approved-by: Bianca Widstam
Approved-by: Matilda Landström
This commit is contained in:
Rasmus Langvad
2025-12-18 15:42:09 +00:00
parent 40e1efa81f
commit b9a62b5280
34 changed files with 520 additions and 1113 deletions

View File

@@ -4,16 +4,133 @@ import { expect } from 'storybook/test'
import { Input } from './Input'
import { TextField } from 'react-aria-components'
import { MaterialIcon } from '../Icons/MaterialIcon'
import type { SymbolCodepoints } from '../Icons/MaterialIcon/MaterialSymbol/types'
const meta: Meta<typeof Input> = {
title: 'Core Components/Input',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
component: ({ isInvalid, ...props }) => (
<TextField isInvalid={isInvalid}>
<Input {...props} />
component: ({ isInvalid, validationState, ...props }) => (
<TextField isInvalid={isInvalid} data-validation-state={validationState}>
<Input {...props} data-validation-state={validationState} />
</TextField>
),
argTypes: {},
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' },
},
},
showLeftIcon: {
control: 'boolean',
description: 'Whether to show a left icon',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
showRightIcon: {
control: 'boolean',
description: 'Whether to show a right icon',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
leftIconName: {
control: 'select',
options: [
'calendar_month',
'credit_card',
'email',
'info_circle',
'location_on',
'lock',
'phone',
'search',
'sell',
'visibility',
'visibility_off',
],
description: 'Icon name for the left icon',
table: {
type: { summary: 'string' },
defaultValue: { summary: "'person'" },
},
},
rightIconName: {
control: 'select',
options: [
'calendar_month',
'credit_card',
'email',
'info_circle',
'location_on',
'lock',
'phone',
'search',
'sell',
'visibility',
'visibility_off',
],
description: 'Icon name for the right icon',
table: {
type: { summary: 'string' },
defaultValue: { summary: "'lock'" },
},
},
showWarning: {
control: 'boolean',
description: 'Whether to show warning validation state',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
} as any,
}
export default meta
@@ -25,8 +142,50 @@ export const Default: Story = {
label: 'Label',
name: 'foo',
required: false,
},
showLeftIcon: false,
showRightIcon: false,
leftIconName: 'person',
rightIconName: 'lock',
showWarning: false,
} as any,
render: (args) => {
// Extract custom Storybook args
const {
showLeftIcon,
showRightIcon,
leftIconName,
rightIconName,
showWarning,
...inputProps
} = args as typeof args & {
showLeftIcon?: boolean
showRightIcon?: boolean
leftIconName?: string
rightIconName?: string
showWarning?: boolean
}
const validationState = showWarning ? 'warning' : undefined
return (
<TextField data-validation-state={validationState}>
<Input
{...inputProps}
data-validation-state={validationState}
leftIcon={
showLeftIcon && leftIconName ? (
<MaterialIcon icon={leftIconName as SymbolCodepoints} />
) : undefined
}
rightIcon={
showRightIcon && rightIconName ? (
<MaterialIcon icon={rightIconName as SymbolCodepoints} />
) : undefined
}
/>
</TextField>
)
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).not.toBeDisabled()
@@ -40,68 +199,3 @@ export const Default: Story = {
expect(textbox).toHaveValue('')
},
}
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 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')
},
}