Skip to content

Commit 3b10bda

Browse files
committed
chore: update Storybook dependencies and enhance testing structure
- Bumped Storybook dependencies to version 9.0.6 for addons and core packages. - Introduced @storybook/test for improved interaction testing in stories. - Removed deprecated 'Writing Tests' documentation file to streamline content. - Enhanced calendar stories with step-based testing for better clarity and maintainability. - Updated DatePicker story to utilize new testing patterns and ensure accurate user interactions.
1 parent ad96ad6 commit 3b10bda

5 files changed

Lines changed: 378 additions & 286 deletions

File tree

apps/docs/package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@
1414
"@hookform/error-message": "^2.0.0",
1515
"@hookform/resolvers": "^3.9.1",
1616
"@lambdacurry/forms": "*",
17-
"@storybook/addon-links": "^9.0.1",
18-
"@storybook/react-vite": "^9.0.1",
17+
"@storybook/addon-links": "^9.0.6",
18+
"@storybook/react-vite": "^9.0.6",
1919
"react": "^19.0.0",
2020
"react-hook-form": "^7.51.0",
2121
"react-router": "^7.6.1",
2222
"remix-hook-form": "^7.0.1",
23-
"storybook": "^9.0.1"
23+
"storybook": "^9.0.6"
2424
},
2525
"devDependencies": {
2626
"@react-router/dev": "^7.0.0",
27-
"@storybook/addon-docs": "^9.0.1",
28-
"@storybook/test-runner": "^0.22.0",
27+
"@storybook/addon-docs": "^9.0.6",
28+
"@storybook/test": "^8.6.14",
29+
"@storybook/test-runner": "^0.22.1",
2930
"@storybook/testing-library": "^0.2.2",
3031
"@tailwindcss/postcss": "^4.1.8",
3132
"@tailwindcss/vite": "^4.0.0",

apps/docs/src/0.2 Writing Tests.mdx

Lines changed: 0 additions & 85 deletions
This file was deleted.

apps/docs/src/remix-hook-form/calendar-with-month-year-select.stories.tsx

Lines changed: 119 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { Calendar } from '@lambdacurry/forms/ui/calendar';
44
import { Label } from '@lambdacurry/forms/ui/label';
55
import { Select } from '@lambdacurry/forms/ui/select';
66
import type { Meta, StoryObj } from '@storybook/react-vite';
7+
import { expect, userEvent, within } from '@storybook/test';
78
import * as React from 'react';
89
import { type ActionFunctionArgs, Form, useFetcher } from 'react-router';
910
import { RemixFormProvider, createFormData, getValidatedFormData, useRemixForm } from 'remix-hook-form';
10-
import { expect, userEvent, within } from 'storybook/test';
1111
import { z } from 'zod';
1212
import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub';
1313

@@ -178,9 +178,6 @@ const meta: Meta<typeof CalendarWithMonthYearSelect> = {
178178
export default meta;
179179
type Story = StoryObj<typeof meta>;
180180

181-
// Helper function for sleep delays
182-
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
183-
184181
export const Default: Story = {
185182
parameters: {
186183
docs: {
@@ -189,51 +186,53 @@ export const Default: Story = {
189186
},
190187
},
191188
},
192-
play: async ({ canvasElement }) => {
189+
play: async ({ canvasElement, step }) => {
193190
const canvas = within(canvasElement);
194191

195-
// Wait for initial render
196-
await sleep(500);
197-
198-
// Find the dropdown select button
199-
const dropdownSelectButton = await canvas.findByRole('combobox', { expanded: false });
200-
expect(dropdownSelectButton).toBeInTheDocument();
201-
202-
// Click to open the dropdown
203-
await userEvent.click(dropdownSelectButton);
204-
await sleep(500);
205-
206-
// Find and click "Month Only" option - look in the popover content
207-
const monthOnlyOption =
208-
document.querySelector('[data-value="dropdown-months"]') || (await canvas.findByText('Month Only'));
209-
await userEvent.click(monthOnlyOption);
210-
await sleep(300);
211-
212-
// Verify the dropdown closed and selection changed
213-
expect(dropdownSelectButton).toHaveTextContent('Month Only');
214-
215-
// Find a day in the calendar to click
216-
const dayButtons = await canvas.findAllByRole('gridcell');
217-
const availableDay = dayButtons.find(
218-
(day) => day.textContent && /^\d+$/.test(day.textContent.trim()) && !day.hasAttribute('aria-disabled'),
219-
) as HTMLElement;
220-
221-
console.log('>>>> availableDay', availableDay);
222-
223-
await userEvent.click(availableDay);
224-
await sleep(300);
225-
226-
// Find and click the submit button
227-
const submitButton = await canvas.findByRole('button', { name: /submit/i });
228-
await userEvent.click(submitButton);
229-
230-
// Wait for form submission
231-
await sleep(1000);
232-
233-
// Verify submission results
234-
expect(canvas.getByText('Submitted with:')).toBeInTheDocument();
235-
expect(canvas.getByText(/Date:/)).toBeInTheDocument();
236-
expect(canvas.getByText('Dropdown Type: dropdown-months')).toBeInTheDocument();
192+
await step('Verify initial state', async () => {
193+
// Verify the dropdown select button is present with default text
194+
const dropdownSelectButton = await canvas.findByText('Month and Year');
195+
expect(dropdownSelectButton).toBeInTheDocument();
196+
197+
// Verify submit button is present but form hasn't been submitted yet
198+
const submitButton = canvas.getByRole('button', { name: /submit/i });
199+
expect(submitButton).toBeInTheDocument();
200+
expect(canvas.queryByText('Submitted with:')).not.toBeInTheDocument();
201+
});
202+
203+
await step('Change dropdown to Month Only', async () => {
204+
// Find and click the dropdown select button (use text content)
205+
const dropdownSelectButton = canvas.getByText('Month and Year');
206+
await userEvent.click(dropdownSelectButton);
207+
208+
// Find and click "Month Only" option
209+
const monthOnlyOption = await within(document.body).findByText('Month Only');
210+
await userEvent.click(monthOnlyOption);
211+
212+
// Verify the dropdown selection changed by checking the new text
213+
expect(canvas.getByText('Month Only')).toBeInTheDocument();
214+
});
215+
216+
await step('Navigate calendar and select a date', async () => {
217+
// Use the calendar's month dropdown to navigate to July (month 7)
218+
const monthDropdown = canvas.getByLabelText('Choose the Month');
219+
await userEvent.selectOptions(monthDropdown, '6'); // June is month 6 (0-indexed)
220+
221+
// Find a specific date button using aria-label (includes day of week)
222+
const dateButton = await canvas.findByRole('button', { name: /15th, 2025/i });
223+
await userEvent.click(dateButton);
224+
});
225+
226+
await step('Submit form and verify success', async () => {
227+
// Submit the form
228+
const submitButton = canvas.getByRole('button', { name: /submit/i });
229+
await userEvent.click(submitButton);
230+
231+
// Verify submission results with comprehensive assertions
232+
await expect(canvas.findByText('Submitted with:')).resolves.toBeInTheDocument();
233+
await expect(canvas.findByText(/Date:/)).resolves.toBeInTheDocument();
234+
await expect(canvas.findByText('Dropdown Type: dropdown-months')).resolves.toBeInTheDocument();
235+
});
237236
},
238237
};
239238

@@ -245,50 +244,44 @@ export const YearOnlyDropdown: Story = {
245244
},
246245
},
247246
},
248-
play: async ({ canvasElement }) => {
247+
play: async ({ canvasElement, step }) => {
249248
const canvas = within(canvasElement);
250249

251-
// Wait for initial render
252-
await sleep(500);
253-
254-
// Find the dropdown select button
255-
const dropdownSelectButton = await canvas.findByRole('combobox', { expanded: false });
256-
expect(dropdownSelectButton).toBeInTheDocument();
257-
258-
// Click to open the dropdown
259-
await userEvent.click(dropdownSelectButton);
260-
await sleep(500);
261-
262-
// Find and click "Year Only" option - look in the popover content
263-
const yearOnlyOption =
264-
document.querySelector('[data-value="dropdown-years"]') || (await canvas.findByText('Year Only'));
265-
await userEvent.click(yearOnlyOption);
266-
await sleep(300);
267-
268-
// Verify the dropdown closed and selection changed
269-
expect(dropdownSelectButton).toHaveTextContent('Year Only');
270-
271-
// Test calendar interaction with year-only dropdown
272-
const dayButtons = await canvas.findAllByRole('gridcell');
273-
const availableDay = dayButtons.find(
274-
(day) => day.textContent && /^\d+$/.test(day.textContent.trim()) && !day.hasAttribute('aria-disabled'),
275-
) as HTMLElement;
276-
277-
console.log('>>>> availableDay', availableDay);
278-
279-
if (availableDay) {
280-
await userEvent.click(availableDay);
281-
await sleep(300);
282-
}
283-
284-
// Submit the form
285-
const submitButton = await canvas.findByRole('button', { name: /submit/i });
286-
await userEvent.click(submitButton);
287-
await sleep(1000);
288-
289-
// Verify submission with year-only dropdown
290-
expect(canvas.getByText('Submitted with:')).toBeInTheDocument();
291-
expect(canvas.getByText('Dropdown Type: dropdown-years')).toBeInTheDocument();
250+
await step('Verify initial state and change to Year Only', async () => {
251+
// Find the dropdown select button (use text content)
252+
const dropdownSelectButton = await canvas.findByText('Month and Year');
253+
expect(dropdownSelectButton).toBeInTheDocument();
254+
255+
// Click to open the dropdown
256+
await userEvent.click(dropdownSelectButton);
257+
258+
// Find and click "Year Only" option
259+
const yearOnlyOption = await within(document.body).findByText('Year Only');
260+
await userEvent.click(yearOnlyOption);
261+
262+
// Verify the dropdown selection changed by checking the new text
263+
expect(canvas.getByText('Year Only')).toBeInTheDocument();
264+
});
265+
266+
await step('Navigate calendar and select a date', async () => {
267+
// With year-only dropdown, we should see the year dropdown
268+
const yearDropdown = canvas.getByLabelText('Choose the Year');
269+
expect(yearDropdown).toBeInTheDocument();
270+
271+
// Find a specific date button using aria-label (includes day of week and year)
272+
const dateButton = await canvas.findByRole('button', { name: /15th, 2025/ });
273+
await userEvent.click(dateButton);
274+
});
275+
276+
await step('Submit form and verify success', async () => {
277+
// Submit the form
278+
const submitButton = canvas.getByRole('button', { name: /submit/i });
279+
await userEvent.click(submitButton);
280+
281+
// Verify submission with year-only dropdown
282+
await expect(canvas.findByText('Submitted with:')).resolves.toBeInTheDocument();
283+
await expect(canvas.findByText('Dropdown Type: dropdown-years')).resolves.toBeInTheDocument();
284+
});
292285
},
293286
};
294287

@@ -300,41 +293,44 @@ export const MonthAndYearDropdown: Story = {
300293
},
301294
},
302295
},
303-
play: async ({ canvasElement }) => {
296+
play: async ({ canvasElement, step }) => {
304297
const canvas = within(canvasElement);
305298

306-
// Wait for initial render
307-
await sleep(500);
308-
309-
// Verify default dropdown selection
310-
const dropdownSelectButton = await canvas.findByRole('combobox', { expanded: false });
311-
expect(dropdownSelectButton).toHaveTextContent('Month and Year');
312-
313-
// Test calendar navigation with month/year dropdowns
314-
// Look for month/year dropdown elements in the calendar
315-
const calendarDropdowns = document.querySelectorAll('[data-slot="calendar"] select');
316-
expect(calendarDropdowns.length).toBeGreaterThan(0);
317-
318-
// Select a date
319-
const dayButtons = await canvas.findAllByRole('gridcell');
320-
const availableDay = dayButtons.find(
321-
(day) => day.textContent && /^\d+$/.test(day.textContent.trim()) && !day.hasAttribute('aria-disabled'),
322-
) as HTMLElement;
323-
324-
console.log('>>>> availableDay', availableDay);
325-
326-
if (availableDay) {
327-
await userEvent.click(availableDay);
328-
await sleep(300);
329-
}
330-
331-
// Submit the form
332-
const submitButton = await canvas.findByRole('button', { name: /submit/i });
333-
await userEvent.click(submitButton);
334-
await sleep(1000);
335-
336-
// Verify submission with default dropdown
337-
expect(canvas.getByText('Submitted with:')).toBeInTheDocument();
338-
expect(canvas.getByText('Dropdown Type: dropdown')).toBeInTheDocument();
299+
await step('Verify initial state with default dropdown', async () => {
300+
// Verify default dropdown selection (use text content)
301+
const dropdownSelectButton = await canvas.findByText('Month and Year');
302+
expect(dropdownSelectButton).toBeInTheDocument();
303+
304+
// Test calendar navigation with month/year dropdowns
305+
// Verify both month and year dropdowns are present
306+
const monthDropdown = canvas.getByLabelText('Choose the Month');
307+
const yearDropdown = canvas.getByLabelText('Choose the Year');
308+
expect(monthDropdown).toBeInTheDocument();
309+
expect(yearDropdown).toBeInTheDocument();
310+
});
311+
312+
await step('Navigate calendar and select a date', async () => {
313+
// Use both month and year dropdowns to navigate
314+
const monthDropdown = canvas.getByLabelText('Choose the Month');
315+
const yearDropdown = canvas.getByLabelText('Choose the Year');
316+
317+
// Navigate to July 2025
318+
await userEvent.selectOptions(monthDropdown, '6'); // June is month 6 (0-indexed)
319+
await userEvent.selectOptions(yearDropdown, '2025');
320+
321+
// Find a specific date button using aria-label (includes day of week)
322+
const dateButton = await canvas.findByRole('button', { name: /15th, 2025/i });
323+
await userEvent.click(dateButton);
324+
});
325+
326+
await step('Submit form and verify success', async () => {
327+
// Submit the form
328+
const submitButton = canvas.getByRole('button', { name: /submit/i });
329+
await userEvent.click(submitButton);
330+
331+
// Verify submission with default dropdown
332+
await expect(canvas.findByText('Submitted with:')).resolves.toBeInTheDocument();
333+
await expect(canvas.findByText('Dropdown Type: dropdown')).resolves.toBeInTheDocument();
334+
});
339335
},
340336
};

0 commit comments

Comments
 (0)