-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdate-picker.stories.tsx
More file actions
157 lines (137 loc) · 5.13 KB
/
date-picker.stories.tsx
File metadata and controls
157 lines (137 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { zodResolver } from '@hookform/resolvers/zod';
import { DatePicker } from '@lambdacurry/forms/remix-hook-form/date-picker';
import { Button } from '@lambdacurry/forms/ui/button';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { expect, userEvent, within } from '@storybook/test';
import { type ActionFunctionArgs, Form, useFetcher } from 'react-router';
import { createFormData, getValidatedFormData, RemixFormProvider, useRemixForm } from 'remix-hook-form';
import { z } from 'zod';
import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub';
// Regex for finding day buttons in calendar
const DAY_BUTTON_REGEX = /^\d+$/;
const formSchema = z.object({
date: z.coerce.date({
required_error: 'Please select a date',
}),
});
type FormData = z.infer<typeof formSchema>;
const ControlledDatePickerExample = () => {
const fetcher = useFetcher<{ message: string; date: string }>();
const methods = useRemixForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
date: undefined,
},
fetcher,
submitConfig: {
action: '/',
method: 'post',
},
submitHandlers: {
onValid: (data) => {
fetcher.submit(
createFormData({
date: data.date.toISOString(),
}),
{
method: 'post',
action: '/',
},
);
},
},
});
return (
<RemixFormProvider {...methods}>
<Form onSubmit={methods.handleSubmit}>
<div className="space-y-4">
<DatePicker name="date" label="Select a date" />
<Button type="submit" className="mt-4">
Submit
</Button>
{fetcher.data?.date && (
<div className="mt-4">
<p className="text-sm font-medium">Submitted with date:</p>
<p className="text-sm text-gray-500">{new Date(fetcher.data.date).toLocaleDateString()}</p>
</div>
)}
</div>
</Form>
</RemixFormProvider>
);
};
const handleFormSubmission = async (request: Request) => {
const { data, errors } = await getValidatedFormData<FormData>(request, zodResolver(formSchema));
if (errors) {
return { errors };
}
return { message: 'Date selected successfully', date: data.date.toISOString() };
};
const meta: Meta<typeof DatePicker> = {
title: 'RemixHookForm/Date Picker',
component: DatePicker,
parameters: { layout: 'centered' },
tags: ['autodocs'],
decorators: [
withReactRouterStubDecorator({
routes: [
{
path: '/',
Component: ControlledDatePickerExample,
action: async ({ request }: ActionFunctionArgs) => handleFormSubmission(request),
},
],
}),
],
} satisfies Meta<typeof DatePicker>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
parameters: {
docs: {
description: {
story: 'A date picker component for selecting a date.',
},
},
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('Verify initial state', async () => {
// Verify the date picker trigger is present and has correct label
const datePickerTrigger = canvas.getByRole('button', { name: /select a date/i });
expect(datePickerTrigger).toBeInTheDocument();
// Verify submit button is present but form hasn't been submitted yet
const submitButton = canvas.getByRole('button', { name: /submit/i });
expect(submitButton).toBeInTheDocument();
expect(canvas.queryByText('Submitted with date:')).not.toBeInTheDocument();
});
await step('Open calendar and select date', async () => {
// Find and click the date picker trigger button
const datePickerTrigger = canvas.getByRole('button', { name: /select a date/i });
await userEvent.click(datePickerTrigger);
// Wait for calendar dialog to appear (it's rendered in a portal)
const dialog = await within(document.body).findByRole('dialog');
const dialogCanvas = within(dialog);
// Find any available day button in the calendar (look for a button with a number)
// We'll look for any day button that's not disabled
const dayButtons = await dialogCanvas.findAllByRole('button');
const availableDayButton = dayButtons.find((button) => {
const text = button.textContent;
return text && DAY_BUTTON_REGEX.test(text.trim()) && !button.hasAttribute('disabled');
});
if (!availableDayButton) {
throw new Error('No available day button found in calendar');
}
await userEvent.click(availableDayButton);
});
await step('Submit form and verify success', async () => {
// Submit the form
const submitButton = canvas.getByRole('button', { name: /submit/i });
await userEvent.click(submitButton);
// Verify submission with comprehensive assertions
await expect(canvas.findByText('Submitted with date:')).resolves.toBeInTheDocument();
// Check for any date format (MM/DD/YYYY or DD/MM/YYYY)
await expect(canvas.findByText(/\d{1,2}\/\d{1,2}\/\d{4}/)).resolves.toBeInTheDocument();
});
},
};