-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0b4c0e0
commit 1f94d93
Showing
6 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./util/component"; | ||
export * from "./util/dom"; | ||
export * from "./util/form"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./form/form.util"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
import { StarkFormControlState, StarkFormUtil } from "./form.util"; | ||
import { FormControl, FormGroup /*, Validators*/ } from "@angular/forms"; | ||
|
||
// import Spy = jasmine.Spy; | ||
|
||
const _startCase: Function = require("lodash/startCase"); | ||
|
||
describe("Util: FormUtil", () => { | ||
const formItemStates: string[] = ["untouched", "touched", "pristine", "dirty"]; | ||
|
||
let mockFormGroup: FormGroup; | ||
let mockFormControls: FormControl[]; | ||
|
||
function getMockFormControl(name: string): FormControl { | ||
return jasmine.createSpyObj<FormControl>(name, [ | ||
"value", | ||
"setValue", | ||
"markAsUntouched", | ||
"markAsTouched", | ||
"markAsPristine", | ||
"markAsDirty" | ||
]); | ||
} | ||
|
||
function assertFormControl(formItem: FormControl, newState?: string): void { | ||
if (newState) { | ||
const newStateSetter: string = "markAs" + _startCase(newState); | ||
expect(formItem[newStateSetter]).toHaveBeenCalledTimes(1); | ||
} | ||
|
||
// check that the setters for other states where not called | ||
const nonUsedStates: string[] = formItemStates.filter((state: string) => state !== newState); | ||
for (const nonUsedState of nonUsedStates) { | ||
const nonUsedStateSetter: string = "markAs" + _startCase(nonUsedState); | ||
expect(formItem[nonUsedStateSetter]).not.toHaveBeenCalled(); | ||
} | ||
} | ||
|
||
beforeEach(() => { | ||
mockFormControls = [getMockFormControl("item1"), getMockFormControl("item2")]; | ||
|
||
mockFormGroup = <any>{ | ||
controls: { | ||
formControl0: mockFormControls[0], | ||
formControl1: mockFormControls[1] | ||
} | ||
}; | ||
}); | ||
|
||
describe("setFormChildControlsState", () => { | ||
it("should call markAsTouched() on every form control if state to be set is 'touched'", () => { | ||
const stateToBeSet: StarkFormControlState = "touched"; | ||
|
||
StarkFormUtil.setFormChildControlsState(mockFormGroup, [stateToBeSet]); | ||
|
||
for (const formControl of mockFormControls) { | ||
assertFormControl(formControl, stateToBeSet); | ||
} | ||
}); | ||
|
||
it("should call markAsUntouched() on every form control if state to be set is 'untouched'", () => { | ||
const stateToBeSet: StarkFormControlState = "untouched"; | ||
|
||
StarkFormUtil.setFormChildControlsState(mockFormGroup, [stateToBeSet]); | ||
|
||
for (const formControl of mockFormControls) { | ||
assertFormControl(formControl, stateToBeSet); | ||
} | ||
}); | ||
|
||
it("should call markAsPristine() on every form control if state to be set is 'pristine'", () => { | ||
const stateToBeSet: StarkFormControlState = "pristine"; | ||
|
||
StarkFormUtil.setFormChildControlsState(mockFormGroup, [stateToBeSet]); | ||
|
||
for (const formControl of mockFormControls) { | ||
assertFormControl(formControl, stateToBeSet); | ||
} | ||
}); | ||
|
||
it("should call markAsDirty() on every form control if state to be set is 'dirty'", () => { | ||
const stateToBeSet: StarkFormControlState = "dirty"; | ||
|
||
StarkFormUtil.setFormChildControlsState(mockFormGroup, [stateToBeSet]); | ||
|
||
for (const formControl of mockFormControls) { | ||
assertFormControl(formControl, stateToBeSet); | ||
} | ||
}); | ||
|
||
it("should NOT call any method on any form control if state to be set is unknown", () => { | ||
const stateToBeSet: any = "unknown state"; | ||
|
||
StarkFormUtil.setFormChildControlsState(mockFormGroup, [stateToBeSet]); | ||
|
||
for (const formControl of mockFormControls) { | ||
assertFormControl(formControl); | ||
} | ||
}); | ||
}); | ||
|
||
describe("setFormControlState", () => { | ||
let formItem: FormControl; | ||
|
||
beforeEach(() => { | ||
formItem = getMockFormControl("dummy item"); | ||
}); | ||
|
||
it("should call markAsTouched() on the given form item if state to be set is 'touched'", () => { | ||
const stateToBeSet: StarkFormControlState = "touched"; | ||
|
||
StarkFormUtil.setFormControlState(formItem, [stateToBeSet]); | ||
|
||
assertFormControl(formItem, stateToBeSet); | ||
}); | ||
|
||
it("should call markAsUntouched() on the given form item if state to be set is 'untouched'", () => { | ||
const stateToBeSet: StarkFormControlState = "untouched"; | ||
|
||
StarkFormUtil.setFormControlState(formItem, [stateToBeSet]); | ||
|
||
assertFormControl(formItem, stateToBeSet); | ||
}); | ||
|
||
it("should call markAsPristine() on the given form item if state to be set is 'pristine'", () => { | ||
const stateToBeSet: StarkFormControlState = "pristine"; | ||
|
||
StarkFormUtil.setFormControlState(formItem, [stateToBeSet]); | ||
|
||
assertFormControl(formItem, stateToBeSet); | ||
}); | ||
|
||
it("should call markAsDirty() on the given form item if state to be set is 'dirty'", () => { | ||
const stateToBeSet: StarkFormControlState = "dirty"; | ||
|
||
StarkFormUtil.setFormControlState(formItem, [stateToBeSet]); | ||
|
||
assertFormControl(formItem, stateToBeSet); | ||
}); | ||
|
||
it("should NOT call any method on the given form item if state to be set is unknown", () => { | ||
const stateToBeSet: any = "unknown state"; | ||
|
||
StarkFormUtil.setFormControlState(formItem, [stateToBeSet]); | ||
|
||
assertFormControl(formItem); | ||
}); | ||
}); | ||
|
||
describe("isFormGroupValid", () => { | ||
it("should set form state to 'pristine' and return TRUE if the form is valid", () => { | ||
spyOn(StarkFormUtil, "setFormChildControlsState"); | ||
|
||
const result: boolean = StarkFormUtil.isFormGroupValid(mockFormGroup); | ||
|
||
expect(result).toBe(true); | ||
expect(StarkFormUtil.setFormChildControlsState).toHaveBeenCalledTimes(1); | ||
expect(StarkFormUtil.setFormChildControlsState).toHaveBeenCalledWith(mockFormGroup, ["pristine"]); | ||
}); | ||
|
||
it("should set form state to 'touched' and return FALSE if the form is NOT valid", () => { | ||
mockFormGroup = <any>{ | ||
controls: { | ||
formControl0: mockFormControls[0], | ||
formControl1: mockFormControls[1] | ||
}, | ||
invalid: true | ||
}; | ||
|
||
spyOn(StarkFormUtil, "setFormChildControlsState"); | ||
|
||
const result: boolean = StarkFormUtil.isFormGroupValid(mockFormGroup); | ||
|
||
expect(result).toBe(false); | ||
expect(StarkFormUtil.setFormChildControlsState).toHaveBeenCalledTimes(1); | ||
expect(StarkFormUtil.setFormChildControlsState).toHaveBeenCalledWith(mockFormGroup, ["touched"]); | ||
}); | ||
}); | ||
|
||
describe("isFormGroup", () => { | ||
it("should return TRUE if the form is an instance of an IFormController", () => { | ||
const result: boolean = StarkFormUtil.isFormGroup(mockFormGroup); | ||
|
||
expect(result).toBe(true); | ||
}); | ||
|
||
it("should return FALSE if the form is NOT an instance of an IFormController", () => { | ||
const result: boolean = StarkFormUtil.isFormGroup("this is not a form"); | ||
|
||
expect(result).toBe(false); | ||
}); | ||
}); | ||
|
||
describe("isFormControl", () => { | ||
it("should return TRUE if the form item is an instance of an FormControl", () => { | ||
const mockFormItem: FormControl = getMockFormControl("dummy item"); | ||
|
||
const result: boolean = StarkFormUtil.isFormControl(mockFormItem); | ||
|
||
expect(result).toBe(true); | ||
}); | ||
|
||
it("should return FALSE if the form item is NOT an instance of an FormControl", () => { | ||
const result: boolean = StarkFormUtil.isFormControl("this is not a form item"); | ||
|
||
expect(result).toBe(false); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { AbstractControl, FormControl, FormGroup } from "@angular/forms"; | ||
|
||
export type StarkFormControlState = "untouched" | "touched" | "pristine" | "dirty"; | ||
export type StarkFormState = "untouched" | "pristine" | "submitted" | "dirty"; | ||
|
||
export class StarkFormUtil { | ||
/** | ||
* Set all the fields of the form group to the given state(s) | ||
* @param formGroup - Angular form group object | ||
* @param statesToBeSet - States to be set to the different controls in the form | ||
*/ | ||
public static setFormChildControlsState(formGroup: FormGroup, statesToBeSet: StarkFormControlState[]): void { | ||
// Verifying it is indeed an Angular FormController | ||
if (StarkFormUtil.isFormGroup(formGroup)) { | ||
for (const key of Object.keys(formGroup.controls)) { | ||
// filtering just the ngModel child objects of the form | ||
const formControl: AbstractControl = formGroup.controls[key]; | ||
if (StarkFormUtil.isFormControl(formControl)) { | ||
this.setFormControlState(formControl, statesToBeSet); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Set the specified form control to the given state(s) | ||
* @param formControl - Angular form control object contained in an Angular form group | ||
* @param statesToBeSet - States to be set to the form control | ||
*/ | ||
public static setFormControlState(formControl: FormControl, statesToBeSet: StarkFormControlState[]): void { | ||
for (const formControlState of statesToBeSet) { | ||
switch (formControlState) { | ||
case "untouched": | ||
// control has not lost focus yet | ||
formControl.markAsUntouched(); | ||
break; | ||
case "touched": | ||
// control has lost focus. | ||
formControl.markAsTouched(); | ||
break; | ||
case "pristine": | ||
// user has not interacted with the form yet. | ||
formControl.markAsPristine(); | ||
break; | ||
case "dirty": | ||
// user has already interacted with the form | ||
formControl.markAsDirty(); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Set the form to the given state(s). Possible states: "untouched", "pristine", "dirty", "submitted" | ||
* @param formGroup - Angular form group object | ||
* @param statesToBeSet - States to be set to the form | ||
*/ | ||
public static setFormGroupState(formGroup: FormGroup, statesToBeSet: StarkFormState[]): void { | ||
// Verifying it is indeed an Angular FormController | ||
if (StarkFormUtil.isFormGroup(formGroup)) { | ||
for (const formControlState of statesToBeSet) { | ||
switch (formControlState) { | ||
case "untouched": | ||
// control has not lost focus yet | ||
formGroup.markAsUntouched(); | ||
break; | ||
case "pristine": | ||
// user has not interacted with the form yet. | ||
formGroup.markAsPristine(); | ||
break; | ||
case "dirty": | ||
// user has already interacted with the form | ||
formGroup.markAsDirty(); | ||
break; | ||
case "submitted": | ||
// user has already interacted with the form | ||
// FIXME Find alternative to $setSubmitted with ReactiveForm if needed | ||
// formGroup.$setSubmitted(); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Check whether the form group is valid (all fields are valid). | ||
* If valid, all the form fields will be set to their "pristine" state, otherwise to their "touched" state | ||
* (calling setFormControlState function). | ||
* @param formGroup - Angular form group object | ||
*/ | ||
public static isFormGroupValid(formGroup: FormGroup): boolean { | ||
if (formGroup.invalid) { | ||
StarkFormUtil.setFormChildControlsState(formGroup, ["touched"]); | ||
return false; | ||
} else { | ||
StarkFormUtil.setFormChildControlsState(formGroup, ["pristine"]); | ||
return true; | ||
} | ||
} | ||
|
||
// type guard | ||
/** | ||
* Check that the given form is an actual instance of an Angular form. | ||
* @param formGroup - Angular form object | ||
*/ | ||
public static isFormGroup(formGroup: any): formGroup is FormGroup { | ||
return typeof formGroup !== "undefined" && formGroup.hasOwnProperty("controls"); | ||
} | ||
|
||
// type guard | ||
/** | ||
* Check that the given form field object is an actual instance of an Angular form field. | ||
* @param formControl - Angular form field object | ||
*/ | ||
public static isFormControl(formControl: any): formControl is FormControl { | ||
return typeof formControl === "object" && typeof formControl["setValue"] !== "undefined"; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
showcase/src/assets/examples/dropdown/reactive-form-dropdown.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<stark-dropdown | ||
dropdownId="disabledDropdown" | ||
[options]="[1, 2, 3, 4, 5, 6, 7]" | ||
placeholder="Sorry, it's disabled" | ||
[isDisabled]="true" | ||
multiSelect | ||
> | ||
</stark-dropdown> |
7 changes: 7 additions & 0 deletions
7
showcase/src/assets/examples/dropdown/reactive-form-dropdown.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Component } from "@angular/core"; | ||
|
||
@Component({ | ||
selector: "demo-dropdown", | ||
templateUrl: "./demo-dropdown.component.html" | ||
}) | ||
export class DemoDropdownComponent {} |