Skip to content

Commit

Permalink
fix(stepper): improve panel status updates (port to 15.x) (#1205)
Browse files Browse the repository at this point in the history
This is a port of dcb5c29 (#1184) to 15.x.

- Set the panel status to invalid immediately when the form becomes
invalid.
- Set the panel status back to valid immediately when the form becomes
valid after being invalid.

CDE-1463
  • Loading branch information
kevinbuhmann authored Feb 6, 2024
1 parent 9030f9f commit 5958a8d
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ export class StepperModel extends AccordionModel {
});
}

setPanelValid(panelId: string) {
this._panels[panelId].status = AccordionStatus.Complete;
}

setPanelInvalid(panelId: string) {
this._panels[panelId].status = AccordionStatus.Error;
}

setPanelsWithErrors(ids: string[]) {
ids.forEach(id => this.setPanelError(id));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ export class StepperService extends AccordionService {
this.emitUpdatedPanels();
}

setPanelValid(panelId: string) {
this.accordion.setPanelValid(panelId);
this.emitUpdatedPanels();
}

setPanelInvalid(panelId: string) {
this.accordion.setPanelInvalid(panelId);
this.emitUpdatedPanels();
}

setPanelsWithErrors(ids: string[]) {
this.accordion.setPanelsWithErrors(ids);
this.emitUpdatedPanels();
Expand Down
15 changes: 10 additions & 5 deletions projects/angular/src/accordion/stepper/stepper-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@angular/core';
import { FormGroupName, NgModelGroup } from '@angular/forms';
import { Subscription } from 'rxjs';
import { filter, pairwise, tap } from 'rxjs/operators';
import { distinctUntilChanged, filter, skipUntil, tap } from 'rxjs/operators';

import { IfExpandService } from '../../utils/conditional/if-expanded.service';
import { triggerAllFormControlValidation } from '../../utils/forms/validation';
Expand Down Expand Up @@ -73,12 +73,17 @@ export class ClrStepperPanel extends ClrAccordionPanel implements OnInit {
this.stepperService.disablePanel(this.id, true);
this.listenToFocusChanges();

// not all stepper panels are guaranteed to have a form (i.e. empty template-driven)
if (this.formGroup) {
// not all stepper panels are guaranteed to have a form (i.e. empty template-driven)
// set panel status on form status change only after the form becomes invalid
const invalidStatusTrigger = this.formGroup.statusChanges.pipe(filter(status => status === 'INVALID'));

this.subscriptions.push(
this.formGroup.statusChanges.pipe(pairwise()).subscribe(([prevStatus, newStatus]) => {
if ('VALID' === prevStatus && 'INVALID' === newStatus) {
this.stepperService.navigateToNextPanel(this.id, this.formGroup.valid);
this.formGroup.statusChanges.pipe(skipUntil(invalidStatusTrigger), distinctUntilChanged()).subscribe(status => {
if (status === 'VALID') {
this.stepperService.setPanelValid(this.id);
} else if (status === 'INVALID') {
this.stepperService.setPanelInvalid(this.id);
}
})
);
Expand Down
48 changes: 45 additions & 3 deletions projects/angular/src/accordion/stepper/stepper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe('ClrStepper', () => {

it('should reset if a previously completed panel is revisited and put into an invalid state', () => {
// all setup...
spyOn(stepperService, 'navigateToNextPanel');
spyOn(stepperService, 'setPanelInvalid');
const group1 = testComponent.form.controls.group as FormGroup;
group1.controls.name.setValue('lmnop');
fixture.detectChanges();
Expand All @@ -109,8 +109,50 @@ describe('ClrStepper', () => {
fixture.detectChanges();

expect(group1.valid).toBe(false, 'first panel form is now invalid');
// making a previously valid form invalid forces navigation; called once earlier and only once after this
expect(stepperService.navigateToNextPanel).toHaveBeenCalledTimes(2);
expect(stepperService.setPanelInvalid).toHaveBeenCalledTimes(1);
});

it('should set the panel status to invalid immediately', () => {
// setup
spyOn(stepperService, 'setPanelInvalid');
const form = testComponent.form.controls.group;

// act (make form invalid)
form.controls.name.setValue('');
fixture.detectChanges();

// assert
expect(stepperService.setPanelInvalid).toHaveBeenCalledTimes(1);
});

it('should set the panel status to valid if form was previously invalid', () => {
// setup
spyOn(stepperService, 'setPanelValid');
const form = testComponent.form.controls.group;

// act 1 (make form invalid)
form.controls.name.setValue('');
fixture.detectChanges();

// act 2 (make form valid)
form.controls.name.setValue('Bob');
fixture.detectChanges();

// assert
expect(stepperService.setPanelValid).toHaveBeenCalledTimes(1);
});

it('should not set the panel status to valid if the form was not previously invalid', () => {
// setup
spyOn(stepperService, 'setPanelInvalid');
const form = testComponent.form.controls.group;

// act (make form valid)
form.controls.name.setValue('Bob');
fixture.detectChanges();

// assert
expect(stepperService.setPanelInvalid).not.toHaveBeenCalled();
});
});

Expand Down

0 comments on commit 5958a8d

Please sign in to comment.