@@ -5,10 +5,12 @@ import { Select } from '@lambdacurry/forms/remix-hook-form/select';
55import { TextField } from '@lambdacurry/forms/remix-hook-form/text-field' ;
66import type { Meta , StoryContext , StoryObj } from '@storybook/react-vite' ;
77import { expect , userEvent , within , screen , waitFor } from '@storybook/test' ;
8- import { useState } from 'react' ;
9- import { type ActionFunctionArgs , useFetcher } from 'react-router' ;
10- import { getValidatedFormData , RemixFormProvider , useRemixForm } from 'remix-hook-form' ;
8+ import { useState , useMemo } from 'react' ;
9+ import { useFetcher } from 'react-router' ;
10+ import { useRemixForm , RemixFormProvider , getValidatedFormData } from 'remix-hook-form' ;
1111import { z } from 'zod' ;
12+ import type { ActionFunctionArgs } from 'react-router' ;
13+ import { selectRadixOption } from '../lib/storybook/test-utils' ;
1214import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub' ;
1315
1416/**
@@ -156,22 +158,24 @@ export const CascadingDropdowns: Story = {
156158 play : async ( { canvasElement } : StoryContext ) => {
157159 const canvas = within ( canvasElement ) ;
158160
159- // Select a country
160- const countryTrigger = canvas . getByRole ( 'combobox' , { name : / c o u n t r y / i } ) ;
161- await userEvent . click ( countryTrigger ) ;
162-
163- // Wait for dropdown to open and select USA
164- const usaOption = await screen . findByTestId ( 'select-option-usa' ) ;
165- await userEvent . click ( usaOption ) ;
166-
167- // Verify state dropdown is now enabled
168- const stateTrigger = canvas . getByRole ( 'combobox' , { name : / s t a t e / i } ) ;
169- expect ( stateTrigger ) . not . toBeDisabled ( ) ;
170-
171- // Select a state
172- await userEvent . click ( stateTrigger ) ;
173- const californiaOption = await screen . findByTestId ( 'select-option-california' ) ;
174- await userEvent . click ( californiaOption ) ;
161+ // Select USA
162+ await selectRadixOption ( canvasElement , {
163+ triggerName : / c o u n t r y / i,
164+ optionName : / u n i t e d s t a t e s / i,
165+ optionTestId : 'select-option-usa' ,
166+ } ) ;
167+
168+ // Select a state (wait for it to be enabled)
169+ await waitFor ( ( ) => {
170+ const stateTrigger = canvas . getByRole ( 'combobox' , { name : / s t a t e / i } ) ;
171+ expect ( stateTrigger ) . not . toBeDisabled ( ) ;
172+ } ) ;
173+
174+ await selectRadixOption ( canvasElement , {
175+ triggerName : / s t a t e / i,
176+ optionName : / c a l i f o r n i a / i,
177+ optionTestId : 'select-option-california' ,
178+ } ) ;
175179
176180 // Enter city
177181 const cityInput = canvas . getByLabelText ( / c i t y / i) ;
@@ -213,7 +217,7 @@ type OrderFormData = z.infer<typeof orderSchema>;
213217const AutoCalculationExample = ( ) => {
214218 const fetcher = useFetcher < { message : string } > ( ) ;
215219
216- const methods = useRemixForm < OrderFormData > ( {
220+ const rawMethods = useRemixForm < OrderFormData > ( {
217221 resolver : zodResolver ( orderSchema ) ,
218222 defaultValues : {
219223 quantity : '1' ,
@@ -228,6 +232,10 @@ const AutoCalculationExample = () => {
228232 } ,
229233 } ) ;
230234
235+ // Memoize methods to prevent unnecessary re-renders of the story tree
236+ // which can disrupt interaction tests using Portals
237+ const methods = useMemo ( ( ) => rawMethods , [ rawMethods ] ) ;
238+
231239 const calculateTotal = ( ) => {
232240 const quantity = Number . parseFloat ( methods . getValues ( 'quantity' ) || '0' ) ;
233241 const pricePerUnit = Number . parseFloat ( methods . getValues ( 'pricePerUnit' ) || '0' ) ;
@@ -382,7 +390,7 @@ const ConditionalFieldsExample = () => {
382390 const [ showShipping , setShowShipping ] = useState ( false ) ;
383391 const [ showPickup , setShowPickup ] = useState ( false ) ;
384392
385- const methods = useRemixForm < ShippingFormData > ( {
393+ const rawMethods = useRemixForm < ShippingFormData > ( {
386394 resolver : zodResolver ( shippingSchema ) ,
387395 defaultValues : {
388396 deliveryType : '' ,
@@ -396,6 +404,9 @@ const ConditionalFieldsExample = () => {
396404 } ,
397405 } ) ;
398406
407+ // Memoize methods to prevent unnecessary re-renders of the story tree
408+ const methods = useMemo ( ( ) => rawMethods , [ rawMethods ] ) ;
409+
399410 // Show/hide fields based on delivery type
400411 useOnFormValueChange ( {
401412 name : 'deliveryType' ,
@@ -482,34 +493,34 @@ export const ConditionalFields: Story = {
482493 const canvas = within ( canvasElement ) ;
483494
484495 // Select delivery
485- await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
486- const deliveryTypeTrigger = canvas . getByRole ( 'combobox' , { name : / d e l i v e r y t y p e / i } ) ;
487- await userEvent . click ( deliveryTypeTrigger ) ;
488-
489- const deliveryOption = await screen . findByTestId ( 'select-option-delivery' ) ;
490- await userEvent . click ( deliveryOption ) ;
496+ await selectRadixOption ( canvasElement , {
497+ triggerName : / d e l i v e r y t y p e / i,
498+ optionName : / h o m e d e l i v e r y / i,
499+ optionTestId : 'select-option-delivery' ,
500+ } ) ;
491501
492502 // Shipping address field should appear
493503 const shippingInput = await canvas . findByLabelText ( / s h i p p i n g a d d r e s s / i) ;
494504 expect ( shippingInput ) . toBeInTheDocument ( ) ;
495505 await userEvent . type ( shippingInput , '123 Main St' ) ;
496506
497507 // Switch to pickup
498- await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
499- const trigger = await canvas . findByRole ( 'combobox' , { name : / d e l i v e r y t y p e / i } ) ;
500- await userEvent . click ( trigger ) ;
501-
502- const pickupOption = await screen . findByTestId ( 'select-option-pickup' , { } , { timeout : 5000 } ) ;
503- await userEvent . click ( pickupOption ) ;
508+ await selectRadixOption ( canvasElement , {
509+ triggerName : / d e l i v e r y t y p e / i,
510+ optionName : / s t o r e p i c k u p / i,
511+ optionTestId : 'select-option-pickup' ,
512+ } ) ;
504513
505514 // Store location should appear, shipping address should be gone
506515 const storeSelect = await canvas . findByRole ( 'combobox' , { name : / s t o r e l o c a t i o n / i } ) ;
507516 expect ( storeSelect ) . toBeInTheDocument ( ) ;
508517
509518 // Select a store
510- await userEvent . click ( storeSelect ) ;
511- const mallOption = await screen . findByTestId ( 'select-option-mall' ) ;
512- await userEvent . click ( mallOption ) ;
519+ await selectRadixOption ( canvasElement , {
520+ triggerName : / s t o r e l o c a t i o n / i,
521+ optionName : / s h o p p i n g m a l l / i,
522+ optionTestId : 'select-option-mall' ,
523+ } ) ;
513524
514525 // Submit form
515526 const submitButton = canvas . getByRole ( 'button' , { name : / c o m p l e t e o r d e r / i } ) ;
0 commit comments