Skip to content

Commit

Permalink
feat(design): pushed CVA implementation to a directive
Browse files Browse the repository at this point in the history
  • Loading branch information
Nolan-Arnold committed May 12, 2020
1 parent a4aee6c commit 4ca62c6
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 55 deletions.
93 changes: 93 additions & 0 deletions libs/design/src/atoms/form/radio/cva/radio-cva.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Component } from '@angular/core';
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { By } from '@angular/platform-browser';

import { DaffRadioComponent } from '../radio.component';
import { DaffRadioModule } from '../radio.module';



@Component({
template: `
<daff-radio name='test' value='testValue' [formControl]='radio'></daff-radio>
`
})
class RadioWrapperComponent {
radio = new FormControl()
}

describe('DaffRadioControlValueAccessorDirective', () => {

describe('with the directive', () => {
let radioWrapper: RadioWrapperComponent;

let component: DaffRadioComponent;

let fixture: ComponentFixture<RadioWrapperComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
RadioWrapperComponent,
],
imports: [
ReactiveFormsModule,
DaffRadioModule
]
})
.compileComponents();
}));
describe('the DaffRadioComponent', () => {

beforeEach(() => {

fixture = TestBed.createComponent(RadioWrapperComponent);
radioWrapper = fixture.componentInstance;
component = fixture.debugElement.query(By.css('daff-radio')).componentInstance;
fixture.detectChanges();
});
it('has the writeValue function for formControls', async () => {

expect(component.checked).toEqual(false);

radioWrapper.radio.setValue('testValue');

expect(component.checked).toEqual(true);
});
});

});
describe('without the directive', () => {
let radioWrapper: RadioWrapperComponent;

let component: DaffRadioComponent;

let fixture: ComponentFixture<RadioWrapperComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
RadioWrapperComponent,
DaffRadioComponent
],
imports: [
ReactiveFormsModule,
]
})
.compileComponents();
}));
describe('the DaffRadioComponent', () => {

it('throws an error without the directive to give the itself the CVA interface', async () => {
fixture = TestBed.createComponent(RadioWrapperComponent);
radioWrapper = fixture.componentInstance;
expect(() => {
component = fixture.debugElement.query(By.css('daff-radio')).componentInstance;
fixture.detectChanges();
}).toThrowError()
});

});
});
});
94 changes: 94 additions & 0 deletions libs/design/src/atoms/form/radio/cva/radio-cva.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Directive, Input, OnInit, Self, Optional } from '@angular/core';
import { NgControl, ControlValueAccessor } from '@angular/forms';
import { DaffRadioComponent } from '../radio.component';
import { DaffRadioRegistry } from '../registry/radio-registry';

/**
* ControlValueAccessor functionality for the DaffRadio
*/
@Directive({
// tslint:disable-next-line: directive-selector
selector: 'daff-radio[ngModel], daff-radio[formControl], daff-radio[formControlName]'
})
export class DaffRadioControlValueAccessorDirective implements OnInit, ControlValueAccessor {
_onChange: () => void;
_onTouched: () => void;

/**
* The value of the ControlValueAccessor
*/
@Input() value: any;

/**
* The name of the ControlValueAccessor
*/
@Input() name: string;

constructor(
@Optional() @Self() public _control: NgControl,
private _registry: DaffRadioRegistry,
private _radio: DaffRadioComponent
) {
if (this._control != null) {
this._control.valueAccessor = this;
}
}

ngOnInit(): void {
this.writeValue(this._control.value);
this._registry.add(this._control, this);

this._radio.selectionChange.subscribe(
value => value ? this._onChange() : null
);
}
/**
*
* writeValue function from the CVA interface
*/
writeValue(value: any): void {
console.log(value);
if (this.value === value) {
this._onChange();
this.fireSelect();
}
}

/**
* registerOnChange implemented from the CVA interface
*/
registerOnChange(fn: any): void {
this._onChange = () => {
fn(this.value);
this._registry.select(this);
};
}

/**
* registerOnTouch implemented from the CVA interface
*/
registerOnTouched(fn: any): void {
this._onTouched = fn;
}

/**
* sets the disabled state.
*/
setDisabledState?(isDisabled: boolean): void {
this._radio.disabled = isDisabled;
}

/**
calls select function for the radio
*/
fireSelect() {
this._radio.select();
}

/**
calls deselect function for the radio
*/
fireDeselect() {
this._radio.deselect();
}
}
47 changes: 42 additions & 5 deletions libs/design/src/atoms/form/radio/radio.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, OnInit, Input, ElementRef, Renderer2, HostBinding, ViewEncapsulation, ChangeDetectionStrategy, HostListener, forwardRef, ViewChild, Optional } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Component, OnInit, Input, HostBinding, ChangeDetectionStrategy, forwardRef, Optional, Output, EventEmitter } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { DaffRadioSetComponent } from '../radio-set/radio-set.component';

let radioUniqueId = 0;
Expand All @@ -18,7 +18,7 @@ let radioUniqueId = 0;
changeDetection: ChangeDetectionStrategy.OnPush
})

export class DaffRadioComponent implements ControlValueAccessor, OnInit {
export class DaffRadioComponent implements OnInit {
onChange: () => {};
onTouched: () => {};

Expand All @@ -30,10 +30,30 @@ export class DaffRadioComponent implements ControlValueAccessor, OnInit {
return this.disabled === true;
};

/**
* Output event of selection being changed
*/
@Output() selectionChange: EventEmitter<boolean> = new EventEmitter();


@Input() checked = false;
/**
* The value of the radio
*/
@Input() value: any;
/**
* The id of the radio. It is uniquely generated but can be overwritten by the user. Must be unique.
*/
@Input() id: string = 'daff-radio-' + radioUniqueId;
/**
* Name of the Radio
*/
@Input() name: string;
/**
* Used for aria-label. Default to name if user does not input a label.
*/
@Input() label = name;

disabled = false;
focused = false;

Expand All @@ -44,14 +64,31 @@ export class DaffRadioComponent implements ControlValueAccessor, OnInit {
this.name = this.radioset ? this.radioset.name : this.name
}



/**
* updates Focus styling
*/
onFocus() {
this.focused = true;
}
/**
* updates Blur styling
*/
onBlur() {
this.focused = false;
}
writeValue(value: any): void {
this.checked = value === this.value ? true : false;
/**
* toggeles checked attribute on
*/
select(): void {
this.checked = true;
}
/**
* toggeles checked attribute off
*/
deselect(): void {
this.checked = false;
}
registerOnChange(fn: any): void {
this.onChange = fn;
Expand Down
6 changes: 4 additions & 2 deletions libs/design/src/atoms/form/radio/radio.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<input type="radio"
[attr.checked]="checked ? '' : null"
[attr.id]="id" [attr.name]="name"
[attr.checked]="checked"
[attr.id]="id"
[attr.name]="name"
[attr.aria-label]="label"
[attr.value]="value"
[attr.disabled] = "disabled ? '' : null"
(change)="onChange()"
Expand Down
27 changes: 26 additions & 1 deletion libs/design/src/atoms/form/radio/radio.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,45 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DaffRadioComponent } from './radio.component';
import { DaffRadioSetComponent } from '../radio-set/radio-set.component';
import { DaffRadioControlValueAccessorDirective } from './cva/radio-cva.directive';
import { DaffRadioRegistry } from './registry/radio-registry';



@NgModule({
exports:[
DaffRadioComponent,
DaffRadioSetComponent
DaffRadioSetComponent,
DaffRadioControlValueAccessorDirective,

],
declarations: [
DaffRadioControlValueAccessorDirective,
DaffRadioComponent,
DaffRadioSetComponent
],
imports: [
CommonModule
],
providers:[
DaffRadioRegistry
]
})
export class DaffRadioModule { }
/**
* imports: [
CommonModule,
GolfImageModule
],
exports: [
GolfSwatchComponent,
GolfSwatchControlValueAccessorDirective
],
declarations: [
GolfSwatchComponent,
GolfSwatchControlValueAccessorDirective
],
providers: [
GolfSwatchRegistry
]
*/
17 changes: 17 additions & 0 deletions libs/design/src/atoms/form/radio/registry/radio-registry.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TestBed } from '@angular/core/testing';

import { DaffRadioRegistry } from './radio-registry';
import { DaffRadioModule } from '../radio.module';

describe('DaffRadioRegistry', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [
DaffRadioModule
]
}));

it('should be created', () => {
const service: DaffRadioRegistry = TestBed.get(DaffRadioRegistry);
expect(service).toBeTruthy();
});
});
Loading

0 comments on commit 4ca62c6

Please sign in to comment.