Skip to content

Commit 57577ce

Browse files
authored
fix(Forms): always run validation on Wizard step changes (#4626)
1 parent 2b87982 commit 57577ce

File tree

2 files changed

+128
-47
lines changed

2 files changed

+128
-47
lines changed

packages/dnb-eufemia/src/extensions/forms/Wizard/Container/WizardContainer.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ function WizardContainer(props: Props) {
307307

308308
const handleChange = useCallback(
309309
({ current_step }) => {
310-
setActiveIndex(current_step, { skipErrorCheck: true })
310+
setActiveIndex(current_step)
311311
},
312312
[setActiveIndex]
313313
)

packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.test.tsx

+127-46
Original file line numberDiff line numberDiff line change
@@ -241,52 +241,55 @@ describe('Wizard.Container', () => {
241241
)
242242

243243
expect(output()).toHaveTextContent('Step 1')
244-
expect(screen.queryByRole('alert')).toBeNull()
244+
expect(document.querySelector('.dnb-form-status')).toBeNull()
245245

246246
await userEvent.click(nextButton())
247247

248+
expect(output()).toHaveTextContent('Step 2')
249+
248250
await waitFor(() => {
249-
expect(output()).toHaveTextContent('Step 2')
250-
expect(screen.queryByRole('alert')).toBeNull()
251+
expect(document.querySelector('.dnb-form-status')).toBeNull()
251252
})
252253

253254
await userEvent.click(nextButton())
254255

256+
expect(output()).toHaveTextContent('Step 2')
257+
255258
await waitFor(() => {
256-
expect(output()).toHaveTextContent('Step 2')
257-
expect(screen.queryByRole('alert')).toBeInTheDocument()
259+
expect(
260+
document.querySelector('.dnb-form-status')
261+
).toBeInTheDocument()
258262
})
259263

260264
await userEvent.click(previousButton())
261265

266+
expect(output()).toHaveTextContent('Step 1')
267+
262268
await waitFor(() => {
263-
expect(output()).toHaveTextContent('Step 1')
264-
expect(screen.queryByRole('alert')).toBeNull()
269+
expect(document.querySelector('.dnb-form-status')).toBeNull()
265270
})
266271

267272
await userEvent.click(nextButton())
268273

269-
await waitFor(() => {
270-
expect(output()).toHaveTextContent('Step 2')
271-
})
274+
expect(output()).toHaveTextContent('Step 2')
272275

273276
await waitFor(() => {
274-
expect(screen.queryByRole('alert')).toBeInTheDocument()
277+
expect(
278+
document.querySelector('.dnb-form-status')
279+
).toBeInTheDocument()
275280
})
276281

277282
await userEvent.type(document.querySelector('input'), 'foo')
278283

279284
await waitFor(() => {
280-
expect(screen.queryByRole('alert')).toBeNull()
285+
expect(document.querySelector('.dnb-form-status')).toBeNull()
281286
})
282287

283288
await userEvent.click(nextButton())
284289

285-
await waitFor(() => {
286-
expect(output()).toHaveTextContent('Step 3')
287-
})
290+
expect(output()).toHaveTextContent('Step 3')
288291

289-
expect(screen.queryByRole('alert')).toBeNull()
292+
expect(document.querySelector('.dnb-form-status')).toBeNull()
290293
})
291294

292295
it('should support navigating back and forth with async validators', async () => {
@@ -332,28 +335,28 @@ describe('Wizard.Container', () => {
332335
const input = () => document.querySelector('input')
333336

334337
expect(output()).toHaveTextContent('Step 1')
335-
expect(screen.queryByRole('alert')).toBeNull()
338+
expect(document.querySelector('.dnb-form-status')).toBeNull()
336339

337340
await userEvent.click(nextButton())
338341

339342
expect(output()).toHaveTextContent('Step 1')
340-
expect(screen.queryByRole('alert')).toHaveTextContent(
343+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
341344
nb.Field.errorRequired
342345
)
343346

344347
await userEvent.type(input(), 'invalid')
345348
fireEvent.click(nextButton())
346349

347350
expect(output()).toHaveTextContent('Step 1')
348-
expect(screen.queryByRole('alert')).toHaveTextContent(
351+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
349352
'onChangeValidator-error'
350353
)
351354

352355
fireEvent.blur(input())
353356

354357
await waitFor(() => {
355358
expect(output()).toHaveTextContent('Step 1')
356-
expect(screen.queryByRole('alert')).toHaveTextContent(
359+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
357360
'onBlurValidator-error'
358361
)
359362
})
@@ -362,33 +365,33 @@ describe('Wizard.Container', () => {
362365
await userEvent.click(nextButton())
363366

364367
expect(output()).toHaveTextContent('Step 2')
365-
expect(screen.queryByRole('alert')).toBeNull()
368+
expect(document.querySelector('.dnb-form-status')).toBeNull()
366369

367370
await userEvent.click(nextButton())
368371

369372
expect(output()).toHaveTextContent('Step 2')
370-
expect(screen.queryByRole('alert')).toHaveTextContent(
373+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
371374
nb.Field.errorRequired
372375
)
373376

374377
await userEvent.click(previousButton())
375378

376379
expect(output()).toHaveTextContent('Step 1')
377-
expect(screen.queryByRole('alert')).toBeNull()
380+
expect(document.querySelector('.dnb-form-status')).toBeNull()
378381

379382
await userEvent.type(input(), '{Backspace>8}invalid')
380383
fireEvent.click(nextButton())
381384

382385
expect(output()).toHaveTextContent('Step 1')
383-
expect(screen.queryByRole('alert')).toHaveTextContent(
386+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
384387
'onChangeValidator-error'
385388
)
386389

387390
fireEvent.blur(input())
388391

389392
await waitFor(() => {
390393
expect(output()).toHaveTextContent('Step 1')
391-
expect(screen.queryByRole('alert')).toHaveTextContent(
394+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
392395
'onBlurValidator-error'
393396
)
394397
})
@@ -397,7 +400,7 @@ describe('Wizard.Container', () => {
397400
await userEvent.click(nextButton())
398401

399402
expect(output()).toHaveTextContent('Step 2')
400-
expect(screen.queryByRole('alert')).toHaveTextContent(
403+
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
401404
nb.Field.errorRequired
402405
)
403406
}, 20000)
@@ -528,35 +531,39 @@ describe('Wizard.Container', () => {
528531
)
529532

530533
expect(output()).toHaveTextContent('Step 1')
531-
expect(screen.queryByRole('alert')).toBeNull()
534+
expect(document.querySelector('.dnb-form-status')).toBeNull()
532535

533536
await userEvent.click(secondStep.querySelector('button'))
534537

535538
await waitFor(() => {
536539
expect(output()).toHaveTextContent('Step 2')
537-
expect(screen.queryByRole('alert')).toBeNull()
540+
expect(document.querySelector('.dnb-form-status')).toBeNull()
538541
})
539542

540543
// Show the error message
541544
await userEvent.click(nextButton())
542545

543546
await waitFor(() => {
544547
expect(output()).toHaveTextContent('Step 2')
545-
expect(screen.queryByRole('alert')).toBeInTheDocument()
548+
expect(
549+
document.querySelector('.dnb-form-status')
550+
).toBeInTheDocument()
546551
})
547552

548553
await userEvent.click(firstStep.querySelector('button'))
549554

550555
await waitFor(() => {
551556
expect(output()).toHaveTextContent('Step 1')
552-
expect(screen.queryByRole('alert')).toBeNull()
557+
expect(document.querySelector('.dnb-form-status')).toBeNull()
553558
})
554559

555560
await userEvent.click(secondStep.querySelector('button'))
556561

557562
await waitFor(() => {
558563
expect(output()).toHaveTextContent('Step 2')
559-
expect(screen.queryByRole('alert')).toBeInTheDocument()
564+
expect(
565+
document.querySelector('.dnb-form-status')
566+
).toBeInTheDocument()
560567
})
561568
})
562569

@@ -1917,9 +1924,9 @@ describe('Wizard.Container', () => {
19171924
await waitFor(() => {
19181925
expect(output()).toHaveTextContent('Step 1')
19191926
expect(screen.queryAllByRole('alert')).toHaveLength(1)
1920-
expect(screen.queryByRole('alert')).toHaveTextContent(
1921-
'Error message'
1922-
)
1927+
expect(
1928+
document.querySelector('.dnb-form-status')
1929+
).toHaveTextContent('Error message')
19231930
expect(previousButton()).toBeDisabled()
19241931
expect(nextButton()).not.toBeDisabled()
19251932
})
@@ -2039,9 +2046,9 @@ describe('Wizard.Container', () => {
20392046
await waitFor(() => {
20402047
expect(output()).toHaveTextContent('Step 1')
20412048
expect(screen.queryAllByRole('alert')).toHaveLength(1)
2042-
expect(screen.queryByRole('alert')).toHaveTextContent(
2043-
'Error message'
2044-
)
2049+
expect(
2050+
document.querySelector('.dnb-form-status')
2051+
).toHaveTextContent('Error message')
20452052
expect(previousButton()).toBeDisabled()
20462053
expect(nextButton()).not.toBeDisabled()
20472054
})
@@ -2191,12 +2198,12 @@ describe('Wizard.Container', () => {
21912198
)
21922199

21932200
expect(output()).toHaveTextContent('Step 1')
2194-
expect(screen.queryByRole('alert')).toBeNull()
2201+
expect(document.querySelector('.dnb-form-status')).toBeNull()
21952202

21962203
fireEvent.click(nextButton())
21972204

21982205
expect(output()).toHaveTextContent('Step 1')
2199-
expect(screen.queryByRole('alert')).toBeInTheDocument()
2206+
expect(document.querySelector('.dnb-form-status')).toBeInTheDocument()
22002207
})
22012208

22022209
it('should set focus on step change', async () => {
@@ -2342,25 +2349,25 @@ describe('Wizard.Container', () => {
23422349
)
23432350

23442351
expect(output()).toHaveTextContent('Step 1')
2345-
expect(screen.queryByRole('alert')).toBeNull()
2352+
expect(document.querySelector('.dnb-form-status')).toBeNull()
23462353

23472354
fireEvent.click(nextButton())
23482355

23492356
expect(output()).toHaveTextContent('Step 1')
2350-
expect(screen.queryByRole('alert')).toBeInTheDocument()
2357+
expect(document.querySelector('.dnb-form-status')).toBeInTheDocument()
23512358

23522359
await userEvent.type(document.querySelector('input'), 'valid')
2353-
expect(screen.queryByRole('alert')).toBeNull()
2360+
expect(document.querySelector('.dnb-form-status')).toBeNull()
23542361

23552362
await userEvent.click(nextButton())
23562363

23572364
expect(output()).toHaveTextContent('Step 2')
2358-
expect(screen.queryByRole('alert')).toBeNull()
2365+
expect(document.querySelector('.dnb-form-status')).toBeNull()
23592366

23602367
await userEvent.click(submitButton())
23612368

23622369
expect(output()).toHaveTextContent('Step 2')
2363-
expect(screen.queryByRole('alert')).toBeInTheDocument()
2370+
expect(document.querySelector('.dnb-form-status')).toBeInTheDocument()
23642371
})
23652372

23662373
it('should prevent navigation if `preventNavigation` is called', async () => {
@@ -2447,15 +2454,17 @@ describe('Wizard.Container', () => {
24472454
await userEvent.click(nextButton())
24482455

24492456
await waitFor(() => {
2450-
expect(screen.queryByRole('alert')).toBeInTheDocument()
2457+
expect(
2458+
document.querySelector('.dnb-form-status')
2459+
).toBeInTheDocument()
24512460
})
24522461

24532462
expect(output()).toHaveTextContent('Step 1')
24542463

24552464
await userEvent.type(document.querySelector('input'), 'valid')
24562465

24572466
await waitFor(() => {
2458-
expect(screen.queryByRole('alert')).toBeNull()
2467+
expect(document.querySelector('.dnb-form-status')).toBeNull()
24592468
})
24602469

24612470
await userEvent.click(nextButton())
@@ -2468,6 +2477,78 @@ describe('Wizard.Container', () => {
24682477
})
24692478
})
24702479

2480+
it('should run validation before every step change using StepIndicator to navigate back and forth', async () => {
2481+
const onStepChange = jest.fn()
2482+
2483+
render(
2484+
<Form.Handler>
2485+
<Wizard.Container onStepChange={onStepChange} mode="loose">
2486+
<Wizard.Step title="Step 1">
2487+
<Field.String required />
2488+
<output>Step 1</output>
2489+
<Wizard.Buttons />
2490+
</Wizard.Step>
2491+
<Wizard.Step title="Step 2">
2492+
<output>Step 2</output>
2493+
<Wizard.Buttons />
2494+
</Wizard.Step>
2495+
</Wizard.Container>
2496+
</Form.Handler>
2497+
)
2498+
2499+
const [firstStep, secondStep] = Array.from(
2500+
document.querySelectorAll('.dnb-step-indicator__item')
2501+
)
2502+
2503+
expect(output()).toHaveTextContent('Step 1')
2504+
2505+
// Try Step 2
2506+
await userEvent.click(secondStep.querySelector('.dnb-button'))
2507+
2508+
await waitFor(() => {
2509+
expect(
2510+
document.querySelector('.dnb-form-status')
2511+
).toBeInTheDocument()
2512+
})
2513+
2514+
expect(output()).toHaveTextContent('Step 1')
2515+
2516+
await userEvent.type(document.querySelector('input'), 'foo')
2517+
2518+
await waitFor(() => {
2519+
expect(document.querySelector('.dnb-form-status')).toBeNull()
2520+
})
2521+
2522+
// Go to Step 2
2523+
await userEvent.click(secondStep.querySelector('.dnb-button'))
2524+
2525+
expect(output()).toHaveTextContent('Step 2')
2526+
expect(onStepChange).toHaveBeenCalledTimes(1)
2527+
expect(onStepChange).toHaveBeenLastCalledWith(1, 'next', {
2528+
previousStep: { index: 0 },
2529+
preventNavigation: expect.any(Function),
2530+
})
2531+
2532+
// Go to Step 1
2533+
await userEvent.click(firstStep.querySelector('.dnb-button'))
2534+
2535+
expect(output()).toHaveTextContent('Step 1')
2536+
expect(document.querySelector('.dnb-form-status')).toBeNull()
2537+
2538+
await userEvent.type(document.querySelector('input'), '{Backspace>3}')
2539+
2540+
// Try Step 2
2541+
await userEvent.click(secondStep.querySelector('.dnb-button'))
2542+
2543+
await waitFor(() => {
2544+
expect(
2545+
document.querySelector('.dnb-form-status')
2546+
).toBeInTheDocument()
2547+
})
2548+
2549+
expect(output()).toHaveTextContent('Step 1')
2550+
})
2551+
24712552
describe('prerenderFieldProps and filterData', () => {
24722553
it('should keep field props in memory during step change', async () => {
24732554
const filterDataHandler = jest.fn(({ props }) => {

0 commit comments

Comments
 (0)