Skip to content

Commit

Permalink
feat: disabled prefilled fields
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKless committed Mar 24, 2021
1 parent 3320b1e commit 0245f2e
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import { instance, mock } from 'ts-mockito';
import { AccountFacade } from 'ish-core/facades/account.facade';
import { extractKeys } from 'ish-shared/formly/dev/testing/formly-testing-utils';

import { RegistrationConfigType } from '../registration-page.component';
import {
RegistrationConfigType,
RegistrationFormConfigurationService,
} from './registration-form-configuration.service';

import { RegistrationConfigurationService } from './registration-configuration.service';

describe('Registration Configuration Service', () => {
let registrationConfigurationService: RegistrationConfigurationService;
describe('Registration Form Configuration Service', () => {
let registrationConfigurationService: RegistrationFormConfigurationService;
let accountFacade: AccountFacade;

beforeEach(() => {
Expand All @@ -23,17 +24,17 @@ describe('Registration Configuration Service', () => {
{ provide: Router, useFactory: () => instance(mock(Router)) },
{ provide: Store, useFactory: () => instance(mock(Store)) },
{ provide: AccountFacade, useFactory: () => instance(accountFacade) },
RegistrationConfigurationService,
RegistrationFormConfigurationService,
],
});
registrationConfigurationService = TestBed.inject(RegistrationConfigurationService);
registrationConfigurationService = TestBed.inject(RegistrationFormConfigurationService);
});

describe('with sso', () => {
const registrationConfig: RegistrationConfigType = { businessCustomer: true, sso: true, userId: 'uid' };

it('should return right fields when calling getRegistrationConfig', () => {
const fieldConfig = registrationConfigurationService.getRegistrationConfiguration(registrationConfig);
const fieldConfig = registrationConfigurationService.getRegistrationFormConfiguration(registrationConfig);
expect(extractKeys(fieldConfig)).toMatchInlineSnapshot(`
Array [
Array [
Expand All @@ -59,7 +60,7 @@ describe('Registration Configuration Service', () => {
const registrationConfig: RegistrationConfigType = { businessCustomer: true, sso: false };

it('should return the right fields when calling getRegistrationConfig', () => {
const fieldConfig = registrationConfigurationService.getRegistrationConfiguration(registrationConfig);
const fieldConfig = registrationConfigurationService.getRegistrationFormConfiguration(registrationConfig);
expect(extractKeys(fieldConfig)).toMatchInlineSnapshot(`
Array [
Array [
Expand Down Expand Up @@ -89,7 +90,7 @@ describe('Registration Configuration Service', () => {
const registrationConfig: RegistrationConfigType = { businessCustomer: false, sso: false };

it('should return the right fields when calling getRegistrationConfig', () => {
const fieldConfig = registrationConfigurationService.getRegistrationConfiguration(registrationConfig);
const fieldConfig = registrationConfigurationService.getRegistrationFormConfiguration(registrationConfig);
expect(extractKeys(fieldConfig)).toMatchInlineSnapshot(`
Array [
Array [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// tslint:disable: project-structure
// tslint:disable-next-line: ish-ordered-imports
import { Injectable } from '@angular/core';
import { FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core';
import { UUID } from 'angular2-uuid';

import { AccountFacade } from 'ish-core/facades/account.facade';
Expand All @@ -14,13 +12,19 @@ import { Customer, CustomerRegistrationType } from 'ish-core/models/customer/cus
import { User } from 'ish-core/models/user/user.model';
import { cancelRegistration, setRegistrationInfo } from 'ish-core/store/customer/sso-registration';
import { SpecialValidators } from 'ish-shared/forms/validators/special-validators';
import { RegistrationConfiguration, RegistrationConfigType } from '../registration-page.component';

@Injectable()
export class RegistrationConfigurationService implements RegistrationConfiguration {
export interface RegistrationConfigType {
businessCustomer?: boolean;
sso?: boolean;
userId?: string;
}

@Injectable({ providedIn: 'root' })
export class RegistrationFormConfigurationService {
// tslint:disable: no-intelligence-in-artifacts
constructor(private accountFacade: AccountFacade, private store: Store, private router: Router) {}

getRegistrationConfiguration(registrationConfig: RegistrationConfigType) {
getRegistrationFormConfiguration(registrationConfig: RegistrationConfigType) {
return [
{
type: 'ish-registration-heading-field',
Expand Down Expand Up @@ -76,8 +80,19 @@ export class RegistrationConfigurationService implements RegistrationConfigurati
];
}

submitRegistration(form: FormGroup, registrationConfig: RegistrationConfigType) {
const formValue = form.value;
getRegistrationFormConfigurationOptions(
registrationConfig: RegistrationConfigType,
model: Record<string, unknown>
): FormlyFormOptions {
if (registrationConfig.sso) {
return { formState: { disabled: Object.keys(model) } };
} else {
return {};
}
}

submitRegistrationForm(form: FormGroup, registrationConfig: RegistrationConfigType, model?: Record<string, unknown>) {
const formValue = { ...form.value, ...model };

const address: Address = {
firstName: formValue.firstName,
Expand Down Expand Up @@ -134,7 +149,7 @@ export class RegistrationConfigurationService implements RegistrationConfigurati
}
}

cancelRegistration(config: RegistrationConfigType): void {
cancelRegistrationForm(config: RegistrationConfigType): void {
config.sso ? this.store.dispatch(cancelRegistration()) : this.router.navigate(['/home']);
}

Expand Down
24 changes: 24 additions & 0 deletions src/app/pages/registration/formly/disable-prefilled.extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FormlyExtension, FormlyFieldConfig } from '@ngx-formly/core';

/**
* This extension disables all fields that have keys that match those
* specified in the formstate.disabled property
*/
type FieldConfigWithDisabled = Omit<FormlyFieldConfig, 'options'> & { options: { formState: { disabled: string[] } } };

export const disablePrefilledExtension: FormlyExtension = {
prePopulate: field => {
if (hasDisabled(field) && field.key) {
field.templateOptions = { ...field.templateOptions };
field.templateOptions.disabled = field.options.formState.disabled.includes(field.key as string);
}
},
};

function hasDisabled(field: FormlyFieldConfig): field is FieldConfigWithDisabled {
return (
Array.isArray(field.options?.formState?.disabled) &&
// tslint:disable-next-line: no-any
(field.options.formState.disabled as any[]).reduce((acc, val) => acc && typeof val === 'string', true)
);
}
5 changes: 2 additions & 3 deletions src/app/pages/registration/registration-page.component.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<ish-error-message [error]="error$ | async"></ish-error-message>
<div data-testing-id="account-create-full-page">
<form [formGroup]="form" (ngSubmit)="onCreate()">
<formly-form [model]="model" [form]="form" [fields]="fields$ | async"></formly-form>

<formly-form [model]="model" [form]="form" [options]="options" [fields]="fields$ | async"></formly-form>
<div class="row">
<div class="col-md-10 col-lg-8 col-xl-6">
<div class="row form-group">
<div class="offset-md-4 col-md-8">
<input
type="submit"
[disabled]="formDisabled"
[disabled]="submitDisabled"
class="btn btn-primary"
value="{{ 'account.create.button.label' | translate }}"
/>
Expand Down
15 changes: 8 additions & 7 deletions src/app/pages/registration/registration-page.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ import { anyString, anything, instance, mock, verify, when } from 'ts-mockito';

import { AccountFacade } from 'ish-core/facades/account.facade';
import { FeatureToggleService } from 'ish-core/feature-toggle.module';
import { RegistrationFormConfigurationService } from 'ish-core/services/registration-form-configuration/registration-form-configuration.service';
import { ErrorMessageComponent } from 'ish-shared/components/common/error-message/error-message.component';
import { FormlyTestingModule } from 'ish-shared/formly/dev/testing/formly-testing.module';

import { RegistrationConfigurationService } from './registration-configuration/registration-configuration.service';
import { REGISTRATION_CONFIGURATION, RegistrationPageComponent } from './registration-page.component';
import { RegistrationPageComponent } from './registration-page.component';

// tslint:disable:no-intelligence-in-artifacts
describe('Registration Page Component', () => {
let fixture: ComponentFixture<RegistrationPageComponent>;
let component: RegistrationPageComponent;
let element: HTMLElement;
let configService: RegistrationConfigurationService;
let configService: RegistrationFormConfigurationService;
let featureToggleService: FeatureToggleService;
let activatedRoute: ActivatedRoute;
let accountFacade: AccountFacade;
Expand All @@ -28,7 +29,7 @@ describe('Registration Page Component', () => {
class DummyComponent {}

beforeEach(async () => {
configService = mock(RegistrationConfigurationService);
configService = mock(RegistrationFormConfigurationService);
featureToggleService = mock(FeatureToggleService);
activatedRoute = mock(ActivatedRoute);
accountFacade = mock(AccountFacade);
Expand All @@ -43,12 +44,12 @@ describe('Registration Page Component', () => {
{ provide: AccountFacade, useFactory: () => instance(accountFacade) },
{ provide: FeatureToggleService, useFactory: () => instance(featureToggleService) },
{ provide: ActivatedRoute, useFactory: () => instance(activatedRoute) },
{ provide: REGISTRATION_CONFIGURATION, useFactory: () => instance(configService) },
{ provide: RegistrationFormConfigurationService, useFactory: () => instance(configService) },
],
}).compileComponents();

when(featureToggleService.enabled(anyString())).thenReturn(false);
when(configService.getRegistrationConfiguration(anything())).thenReturn([
when(configService.getRegistrationFormConfiguration(anything())).thenReturn([
{
key: 'test',
type: 'ish-text-input-field',
Expand Down Expand Up @@ -117,6 +118,6 @@ describe('Registration Page Component', () => {

tick(500);

verify(configService.cancelRegistration(anything())).once();
verify(configService.cancelRegistrationForm(anything())).once();
}));
});
43 changes: 21 additions & 22 deletions src/app/pages/registration/registration-page.component.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core';
import { Observable, merge } from 'rxjs';
import { map, tap } from 'rxjs/operators';

import { AccountFacade } from 'ish-core/facades/account.facade';
import { FeatureToggleService } from 'ish-core/feature-toggle.module';
import { HttpError } from 'ish-core/models/http-error/http-error.model';
import {
RegistrationConfigType,
RegistrationFormConfigurationService,
} from 'ish-core/services/registration-form-configuration/registration-form-configuration.service';

export const REGISTRATION_CONFIGURATION = new InjectionToken<RegistrationConfiguration>('registrationConfiguration');

export interface RegistrationConfiguration {
getRegistrationConfiguration(registrationConfig: RegistrationConfigType): FormlyFieldConfig[];
submitRegistration(form: FormGroup, registrationConfig: RegistrationConfigType): void;
cancelRegistration(config: RegistrationConfigType): void;
}

export interface RegistrationConfigType {
businessCustomer?: boolean;
sso?: boolean;
userId?: string;
}

// tslint:disable:no-intelligence-in-artifacts
/**
* The Registration Page Container renders the customer registration form using the {@link RegistrationFormComponent}
*
Expand All @@ -38,15 +29,16 @@ export class RegistrationPageComponent implements OnInit {
private accountFacade: AccountFacade,
private route: ActivatedRoute,
private featureToggle: FeatureToggleService,
@Inject(REGISTRATION_CONFIGURATION) private registrationConfigurationProvider: RegistrationConfiguration
private registrationFormConfiguration: RegistrationFormConfigurationService
) {}

submitted = false;

registrationConfig: RegistrationConfigType;

fields$: Observable<FormlyFieldConfig[]>;
model: { [key: string]: unknown };
model: Record<string, unknown>;
options: FormlyFormOptions;
form = new FormGroup({});

ngOnInit() {
Expand All @@ -59,24 +51,31 @@ export class RegistrationPageComponent implements OnInit {
businessCustomer: this.featureToggle.enabled('businessCustomerRegistration'),
})),
tap(config => (this.registrationConfig = config)),
map(config => this.registrationConfigurationProvider.getRegistrationConfiguration(config))
tap(
config =>
(this.options = this.registrationFormConfiguration.getRegistrationFormConfigurationOptions(
config,
this.model
))
),
map(config => this.registrationFormConfiguration.getRegistrationFormConfiguration(config))
);
}

cancelForm() {
this.registrationConfigurationProvider.cancelRegistration(this.registrationConfig);
this.registrationFormConfiguration.cancelRegistrationForm(this.registrationConfig);
}

onCreate() {
if (this.form.invalid) {
this.submitted = true;
return;
}
this.registrationConfigurationProvider.submitRegistration(this.form, this.registrationConfig);
this.registrationFormConfiguration.submitRegistrationForm(this.form, this.registrationConfig, this.model);
}

/** return boolean to set submit button enabled/disabled */
get formDisabled(): boolean {
get submitDisabled(): boolean {
return this.form.invalid && this.submitted;
}

Expand Down
6 changes: 3 additions & 3 deletions src/app/pages/registration/registration-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { ConfigOption, FormlyModule } from '@ngx-formly/core';

import { SharedModule } from 'ish-shared/shared.module';

import { disablePrefilledExtension } from './formly/disable-prefilled.extension';
import { RegistrationAddressFieldComponent } from './formly/registration-address-field/registration-address-field.component';
import { RegistrationHeadingFieldComponent } from './formly/registration-heading-field/registration-heading-field.component';
import { RegistrationTacFieldComponent } from './formly/registration-tac-field/registration-tac-field.component';
import { RegistrationConfigurationService } from './registration-configuration/registration-configuration.service';
import { REGISTRATION_CONFIGURATION, RegistrationPageComponent } from './registration-page.component';
import { RegistrationPageComponent } from './registration-page.component';

const registrationPageRoutes: Routes = [{ path: '', component: RegistrationPageComponent }];

Expand All @@ -21,6 +21,7 @@ const registrationFormlyConfig: ConfigOption = {
{ name: 'ish-registration-heading-field', component: RegistrationHeadingFieldComponent },
{ name: 'ish-registration-tac-field', component: RegistrationTacFieldComponent },
],
extensions: [{ name: 'disable-prefilled', extension: disablePrefilledExtension }],
};

@NgModule({
Expand All @@ -35,6 +36,5 @@ const registrationFormlyConfig: ConfigOption = {
RegistrationPageComponent,
RegistrationTacFieldComponent,
],
providers: [{ provide: REGISTRATION_CONFIGURATION, useClass: RegistrationConfigurationService }],
})
export class RegistrationPageModule {}

1 comment on commit 0245f2e

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Azure Demo Servers are available:

Please sign in to comment.