@@ -8,6 +8,8 @@ import { RemixFormProvider, getValidatedFormData, useRemixForm } from 'remix-hoo
88import { z } from 'zod' ;
99import { withReactRouterStubDecorator } from '../lib/storybook/react-router-stub' ;
1010
11+ const successMessageRegex = / F o r m s u b m i t t e d s u c c e s s f u l l y / ;
12+
1113// Define a schema for phone number validation
1214const formSchema = z . object ( {
1315 usaPhone : z . string ( ) . min ( 1 , 'USA phone number is required' ) ,
@@ -37,16 +39,14 @@ const ControlledPhoneInputExample = () => {
3739 < div className = "grid gap-8" >
3840 < PhoneInput
3941 name = "usaPhone"
40- label = "USA Phone Number"
42+ label = "Phone Number"
4143 description = "Enter a US phone number"
42- defaultCountry = "US"
43- international = { false }
4444 />
4545 < PhoneInput
4646 name = "internationalPhone"
4747 label = "International Phone Number"
4848 description = "Enter an international phone number"
49- international = { true }
49+ isInternational = { true }
5050 />
5151 </ div >
5252 < Button type = "submit" className = "mt-8" >
@@ -65,8 +65,8 @@ const handleFormSubmission = async (request: Request) => {
6565 return { errors } ;
6666 }
6767
68- return {
69- message : `Form submitted successfully! USA: ${ data . usaPhone } , International: ${ data . internationalPhone } `
68+ return {
69+ message : `Form submitted successfully! USA: ${ data . usaPhone } , International: ${ data . internationalPhone } ` ,
7070 } ;
7171} ;
7272
@@ -75,6 +75,12 @@ const meta: Meta<typeof PhoneInput> = {
7575 component : PhoneInput ,
7676 parameters : { layout : 'centered' } ,
7777 tags : [ 'autodocs' ] ,
78+ } satisfies Meta < typeof PhoneInput > ;
79+
80+ export default meta ;
81+ type Story = StoryObj < typeof meta > ;
82+
83+ export const Default : Story = {
7884 decorators : [
7985 withReactRouterStubDecorator ( {
8086 routes : [
@@ -86,12 +92,6 @@ const meta: Meta<typeof PhoneInput> = {
8692 ] ,
8793 } ) ,
8894 ] ,
89- } satisfies Meta < typeof PhoneInput > ;
90-
91- export default meta ;
92- type Story = StoryObj < typeof meta > ;
93-
94- export const Default : Story = {
9595 parameters : {
9696 docs : {
9797 description : {
@@ -125,16 +125,14 @@ const ControlledPhoneInputExample = () => {
125125 <div className="grid gap-8">
126126 <PhoneInput
127127 name="usaPhone"
128- label="USA Phone Number"
128+ label="Phone Number"
129129 description="Enter a US phone number"
130- defaultCountry="US"
131- international={false}
132130 />
133131 <PhoneInput
134132 name="internationalPhone"
135133 label="International Phone Number"
136134 description="Enter an international phone number"
137- international={true}
135+ isInternational
138136 />
139137 </div>
140138 <Button type="submit" className="mt-8">
@@ -152,21 +150,21 @@ const ControlledPhoneInputExample = () => {
152150 const canvas = within ( canvasElement ) ;
153151
154152 await step ( 'Verify initial state' , async ( ) => {
155- // Verify phone input fields are present
156- const usaPhoneLabel = canvas . getByLabelText ( 'USA Phone Number') ;
157- const internationalPhoneLabel = canvas . getByLabelText ( 'International Phone Number' ) ;
158-
153+ // Wait for inputs to be mounted and associated with their labels
154+ const usaPhoneLabel = await canvas . findByLabelText ( ' Phone Number') ;
155+ const internationalPhoneLabel = await canvas . findByLabelText ( 'International Phone Number' ) ;
156+
159157 expect ( usaPhoneLabel ) . toBeInTheDocument ( ) ;
160158 expect ( internationalPhoneLabel ) . toBeInTheDocument ( ) ;
161159
162- // Verify submit button is present
163- const submitButton = canvas . getByRole ( 'button' , { name : 'Submit' } ) ;
160+ // Wait for submit button to be present
161+ const submitButton = await canvas . findByRole ( 'button' , { name : 'Submit' } ) ;
164162 expect ( submitButton ) . toBeInTheDocument ( ) ;
165163 } ) ;
166164
167165 await step ( 'Test validation errors on invalid submission' , async ( ) => {
168166 // Submit form without entering phone numbers
169- const submitButton = canvas . getByRole ( 'button' , { name : 'Submit' } ) ;
167+ const submitButton = await canvas . findByRole ( 'button' , { name : 'Submit' } ) ;
170168 await userEvent . click ( submitButton ) ;
171169
172170 // Verify validation error messages appear
@@ -175,27 +173,36 @@ const ControlledPhoneInputExample = () => {
175173 } ) ;
176174
177175 await step ( 'Test successful form submission with valid phone numbers' , async ( ) => {
178- // Enter valid phone numbers
179- const usaPhoneInput = canvas . getByLabelText ( 'USA Phone Number') ;
180- const internationalPhoneInput = canvas . getByLabelText ( 'International Phone Number' ) ;
176+ // Enter valid phone numbers (await the inputs before typing)
177+ const usaPhoneInput = await canvas . findByLabelText ( ' Phone Number') ;
178+ const internationalPhoneInput = await canvas . findByLabelText ( 'International Phone Number' ) ;
181179
182- // Enter a US phone number
180+ // Enter a US phone number (should format to (202) 555-0123)
183181 await userEvent . type ( usaPhoneInput , '2025550123' ) ;
184-
185- // Enter an international phone number (UK format)
182+
183+ // Enter an international phone number (UK example digits; component will normalize & format with + and spaces )
186184 await userEvent . type ( internationalPhoneInput , '7911123456' ) ;
187185
188186 // Submit form
189- const submitButton = canvas . getByRole ( 'button' , { name : 'Submit' } ) ;
187+ const submitButton = await canvas . findByRole ( 'button' , { name : 'Submit' } ) ;
190188 await userEvent . click ( submitButton ) ;
191189
192- // Verify success message
193- await expect ( canvas . findByText ( / F o r m s u b m i t t e d s u c c e s s f u l l y / ) ) . resolves . toBeInTheDocument ( ) ;
190+ // Verify success message (regex matches the prefix of the success text)
191+ await expect ( canvas . findByText ( successMessageRegex ) ) . resolves . toBeInTheDocument ( ) ;
194192 } ) ;
195193 } ,
196194} ;
197195
198196export const WithCustomStyling : Story = {
197+ decorators : [
198+ withReactRouterStubDecorator ( {
199+ routes : [
200+ {
201+ path : '/' ,
202+ } ,
203+ ] ,
204+ } ) ,
205+ ] ,
199206 render : ( ) => {
200207 const fetcher = useFetcher < { message : string } > ( ) ;
201208 const methods = useRemixForm < FormData > ( {
@@ -219,85 +226,16 @@ export const WithCustomStyling: Story = {
219226 name = "usaPhone"
220227 label = "Custom Styled Phone Input"
221228 description = "With custom styling applied"
222- defaultCountry = "US"
223229 className = "border-2 border-blue-500 p-4 rounded-lg"
224230 inputClassName = "bg-gray-100"
225- selectClassName = "bg-gray-100 border-blue-300"
226- />
227- </ div >
228- < Button type = "submit" className = "mt-8" >
229- Submit
230- </ Button >
231- </ fetcher . Form >
232- </ RemixFormProvider >
233- ) ;
234- } ,
235- parameters : {
236- docs : {
237- description : {
238- story : 'Phone input with custom styling applied.' ,
239- } ,
240- } ,
241- } ,
242- } ;
243-
244- export const WithDifferentDefaultCountries : Story = {
245- render : ( ) => {
246- const fetcher = useFetcher < { message : string } > ( ) ;
247- const methods = useRemixForm < {
248- usPhone : string ;
249- ukPhone : string ;
250- canadaPhone : string ;
251- australiaPhone : string ;
252- } > ( {
253- resolver : zodResolver (
254- z . object ( {
255- usPhone : z . string ( ) . optional ( ) ,
256- ukPhone : z . string ( ) . optional ( ) ,
257- canadaPhone : z . string ( ) . optional ( ) ,
258- australiaPhone : z . string ( ) . optional ( ) ,
259- } )
260- ) ,
261- defaultValues : {
262- usPhone : '' ,
263- ukPhone : '' ,
264- canadaPhone : '' ,
265- australiaPhone : '' ,
266- } ,
267- fetcher,
268- submitConfig : {
269- action : '/' ,
270- method : 'post' ,
271- } ,
272- } ) ;
273-
274- return (
275- < RemixFormProvider { ...methods } >
276- < fetcher . Form onSubmit = { methods . handleSubmit } >
277- < div className = "grid gap-8" >
278- < PhoneInput
279- name = "usPhone"
280- label = "US Phone Number"
281- defaultCountry = "US"
282- international = { true }
283231 />
284232 < PhoneInput
285- name = "ukPhone"
286- label = "UK Phone Number"
287- defaultCountry = "GB"
288- international = { true }
289- />
290- < PhoneInput
291- name = "canadaPhone"
292- label = "Canada Phone Number"
293- defaultCountry = "CA"
294- international = { true }
295- />
296- < PhoneInput
297- name = "australiaPhone"
298- label = "Australia Phone Number"
299- defaultCountry = "AU"
300- international = { true }
233+ name = "internationalPhone"
234+ label = "Custom Styled Intl Phone Input"
235+ description = "With custom styling applied"
236+ isInternational
237+ className = "border-2 border-blue-500 p-4 rounded-lg"
238+ inputClassName = "bg-gray-100"
301239 />
302240 </ div >
303241 < Button type = "submit" className = "mt-8" >
@@ -310,9 +248,8 @@ export const WithDifferentDefaultCountries: Story = {
310248 parameters : {
311249 docs : {
312250 description : {
313- story : 'Phone inputs with different default countries .' ,
251+ story : 'Phone input with custom styling applied for US and International modes .' ,
314252 } ,
315253 } ,
316254 } ,
317255} ;
318-
0 commit comments