241 lines
7.1 KiB
TypeScript
241 lines
7.1 KiB
TypeScript
import { useState } from 'react';
|
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
import { CalendarDaysIcon } from '@heroicons/react/24/solid';
|
|
import { DatePicker } from './DatePicker';
|
|
|
|
const meta = {
|
|
title: 'Components/DatePicker',
|
|
component: DatePicker,
|
|
tags: ['autodocs'],
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
component:
|
|
'Date selection field with InputField-compatible API, supporting date/time/datetime-local values, size/layout variants, and validation state.',
|
|
},
|
|
},
|
|
},
|
|
argTypes: {
|
|
label: {
|
|
description: 'Label text shown above (stacked) or on the left (inline).',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
placeholder: {
|
|
description: 'Input placeholder text.',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
type: {
|
|
description: 'Native date input type.',
|
|
options: ['date', 'datetime-local', 'time'],
|
|
control: 'inline-radio',
|
|
table: { type: { summary: "'date' | 'datetime-local' | 'time'" } },
|
|
},
|
|
size: {
|
|
description: 'Input size.',
|
|
options: ['sm', 'md', 'lg', 'full'],
|
|
control: 'inline-radio',
|
|
table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } },
|
|
},
|
|
width: {
|
|
description: 'Input width constraint.',
|
|
options: ['sm', 'md', 'lg', 'full'],
|
|
control: 'inline-radio',
|
|
table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } },
|
|
},
|
|
layout: {
|
|
description: 'Label/input layout mode.',
|
|
options: ['stacked', 'inline'],
|
|
control: 'inline-radio',
|
|
table: { type: { summary: "'stacked' | 'inline'" } },
|
|
},
|
|
value: {
|
|
description: 'Controlled input value.',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
name: {
|
|
description: 'Native input `name` attribute.',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
disabled: {
|
|
description: 'Disables the input.',
|
|
control: 'boolean',
|
|
table: { type: { summary: 'boolean' } },
|
|
},
|
|
required: {
|
|
description: 'Sets the native HTML `required` attribute.',
|
|
control: 'boolean',
|
|
table: { type: { summary: 'boolean' } },
|
|
},
|
|
error: {
|
|
description: 'Validation message shown below the field.',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
rightIcon: {
|
|
description: 'Optional trailing icon node.',
|
|
control: false,
|
|
table: { type: { summary: 'ReactNode' } },
|
|
},
|
|
className: {
|
|
description: 'Extra CSS classes for the outer wrapper.',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
inputClassName: {
|
|
description: 'Extra CSS classes for the `<input>` element.',
|
|
control: 'text',
|
|
table: { type: { summary: 'string' } },
|
|
},
|
|
onChange: {
|
|
description: 'Change handler callback.',
|
|
action: 'changed',
|
|
table: { type: { summary: 'ChangeEventHandler<HTMLInputElement>' } },
|
|
},
|
|
onBlur: {
|
|
description: 'Blur handler callback.',
|
|
control: false,
|
|
table: { type: { summary: 'FocusEventHandler<HTMLInputElement>' } },
|
|
},
|
|
inputRef: {
|
|
description: 'Ref forwarded to the native `<input>` element.',
|
|
control: false,
|
|
table: { type: { summary: 'Ref<HTMLInputElement>' } },
|
|
},
|
|
},
|
|
args: {
|
|
label: 'Schedule at',
|
|
type: 'datetime-local',
|
|
value: '',
|
|
size: 'md',
|
|
width: 'md',
|
|
layout: 'stacked',
|
|
},
|
|
} satisfies Meta<typeof DatePicker>;
|
|
|
|
export default meta;
|
|
type Story = StoryObj<typeof meta>;
|
|
|
|
export const DateOnly: Story = {
|
|
args: {
|
|
type: 'date',
|
|
label: 'Publish date',
|
|
},
|
|
render: (args) => {
|
|
const [value, setValue] = useState('2031-05-20');
|
|
return (
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
onChange={(event) => {
|
|
setValue(event.target.value);
|
|
args.onChange?.(event);
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
};
|
|
|
|
export const DateTime: Story = {
|
|
args: {
|
|
type: 'datetime-local',
|
|
label: 'Schedule at',
|
|
},
|
|
render: (args) => {
|
|
const [value, setValue] = useState('2031-05-20T14:30');
|
|
return (
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
onChange={(event) => {
|
|
setValue(event.target.value);
|
|
args.onChange?.(event);
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
};
|
|
|
|
export const TimeOnlyInline: Story = {
|
|
args: {
|
|
type: 'time',
|
|
label: 'Start time',
|
|
layout: 'inline',
|
|
size: 'sm',
|
|
rightIcon: <CalendarDaysIcon className="h-4 w-4 ui-body-secondary" />,
|
|
},
|
|
render: (args) => {
|
|
const [value, setValue] = useState('09:00');
|
|
return (
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
onChange={(event) => {
|
|
setValue(event.target.value);
|
|
args.onChange?.(event);
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
};
|
|
|
|
export const Error: Story = {
|
|
args: {
|
|
type: 'datetime-local',
|
|
label: 'Schedule at',
|
|
value: '',
|
|
error: 'Pick a valid future date and time',
|
|
},
|
|
};
|
|
|
|
export const Disabled: Story = {
|
|
args: {
|
|
type: 'datetime-local',
|
|
label: 'Published at',
|
|
value: '2031-05-20T14:30',
|
|
disabled: true,
|
|
},
|
|
};
|
|
|
|
export const SizeMatrix: Story = {
|
|
args: {
|
|
type: 'datetime-local',
|
|
label: 'Schedule at',
|
|
},
|
|
render: (args) => {
|
|
const [value, setValue] = useState('2031-05-20T14:30');
|
|
return (
|
|
<div className="grid grid-cols-1 gap-3">
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
size="sm"
|
|
onChange={(event) => setValue(event.target.value)}
|
|
/>
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
size="md"
|
|
onChange={(event) => setValue(event.target.value)}
|
|
/>
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
size="lg"
|
|
onChange={(event) => setValue(event.target.value)}
|
|
/>
|
|
<DatePicker
|
|
{...args}
|
|
value={value}
|
|
size="full"
|
|
width="full"
|
|
onChange={(event) => setValue(event.target.value)}
|
|
/>
|
|
</div>
|
|
);
|
|
},
|
|
};
|