Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ccb56b4

Browse filesBrowse files
committed
refactor(nav): signal inputs, host bindings, cleanup, tests
1 parent f95a237 commit ccb56b4
Copy full SHA for ccb56b4

File tree

Expand file treeCollapse file tree

4 files changed

+192
-56
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+192
-56
lines changed
+79-2Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,85 @@
11
import { NavLinkDirective } from './nav-link.directive';
2+
import { ComponentFixture, TestBed } from '@angular/core/testing';
3+
import { Component, ComponentRef, DebugElement, input } from '@angular/core';
4+
import { By } from '@angular/platform-browser';
5+
6+
@Component({
7+
template: '<a cNavLink [active]="active()" [disabled]="disabled()">test</a>',
8+
imports: [NavLinkDirective]
9+
})
10+
class TestComponent {
11+
readonly active = input(false);
12+
readonly disabled = input(false);
13+
}
214

315
describe('NavLinkDirective', () => {
16+
let fixture: ComponentFixture<TestComponent>;
17+
let component: TestComponent;
18+
let componentRef: ComponentRef<TestComponent>;
19+
let debugElement: DebugElement;
20+
21+
beforeEach(() => {
22+
TestBed.configureTestingModule({
23+
imports: [TestComponent]
24+
}).compileComponents();
25+
26+
fixture = TestBed.createComponent(TestComponent);
27+
component = fixture.componentInstance;
28+
componentRef = fixture.componentRef;
29+
debugElement = fixture.debugElement.query(By.directive(NavLinkDirective));
30+
fixture.detectChanges();
31+
});
32+
433
it('should create an instance', () => {
5-
const directive = new NavLinkDirective();
6-
expect(directive).toBeTruthy();
34+
TestBed.runInInjectionContext(() => {
35+
const directive = new NavLinkDirective();
36+
expect(directive).toBeTruthy();
37+
});
38+
});
39+
40+
it('should have css classes', () => {
41+
expect(debugElement.nativeElement).toHaveClass('nav-link');
42+
});
43+
44+
it('should have css classes for active', () => {
45+
expect(debugElement.nativeElement).not.toHaveClass('active');
46+
componentRef.setInput('active', true);
47+
fixture.detectChanges();
48+
expect(debugElement.nativeElement).toHaveClass('active');
49+
componentRef.setInput('active', false);
50+
fixture.detectChanges();
51+
expect(debugElement.nativeElement).not.toHaveClass('active');
52+
});
53+
54+
it('should have css classes for disabled', () => {
55+
expect(debugElement.nativeElement).not.toHaveClass('disabled');
56+
componentRef.setInput('disabled', true);
57+
fixture.detectChanges();
58+
expect(debugElement.nativeElement).toHaveClass('disabled');
59+
componentRef.setInput('disabled', false);
60+
fixture.detectChanges();
61+
expect(debugElement.nativeElement).not.toHaveClass('disabled');
62+
});
63+
64+
it('should have aria-* attr for active', () => {
65+
expect(debugElement.nativeElement.getAttribute('aria-current')).not.toBe('page');
66+
componentRef.setInput('active', true);
67+
fixture.detectChanges();
68+
expect(debugElement.nativeElement.getAttribute('aria-current')).toBe('page');
69+
});
70+
71+
it('should have attributes for disabled', () => {
72+
expect(debugElement.nativeElement.getAttribute('disabled')).toBeNull();
73+
expect(debugElement.nativeElement.getAttribute('aria-disabled')).not.toBeTruthy();
74+
expect(debugElement.nativeElement.getAttribute('tabindex')).not.toBe('-1');
75+
expect(debugElement.nativeElement.style.cursor).toBe('pointer');
76+
componentRef.setInput('disabled', true);
77+
fixture.detectChanges();
78+
expect(debugElement.nativeElement.getAttribute('disabled')).not.toBeNull();
79+
expect(debugElement.nativeElement.getAttribute('aria-disabled')).toBeTruthy();
80+
expect(debugElement.nativeElement.getAttribute('tabindex')).toBe('-1');
81+
expect(debugElement.nativeElement.style.cursor).not.toBe('pointer');
82+
componentRef.setInput('disabled', false);
83+
fixture.detectChanges();
784
});
885
});
+40-41Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,57 @@
1-
import { booleanAttribute, Directive, HostBinding, Input } from '@angular/core';
1+
import { booleanAttribute, computed, Directive, effect, input } from '@angular/core';
22

33
@Directive({
4-
selector: '[cNavLink]'
4+
selector: '[cNavLink]',
5+
host: {
6+
'[class]': 'hostClasses()',
7+
'[attr.aria-current]': 'ariaCurrent()',
8+
'[attr.aria-disabled]': 'ariaDisabled',
9+
'[attr.disabled]': 'attrDisabled',
10+
'[attr.tabindex]': 'attrTabindex',
11+
'[style.cursor]': 'styleCursor'
12+
}
513
})
614
export class NavLinkDirective {
715
/**
816
* Sets .nav-link class to the host. [docs]
9-
* @type boolean
1017
* @default true
1118
*/
12-
@Input({ transform: booleanAttribute }) cNavLink: string | boolean = true;
19+
readonly cNavLink = input(true, { transform: booleanAttribute });
1320

1421
/**
1522
* Toggle the active state for the component. [docs]
16-
* @type boolean
23+
* @default undefined
1724
*/
18-
@Input() active?: boolean;
25+
readonly active = input<boolean>();
26+
1927
/**
2028
* Set disabled attr for the host element. [docs]
21-
* @type boolean
29+
* @default false
2230
*/
23-
@Input({ transform: booleanAttribute }) disabled: string | boolean = false;
24-
25-
@HostBinding('attr.aria-current')
26-
get ariaCurrent(): string | null {
27-
return this.active ? 'page' : null;
28-
}
29-
30-
@HostBinding('attr.aria-disabled')
31-
get isDisabled(): boolean | null {
32-
return <boolean>this.disabled || null;
33-
}
34-
35-
@HostBinding('attr.disabled')
36-
get attrDisabled() {
37-
return this.disabled ? '' : null;
38-
}
39-
40-
@HostBinding('attr.tabindex')
41-
get getTabindex(): string | null {
42-
return this.disabled ? '-1' : null;
43-
}
44-
45-
@HostBinding('style.cursor')
46-
get getCursorStyle(): string | null {
47-
return this.disabled ? null : 'pointer';
48-
}
49-
50-
@HostBinding('class')
51-
get hostClasses(): any {
31+
readonly disabled = input(false, { transform: booleanAttribute });
32+
33+
readonly ariaCurrent = computed(() => {
34+
return this.active() ? 'page' : null;
35+
});
36+
37+
ariaDisabled: boolean | null = null;
38+
attrDisabled: boolean | string | null = null;
39+
attrTabindex: '-1' | null = null;
40+
styleCursor: 'pointer' | null = null;
41+
42+
readonly disabledEffect = effect(() => {
43+
const disabled = this.disabled();
44+
this.ariaDisabled = disabled || null;
45+
this.attrDisabled = disabled ? '' : null;
46+
this.attrTabindex = disabled ? '-1' : null;
47+
this.styleCursor = disabled ? null : 'pointer';
48+
});
49+
50+
readonly hostClasses = computed(() => {
5251
return {
53-
'nav-link': this.cNavLink,
54-
disabled: this.disabled,
55-
active: this.active
56-
};
57-
}
52+
'nav-link': this.cNavLink(),
53+
disabled: this.disabled(),
54+
active: this.active()
55+
} as Record<string, boolean>;
56+
});
5857
}

‎projects/coreui-angular/src/lib/nav/nav.component.spec.ts

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/nav/nav.component.spec.ts
+60-2Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
22

33
import { NavComponent } from './nav.component';
4+
import { ComponentRef } from '@angular/core';
45

56
describe('NavComponent', () => {
67
let component: NavComponent;
78
let fixture: ComponentFixture<NavComponent>;
9+
let componentRef: ComponentRef<NavComponent>;
810

911
beforeEach(waitForAsync(() => {
1012
TestBed.configureTestingModule({
1113
imports: [NavComponent]
12-
})
13-
.compileComponents();
14+
}).compileComponents();
1415
}));
1516

1617
beforeEach(() => {
1718
fixture = TestBed.createComponent(NavComponent);
1819
component = fixture.componentInstance;
20+
componentRef = fixture.componentRef;
1921
fixture.detectChanges();
2022
});
2123

@@ -26,4 +28,60 @@ describe('NavComponent', () => {
2628
it('should have css classes', () => {
2729
expect(fixture.nativeElement).toHaveClass('nav');
2830
});
31+
32+
it('should have css classes for layout', () => {
33+
componentRef.setInput('layout', 'fill');
34+
fixture.detectChanges();
35+
expect(fixture.nativeElement).toHaveClass('nav-fill');
36+
componentRef.setInput('layout', 'justified');
37+
fixture.detectChanges();
38+
expect(fixture.nativeElement).not.toHaveClass('nav-fill');
39+
expect(fixture.nativeElement).toHaveClass('nav-justified');
40+
componentRef.setInput('layout', undefined);
41+
fixture.detectChanges();
42+
expect(fixture.nativeElement).not.toHaveClass('nav-fill');
43+
expect(fixture.nativeElement).not.toHaveClass('nav-justified');
44+
});
45+
46+
it('should have css classes for variant', () => {
47+
expect(fixture.nativeElement).not.toHaveClass('nav-tabs');
48+
expect(fixture.nativeElement).not.toHaveClass('nav-pills');
49+
expect(fixture.nativeElement).not.toHaveClass('nav-underline');
50+
expect(fixture.nativeElement).not.toHaveClass('nav-underline-border');
51+
52+
componentRef.setInput('variant', 'tabs');
53+
fixture.detectChanges();
54+
expect(fixture.nativeElement).toHaveClass('nav-tabs');
55+
expect(fixture.nativeElement).not.toHaveClass('nav-pills');
56+
expect(fixture.nativeElement).not.toHaveClass('nav-underline');
57+
expect(fixture.nativeElement).not.toHaveClass('nav-underline-border');
58+
59+
componentRef.setInput('variant', 'pills');
60+
fixture.detectChanges();
61+
expect(fixture.nativeElement).not.toHaveClass('nav-tabs');
62+
expect(fixture.nativeElement).toHaveClass('nav-pills');
63+
expect(fixture.nativeElement).not.toHaveClass('nav-underline');
64+
expect(fixture.nativeElement).not.toHaveClass('nav-underline-border');
65+
66+
componentRef.setInput('variant', 'underline');
67+
fixture.detectChanges();
68+
expect(fixture.nativeElement).not.toHaveClass('nav-tabs');
69+
expect(fixture.nativeElement).not.toHaveClass('nav-pills');
70+
expect(fixture.nativeElement).toHaveClass('nav-underline');
71+
expect(fixture.nativeElement).not.toHaveClass('nav-underline-border');
72+
73+
componentRef.setInput('variant', 'underline-border');
74+
fixture.detectChanges();
75+
expect(fixture.nativeElement).not.toHaveClass('nav-tabs');
76+
expect(fixture.nativeElement).not.toHaveClass('nav-pills');
77+
expect(fixture.nativeElement).not.toHaveClass('nav-underline');
78+
expect(fixture.nativeElement).toHaveClass('nav-underline-border');
79+
80+
componentRef.setInput('variant', undefined);
81+
fixture.detectChanges();
82+
expect(fixture.nativeElement).not.toHaveClass('nav-tabs');
83+
expect(fixture.nativeElement).not.toHaveClass('nav-pills');
84+
expect(fixture.nativeElement).not.toHaveClass('nav-underline');
85+
expect(fixture.nativeElement).not.toHaveClass('nav-underline-border');
86+
});
2987
});
+13-11Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
1-
import { Component, HostBinding, Input } from '@angular/core';
1+
import { Component, computed, input } from '@angular/core';
22

33
@Component({
44
selector: 'c-nav',
55
template: '<ng-content />',
66
styleUrls: ['./nav.component.scss'],
7-
host: { class: 'nav' }
7+
host: { class: 'nav', '[class]': 'hostClasses()' }
88
})
99
export class NavComponent {
1010
/**
1111
* Specify a layout type for component.
12-
* @type {'fill' | 'justified'}
12+
* @default undefined
1313
*/
14-
@Input() layout?: 'fill' | 'justified';
14+
readonly layout = input<'fill' | 'justified'>();
15+
1516
/**
1617
* Set the nav variant to tabs or pills.
17-
* @type 'tabs' | 'pills' | 'underline' | 'underline-border'
18+
* @default undefined
1819
*/
19-
@Input() variant?: '' | 'tabs' | 'pills' | 'underline' | 'underline-border';
20+
readonly variant = input<'tabs' | 'pills' | 'underline' | 'underline-border' | ''>();
2021

21-
@HostBinding('class')
22-
get hostClasses(): any {
22+
readonly hostClasses = computed(() => {
23+
const layout = this.layout();
24+
const variant = this.variant();
2325
return {
2426
nav: true,
25-
[`nav-${this.layout}`]: !!this.layout,
26-
[`nav-${this.variant}`]: !!this.variant
27+
[`nav-${layout}`]: !!layout,
28+
[`nav-${variant}`]: !!variant
2729
};
28-
}
30+
});
2931
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.