Skip to content

Commit

Permalink
feat(demo): add country form (#2617)
Browse files Browse the repository at this point in the history
  • Loading branch information
griest024 committed Nov 22, 2023
1 parent b90454e commit da882b1
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DaffNativeSelectModule,
DaffFormFieldModule,
} from '@daffodil/design';
import { DaffGeographyStateModule } from '@daffodil/geography/state';

import { AddressFormComponent } from './components/address-form/address-form.component';

Expand All @@ -17,6 +18,7 @@ import { AddressFormComponent } from './components/address-form/address-form.com
DaffInputModule,
DaffNativeSelectModule,
DaffFormFieldModule,
DaffGeographyStateModule,
],
declarations: [
AddressFormComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,23 @@
[formSubmitted]="submitted"/>
<daff-error-message *ngIf="formGroup.controls['city'].errors && (formGroup.controls['city'].touched || submitted)">This field is required.</daff-error-message>
</daff-form-field>
<daff-form-field class="demo-address-form__country">
<select daff-native-select
name="country"
[formControl]="formGroup.controls['country']"
[formSubmitted]="submitted">
<option [value]="''">Country</option>
<option *ngFor="let country of (countrySelectValues$ | async)" [value]="country.id" [disabled]="country.disabled">{{country.name}}</option>
</select>
<daff-error-message *ngIf="formGroup.controls['state'].errors && (formGroup.controls['state'].touched || submitted)">This field is required.</daff-error-message>
</daff-form-field>
<daff-form-field class="demo-address-form__state">
<select daff-native-select
name="state"
[formControl]="formGroup.controls['state']"
[formSubmitted]="submitted">
<option *ngFor="let state of stateSelectValues" [value]="state.value" [disabled]="state.disabled">{{state.label}}</option>
<option [value]="''">State</option>
<option *ngFor="let state of (stateSelectValues$ | async)" [value]="state.id" [disabled]="state.disabled">{{state.name}}</option>
</select>
<daff-error-message *ngIf="formGroup.controls['state'].errors && (formGroup.controls['state'].touched || submitted)">This field is required.</daff-error-message>
</daff-form-field>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
'firstname firstname firstname lastname lastname lastname'
' street street street street street street'
' city city state state postcode postcode'
'telephone telephone telephone free-space1 free-space1 free-space1';
'telephone telephone telephone country country country';

&__first-name {
grid-area: firstname;
Expand All @@ -32,6 +32,10 @@
grid-area: state;
}

&__country {
grid-area: country;
}

&__postcode {
grid-area: postcode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import {
UntypedFormBuilder,
} from '@angular/forms';
import { By } from '@angular/platform-browser';
import { cold } from 'jasmine-marbles';
import {
BehaviorSubject,
of,
} from 'rxjs';

import {
DaffInputModule,
Expand All @@ -21,22 +26,38 @@ import {
DaffNativeSelectModule,
DaffInputComponent,
} from '@daffodil/design';
import { DaffCountry } from '@daffodil/geography';
import { DaffCountryLoad } from '@daffodil/geography/state';
import {
DaffGeographyStateTestingModule,
MockDaffGeographyFacade,
} from '@daffodil/geography/state/testing';
import { DaffCountryFactory } from '@daffodil/geography/testing';

import { AddressFormComponent } from './address-form.component';
import { AddressFormFactory } from '../../factories/address-form.factory';
import { AddressFormGroup } from '../../models/address-form.type';

@Component({
template: '<demo-address-form [formGroup]="formGroupValue" ' +
'[submitted]="submittedValue"></demo-address-form>',
template: `
<demo-address-form
[formGroup]="formGroupValue"
[submitted]="submittedValue"
></demo-address-form>
`,
})
class WrapperComponent {
formGroupValue: UntypedFormGroup;
formGroupValue: AddressFormGroup;
submittedValue: boolean;
}

describe('AddressFormComponent', () => {
let wrapper: WrapperComponent;
let fixture: ComponentFixture<WrapperComponent>;
let addressForm: AddressFormComponent;
let component: AddressFormComponent;
let geographyFacade: MockDaffGeographyFacade;
let countryFactory: DaffCountryFactory;
let mockCountry: DaffCountry;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
Expand All @@ -46,6 +67,7 @@ describe('AddressFormComponent', () => {
DaffInputModule,
DaffNativeSelectModule,
DaffFormFieldModule,
DaffGeographyStateTestingModule,
],
declarations: [
WrapperComponent,
Expand All @@ -56,36 +78,34 @@ describe('AddressFormComponent', () => {
}));

beforeEach(() => {
geographyFacade = TestBed.inject(MockDaffGeographyFacade);
countryFactory = TestBed.inject(DaffCountryFactory);

mockCountry = countryFactory.create();
geographyFacade.countries$.next([mockCountry]);
spyOn(geographyFacade, 'getCountrySubdivisions').withArgs(mockCountry.id).and.returnValue(new BehaviorSubject(mockCountry.subdivisions));

fixture = TestBed.createComponent(WrapperComponent);
wrapper = fixture.componentInstance;
const formBuilder = new UntypedFormBuilder();
wrapper.formGroupValue = formBuilder.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
street: ['', Validators.required],
city: ['', Validators.required],
state: ['State', Validators.required],
postcode: ['', Validators.required],
telephone: ['', Validators.required],
});
wrapper.formGroupValue = TestBed.inject(AddressFormFactory).create({});

wrapper.submittedValue = false;

fixture.detectChanges();

addressForm = fixture.debugElement.query(By.css('demo-address-form')).componentInstance;
component = fixture.debugElement.query(By.css('demo-address-form')).componentInstance;
});

it('should create', () => {
expect(addressForm).toBeTruthy();
expect(component).toBeTruthy();
});

it('should be able to take formGroup as input', () => {
expect(addressForm.formGroup).toEqual(wrapper.formGroupValue);
expect(component.formGroup).toEqual(wrapper.formGroupValue);
});

it('should be able to take submitted as input', () => {
expect(addressForm.submitted).toEqual(wrapper.submittedValue);
expect(component.submitted).toEqual(wrapper.submittedValue);
});

describe('on [daff-input]', () => {
Expand All @@ -97,11 +117,11 @@ describe('AddressFormComponent', () => {
});

it('should set formControl', () => {
expect(input.ngControl.control).toEqual(addressForm.formGroup.controls['firstname']);
expect(input.ngControl.control).toEqual(component.formGroup.controls['firstname']);
});

it('should set formSubmitted', () => {
expect(input.formSubmitted).toEqual(addressForm.submitted);
expect(input.formSubmitted).toEqual(component.submitted);
});
});

Expand All @@ -113,12 +133,23 @@ describe('AddressFormComponent', () => {
select = fixture.debugElement.queryAll(By.css('[daff-native-select]'))[0].componentInstance;
});

it('should set formControl', () => {
expect(select.ngControl.control).toEqual(<AbstractControl> addressForm.formGroup.controls['state']);
it('should set formSubmitted', () => {
expect(select.formSubmitted).toEqual(component.submitted);
});
});

it('should set formSubmitted', () => {
expect(select.formSubmitted).toEqual(addressForm.submitted);
describe('when a country is selected', () => {
beforeEach(() => {
spyOn(geographyFacade, 'dispatch');
component.formGroup.controls.country.patchValue(mockCountry.id);
});

it('should load the country', () => {
expect(geographyFacade.dispatch).toHaveBeenCalledWith(new DaffCountryLoad(mockCountry.id));
});

it('should render a list of the subdivisions', () => {
expect(component.stateSelectValues$).toBeObservable(cold('a', { a: mockCountry.subdivisions }));
});
});
});
Original file line number Diff line number Diff line change
@@ -1,29 +1,59 @@
import {
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import {
Observable,
Subject,
of,
takeUntil,
} from 'rxjs';

import {
DaffCountry,
DaffSubdivision,
} from '@daffodil/geography';
import {
DaffCountryList,
DaffCountryLoad,
DaffGeographyFacade,
} from '@daffodil/geography/state';

interface RegionOption {
label: string;
value: any;
};
import { AddressFormGroup } from '../../models/address-form.type';

@Component({
selector: 'demo-address-form',
templateUrl: './address-form.component.html',
styleUrls: ['./address-form.component.scss'],
})
export class AddressFormComponent {
export class AddressFormComponent implements OnInit, OnDestroy {
private _destroyed$ = new Subject<boolean>();

@Input() formGroup: UntypedFormGroup;
@Input() formGroup: AddressFormGroup;
@Input() submitted: boolean;

constructor() { }
countrySelectValues$: Observable<DaffCountry[]>;
stateSelectValues$: Observable<DaffSubdivision[]> = of([]);

constructor(
private geographyFacade: DaffGeographyFacade,
) {}

ngOnInit(): void {
this.geographyFacade.dispatch(new DaffCountryList());

this.countrySelectValues$ = this.geographyFacade.countries$;
this.formGroup.controls.country.valueChanges.pipe(
takeUntil(this._destroyed$),
).subscribe((country) => {
this.geographyFacade.dispatch(new DaffCountryLoad(country));
this.stateSelectValues$ = this.geographyFacade.getCountrySubdivisions(country);
});
}

stateSelectValues: RegionOption[] = [
{ label:'State', value: '' },
{ label: 'Alabama', value: 'AL' },
{ label: 'Alaska', value: 'AK' },
];
ngOnDestroy(): void {
this._destroyed$.next(true);
}
}

0 comments on commit da882b1

Please sign in to comment.