diff --git a/projects/ng-aquila/src/button/anchor-button.component.spec.ts b/projects/ng-aquila/src/button/anchor-button.component.spec.ts index ffbb3ad89..ef1fe0c9a 100644 --- a/projects/ng-aquila/src/button/anchor-button.component.spec.ts +++ b/projects/ng-aquila/src/button/anchor-button.component.spec.ts @@ -1,6 +1,5 @@ import { Component, Directive, Type, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; -import { NxIconModule } from '@aposin/ng-aquila/icon'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { NxButtonModule } from '.'; import { NxAnchorButtonComponent } from './anchor-button.component'; @@ -11,48 +10,55 @@ abstract class AnchorButtonTest { } @Component({ - template: `Hello Anchor Button`, + template: `Hello Anchor Button`, }) -class AnchorButton extends AnchorButtonTest {} +class TestInstance extends AnchorButtonTest { + clickBindingSpy = jasmine.createSpy('clickSpy'); +} describe('NxAnchorButtonComponent', () => { - let fixture: ComponentFixture; - let testInstance: AnchorButton; - let buttonInstance: NxAnchorButtonComponent; - let buttonNativeElement: HTMLAnchorElement; - beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [AnchorButton], - imports: [NxIconModule, NxButtonModule], + declarations: [TestInstance], + imports: [NxButtonModule], }).compileComponents(); })); - function createTestComponent(component: Type) { + let fixture: ComponentFixture; + let testInstance: TestInstance; + let buttonInstance: NxAnchorButtonComponent; + let buttonNativeElement: HTMLAnchorElement; + + function createTestComponent(component: Type) { fixture = TestBed.createComponent(component); fixture.detectChanges(); testInstance = fixture.componentInstance; - buttonInstance = testInstance.buttonInstance; + buttonInstance = fixture.componentInstance.buttonInstance; buttonNativeElement = fixture.nativeElement.querySelector('a') as HTMLAnchorElement; } it('creates the button', waitForAsync(() => { - createTestComponent(AnchorButton); + createTestComponent(TestInstance); expect(buttonInstance).toBeTruthy(); })); - it('prevents default when the anchor button is disabled', fakeAsync(() => { - createTestComponent(AnchorButton); - const clickSpy = jasmine.createSpy('clickSpy'); - buttonNativeElement.addEventListener('click', clickSpy); + it('has correct base class', waitForAsync(() => { + createTestComponent(TestInstance); + expect(buttonNativeElement).toHaveClass('nx-button'); + })); + + it('disabled state prevents click binding on host element from firing', () => { + createTestComponent(TestInstance); + fixture.detectChanges(); buttonInstance.disabled = true; buttonNativeElement.click(); - expect(clickSpy).toHaveBeenCalledTimes(0); - })); + + expect(testInstance.clickBindingSpy).not.toHaveBeenCalled(); + }); describe('a11y', () => { it('has no accessibility violations', async () => { - createTestComponent(AnchorButton); + createTestComponent(TestInstance); await expectAsync(fixture.nativeElement).toBeAccessible(); }); }); diff --git a/projects/ng-aquila/src/button/anchor-button.component.ts b/projects/ng-aquila/src/button/anchor-button.component.ts index a5f025b88..21654cfd4 100644 --- a/projects/ng-aquila/src/button/anchor-button.component.ts +++ b/projects/ng-aquila/src/button/anchor-button.component.ts @@ -1,28 +1,16 @@ -import { FocusMonitor } from '@angular/cdk/a11y'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { NxTriggerButton } from '@aposin/ng-aquila/overlay'; - -import { NxButtonComponent } from './button.component'; +import { NxAnchorButtonBase } from './button-base'; @Component({ templateUrl: './button.html', styleUrls: ['button.scss'], selector: 'a[nxButton]', - inputs: ['classNames:nxButton'], changeDetection: ChangeDetectionStrategy.OnPush, + inputs: ['classNames:nxButton'], providers: [{ provide: NxTriggerButton, useExisting: NxAnchorButtonComponent }], + host: { + class: 'nx-button', + }, }) -export class NxAnchorButtonComponent extends NxButtonComponent { - /** @docs-private */ - @HostListener('click', ['$event']) - _checkEventsDisabled(event: Event) { - if (this.disabled) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - } - - constructor(_cdr: ChangeDetectorRef, elementRef: ElementRef, focusMonitor: FocusMonitor) { - super(_cdr, elementRef, focusMonitor); - } -} +export class NxAnchorButtonComponent extends NxAnchorButtonBase {} diff --git a/projects/ng-aquila/src/button/anchor-icon-button.component.spec.ts b/projects/ng-aquila/src/button/anchor-icon-button.component.spec.ts index a25f70d68..5551c9d41 100644 --- a/projects/ng-aquila/src/button/anchor-icon-button.component.spec.ts +++ b/projects/ng-aquila/src/button/anchor-icon-button.component.spec.ts @@ -7,53 +7,60 @@ import { NxAnchorButtonComponent } from './anchor-button.component'; import { NxAnchorIconButtonComponent } from './anchor-icon-button.component'; @Directive() -abstract class AnchorIconButtonTest { - @ViewChild('button') buttonInstance!: NxAnchorIconButtonComponent; +abstract class AnchorButtonTest { + @ViewChild('button') buttonInstance!: NxAnchorButtonComponent; } @Component({ - template: `Hello Anchor Button`, + template: ``, }) -class AnchorButton extends AnchorIconButtonTest {} +class TestInstance extends AnchorButtonTest { + clickBindingSpy = jasmine.createSpy('clickSpy'); +} describe('NxAnchorIconButtonComponent', () => { - let fixture: ComponentFixture; - let testInstance: AnchorButton; - let buttonInstance: NxAnchorButtonComponent; - let buttonNativeElement: HTMLAnchorElement; - beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [AnchorButton], + declarations: [TestInstance], imports: [NxIconModule, NxButtonModule], }).compileComponents(); })); - function createTestComponent(component: Type) { + let fixture: ComponentFixture; + let testInstance: TestInstance; + let buttonInstance: NxAnchorButtonComponent; + let buttonNativeElement: HTMLAnchorElement; + + function createTestComponent(component: Type) { fixture = TestBed.createComponent(component); fixture.detectChanges(); testInstance = fixture.componentInstance; - buttonInstance = testInstance.buttonInstance; + buttonInstance = fixture.componentInstance.buttonInstance; buttonNativeElement = fixture.nativeElement.querySelector('a') as HTMLAnchorElement; } it('creates the button', waitForAsync(() => { - createTestComponent(AnchorButton); + createTestComponent(TestInstance); expect(buttonInstance).toBeTruthy(); })); - it('prevents default when the anchor button is disabled', fakeAsync(() => { - createTestComponent(AnchorButton); - const clickSpy = jasmine.createSpy('clickSpy'); - buttonNativeElement.addEventListener('click', clickSpy); + it('has correct base class', waitForAsync(() => { + createTestComponent(TestInstance); + expect(buttonNativeElement).toHaveClass('nx-icon-button'); + })); + + it('disabled state prevents click binding on host element from firing', () => { + createTestComponent(TestInstance); + fixture.detectChanges(); buttonInstance.disabled = true; buttonNativeElement.click(); - expect(clickSpy).toHaveBeenCalledTimes(0); - })); + + expect(testInstance.clickBindingSpy).not.toHaveBeenCalled(); + }); describe('a11y', () => { it('has no accessibility violations', async () => { - createTestComponent(AnchorButton); + createTestComponent(TestInstance); await expectAsync(fixture.nativeElement).toBeAccessible(); }); }); diff --git a/projects/ng-aquila/src/button/anchor-icon-button.component.ts b/projects/ng-aquila/src/button/anchor-icon-button.component.ts index c58078118..c1242640e 100644 --- a/projects/ng-aquila/src/button/anchor-icon-button.component.ts +++ b/projects/ng-aquila/src/button/anchor-icon-button.component.ts @@ -1,28 +1,16 @@ -import { FocusMonitor } from '@angular/cdk/a11y'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { NxTriggerButton } from '@aposin/ng-aquila/overlay'; - -import { NxIconButtonComponent } from './icon-button.component'; +import { NxAnchorButtonBase } from './button-base'; @Component({ templateUrl: './button.html', styleUrls: ['button.scss'], selector: 'a[nxIconButton]', - inputs: ['classNames:nxIconButton'], changeDetection: ChangeDetectionStrategy.OnPush, + inputs: ['classNames:nxIconButton'], providers: [{ provide: NxTriggerButton, useExisting: NxAnchorIconButtonComponent }], + host: { + class: 'nx-icon-button', + }, }) -export class NxAnchorIconButtonComponent extends NxIconButtonComponent { - /** @docs-private */ - @HostListener('click', ['$event']) - _checkEventsDisabled(event: Event) { - if (this.disabled) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - } - - constructor(_cdr: ChangeDetectorRef, elementRef: ElementRef, focusMonitor: FocusMonitor) { - super(_cdr, elementRef, focusMonitor); - } -} +export class NxAnchorIconButtonComponent extends NxAnchorButtonBase {} diff --git a/projects/ng-aquila/src/button/anchor-plain-button.component.spec.ts b/projects/ng-aquila/src/button/anchor-plain-button.component.spec.ts index e71c3a88a..9b4ebf90b 100644 --- a/projects/ng-aquila/src/button/anchor-plain-button.component.spec.ts +++ b/projects/ng-aquila/src/button/anchor-plain-button.component.spec.ts @@ -1,59 +1,64 @@ import { Component, Directive, Type, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { NxAnchorPlainButtonComponent } from './anchor-plain-button.component'; -import { NxButtonModule } from './button.module'; +import { NxAnchorPlainButtonComponent, NxButtonModule } from '.'; +import { NxAnchorButtonComponent } from './anchor-button.component'; @Directive() -abstract class ButtonTest { - @ViewChild('button') buttonInstance!: NxAnchorPlainButtonComponent; - - disabled = false; +abstract class AnchorButtonTest { + @ViewChild('button') buttonInstance!: NxAnchorButtonComponent; } @Component({ - template: `Hello Button`, + template: `Link Text`, }) -class BasicButton extends ButtonTest {} +class TestInstance extends AnchorButtonTest { + clickBindingSpy = jasmine.createSpy('clickSpy'); +} -describe('NxAnchorPlainButtonComponent', () => { - let fixture: ComponentFixture; - let testInstance: ButtonTest; - let buttonElement: HTMLAnchorElement; +describe('NxAnchorButtonComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [TestInstance], + imports: [NxButtonModule], + }).compileComponents(); + })); - function createTestComponent(component: Type) { + let fixture: ComponentFixture; + let testInstance: TestInstance; + let buttonInstance: NxAnchorPlainButtonComponent; + let buttonNativeElement: HTMLAnchorElement; + + function createTestComponent(component: Type) { fixture = TestBed.createComponent(component); fixture.detectChanges(); testInstance = fixture.componentInstance; - buttonElement = fixture.nativeElement.querySelector('a') as HTMLAnchorElement; + buttonInstance = fixture.componentInstance.buttonInstance; + buttonNativeElement = fixture.nativeElement.querySelector('a') as HTMLAnchorElement; } - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - imports: [NxButtonModule], - declarations: [BasicButton], - }).compileComponents(); + it('creates the button', waitForAsync(() => { + createTestComponent(TestInstance); + expect(buttonInstance).toBeTruthy(); })); - it('should create the button component', () => { - createTestComponent(BasicButton); - expect(testInstance.buttonInstance).toBeTruthy(); - expect(buttonElement).toBeTruthy(); - expect(buttonElement.textContent).toBe('Hello Button'); - }); - - it('prevents default when the anchor button is disabled', fakeAsync(() => { - createTestComponent(BasicButton); - const clickSpy = jasmine.createSpy('clickSpy'); - buttonElement.addEventListener('click', clickSpy); - testInstance.buttonInstance.disabled = true; - buttonElement.click(); - expect(clickSpy).toHaveBeenCalledTimes(0); + it('has correct base class', waitForAsync(() => { + createTestComponent(TestInstance); + expect(buttonNativeElement).toHaveClass('nx-plain-button'); })); + it('disabled state prevents click binding on host element from firing', () => { + createTestComponent(TestInstance); + fixture.detectChanges(); + buttonInstance.disabled = true; + buttonNativeElement.click(); + + expect(testInstance.clickBindingSpy).not.toHaveBeenCalled(); + }); + describe('a11y', () => { it('has no accessibility violations', async () => { - createTestComponent(BasicButton); + createTestComponent(TestInstance); await expectAsync(fixture.nativeElement).toBeAccessible(); }); }); diff --git a/projects/ng-aquila/src/button/anchor-plain-button.component.ts b/projects/ng-aquila/src/button/anchor-plain-button.component.ts index 469bb85ff..22983042f 100644 --- a/projects/ng-aquila/src/button/anchor-plain-button.component.ts +++ b/projects/ng-aquila/src/button/anchor-plain-button.component.ts @@ -1,28 +1,16 @@ -import { FocusMonitor } from '@angular/cdk/a11y'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { NxTriggerButton } from '@aposin/ng-aquila/overlay'; - -import { NxPlainButtonComponent } from './plain-button.component'; +import { NxAnchorButtonBase } from './button-base'; @Component({ templateUrl: './plain-button.component.html', styleUrls: ['plain-button.component.scss'], selector: 'a[nxPlainButton]', - inputs: ['classNames:nxPlainButton'], changeDetection: ChangeDetectionStrategy.OnPush, + inputs: ['classNames:nxPlainButton'], providers: [{ provide: NxTriggerButton, useExisting: NxAnchorPlainButtonComponent }], + host: { + class: 'nx-plain-button', + }, }) -export class NxAnchorPlainButtonComponent extends NxPlainButtonComponent { - /** @docs-private */ - @HostListener('click', ['$event']) - _checkEventsDisabled(event: Event) { - if (this.disabled) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - } - - constructor(_cdr: ChangeDetectorRef, elementRef: ElementRef, focusMonitor: FocusMonitor) { - super(_cdr, elementRef, focusMonitor); - } -} +export class NxAnchorPlainButtonComponent extends NxAnchorButtonBase {} diff --git a/projects/ng-aquila/src/button/button-base.ts b/projects/ng-aquila/src/button/button-base.ts index 4fd0d631f..1604e07fa 100644 --- a/projects/ng-aquila/src/button/button-base.ts +++ b/projects/ng-aquila/src/button/button-base.ts @@ -1,6 +1,6 @@ import { FocusMonitor } from '@angular/cdk/a11y'; import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ChangeDetectorRef, Directive, ElementRef, HostBinding, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectorRef, Directive, ElementRef, HostBinding, Input, NgZone, OnDestroy } from '@angular/core'; import { NxTriggerButton } from '@aposin/ng-aquila/overlay'; /** Type of a button. */ @@ -150,3 +150,27 @@ export class NxButtonBase implements NxTriggerButton, OnDestroy { this._cdr.markForCheck(); } } + +/** @docs-private **/ +@Directive() +export class NxAnchorButtonBase extends NxButtonBase { + constructor(private _ngZone: NgZone, _cdr: ChangeDetectorRef, elementRef: ElementRef, focusMonitor: FocusMonitor) { + super(_cdr, elementRef, focusMonitor); + this._ngZone.runOutsideAngular(() => { + (this.elementRef.nativeElement).addEventListener('click', this._checkEventsDisabled); + }); + } + + override ngOnDestroy() { + super.ngOnDestroy(); + (this.elementRef.nativeElement).removeEventListener('click', this._checkEventsDisabled); + } + + /** @docs-private */ + private _checkEventsDisabled = (event: Event) => { + if (this.disabled) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + }; +} diff --git a/projects/ng-aquila/src/button/button.component.ts b/projects/ng-aquila/src/button/button.component.ts index 3fde2334a..62624ba02 100644 --- a/projects/ng-aquila/src/button/button.component.ts +++ b/projects/ng-aquila/src/button/button.component.ts @@ -12,7 +12,7 @@ import { NxButtonBase } from './button-base'; inputs: ['classNames:nxButton'], providers: [{ provide: NxTriggerButton, useExisting: NxButtonComponent }], host: { - '[class.nx-button]': 'true', + class: 'nx-button', }, }) export class NxButtonComponent extends NxButtonBase { diff --git a/projects/ng-aquila/src/button/icon-button.component.ts b/projects/ng-aquila/src/button/icon-button.component.ts index b72a0eb45..e5d115bb1 100644 --- a/projects/ng-aquila/src/button/icon-button.component.ts +++ b/projects/ng-aquila/src/button/icon-button.component.ts @@ -12,7 +12,7 @@ import { NxButtonBase } from './button-base'; inputs: ['classNames:nxIconButton'], providers: [{ provide: NxTriggerButton, useExisting: NxIconButtonComponent }], host: { - '[class.nx-icon-button]': 'true', + class: 'nx-icon-button', }, }) export class NxIconButtonComponent extends NxButtonBase { diff --git a/projects/ng-aquila/src/button/plain-button.component.ts b/projects/ng-aquila/src/button/plain-button.component.ts index a06e4ea97..af7115e79 100644 --- a/projects/ng-aquila/src/button/plain-button.component.ts +++ b/projects/ng-aquila/src/button/plain-button.component.ts @@ -15,7 +15,7 @@ export type NxPlainButtonVariant = 'primary' | 'secondary'; changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['classNames:nxPlainButton'], host: { - '[class.nx-plain-button]': 'true', + class: 'nx-plain-button', '[class.nx-plain-button--danger]': 'critical', '[class.nx-plain-button--secondary]': 'variant === "secondary"', '[class.nx-plain-button--small]': 'size === "small"',