Skip to content

Commit 3cd526d

Browse files
committed
feat(tag): allow customizing remove icon
1 parent 192a0de commit 3cd526d

3 files changed

Lines changed: 176 additions & 121 deletions

File tree

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
{{ text }}
2-
<nb-icon *ngIf="removable"
3-
(click)="_remove()"
4-
class="nb-tag-remove size-{{size}}"
5-
icon="close-outline"
6-
pack="nebular-essentials"
7-
aria-hidden="true">
2+
<nb-icon
3+
*ngIf="removable"
4+
(click)="_remove()"
5+
class="nb-tag-remove size-{{ size }}"
6+
[icon]="removeIcon"
7+
[pack]="removeIconPack"
8+
aria-hidden="true"
9+
>
810
</nb-icon>

src/framework/theme/components/tag/tag.component.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,13 @@ let tagUniqueId = 0;
228228
* tag-outline-control-selected-text-color:
229229
*/
230230
@Component({
231-
selector: 'nb-tag',
232-
templateUrl: './tag.component.html',
233-
exportAs: 'nbTag',
234-
changeDetection: ChangeDetectionStrategy.OnPush,
235-
standalone: false
231+
selector: 'nb-tag',
232+
templateUrl: './tag.component.html',
233+
exportAs: 'nbTag',
234+
changeDetection: ChangeDetectionStrategy.OnPush,
235+
standalone: false,
236236
})
237237
export class NbTagComponent implements AfterViewInit, OnDestroy, NbHighlightableOption {
238-
239238
private _destroy$: Subject<NbTagComponent> = new Subject<NbTagComponent>();
240239

241240
get destroy$(): Observable<NbTagComponent> {
@@ -275,6 +274,18 @@ export class NbTagComponent implements AfterViewInit, OnDestroy, NbHighlightable
275274
protected _removable: boolean = false;
276275
static ngAcceptInputType_removable: NbBooleanInput;
277276

277+
/**
278+
* Remove icon name
279+
* @param {string} status
280+
*/
281+
@Input() removeIcon: string = 'minus-outline';
282+
283+
/**
284+
* Remove icon pack name
285+
* @param {string} status
286+
*/
287+
@Input() removeIconPack: string = 'nebular-essentials';
288+
278289
/**
279290
* Tag appearance: `filled`, `outline`.
280291
*/
@@ -414,9 +425,11 @@ export class NbTagComponent implements AfterViewInit, OnDestroy, NbHighlightable
414425

415426
ngAfterViewInit() {
416427
// TODO: #2254
417-
this.zone.runOutsideAngular(() => setTimeout(() => {
418-
this.renderer.addClass(this._hostElement.nativeElement, 'nb-transition');
419-
}));
428+
this.zone.runOutsideAngular(() =>
429+
setTimeout(() => {
430+
this.renderer.addClass(this._hostElement.nativeElement, 'nb-transition');
431+
}),
432+
);
420433
}
421434

422435
ngOnDestroy() {

src/framework/theme/components/tag/tag.spec.ts

Lines changed: 146 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -4,123 +4,163 @@ import { NbTagComponent, NbTagModule, NbThemeModule } from '@nebular/theme';
44
import createSpy = jasmine.createSpy;
55

66
@Component({
7-
selector: 'nb-tag-test',
8-
template: `<nb-tag [text]="'test-tag'" [removable]="isRemovable" [selected]="selected"></nb-tag>`,
9-
standalone: false
7+
selector: 'nb-tag-test',
8+
template: `<nb-tag
9+
[text]="'test-tag'"
10+
[removable]="isRemovable"
11+
[selected]="selected"
12+
[removeIcon]="removeIcon"
13+
[removeIconPack]="removeIconPack"
14+
></nb-tag>`,
15+
standalone: false,
1016
})
11-
1217
export class NbTagTestComponent {
13-
@ViewChild(NbTagComponent) nbTag: NbTagComponent;
14-
isRemovable: boolean = true;
15-
selected: boolean = false;
18+
@ViewChild(NbTagComponent) nbTag: NbTagComponent;
19+
isRemovable: boolean = true;
20+
selected: boolean = false;
21+
removeIcon: string = 'close-outline';
22+
removeIconPack: string = 'nebular-essentials';
1623
}
1724

1825
describe('Component: NbTagComponent', () => {
19-
let tag: NbTagComponent;
20-
let fixture: ComponentFixture<NbTagComponent>;
21-
22-
beforeEach(() => {
23-
TestBed.configureTestingModule({
24-
imports: [NbThemeModule.forRoot(), NbTagModule],
25-
declarations: [NbTagTestComponent],
26-
});
27-
28-
fixture = TestBed.createComponent(NbTagComponent);
29-
tag = fixture.componentInstance;
30-
fixture.detectChanges();
31-
});
32-
33-
it('should be created', () => {
34-
expect(tag).toBeTruthy();
35-
})
36-
37-
it('should return default selected property', () => {
38-
expect(fixture.nativeElement.classList).not.toContain('selected');
39-
});
40-
41-
it('should return toggled selection value', () => {
42-
tag._toggleSelection();
43-
fixture.detectChanges();
44-
expect(fixture.nativeElement.classList).toContain('selected');
45-
});
46-
47-
it('should be inactive by default', () => {
48-
expect(fixture.nativeElement.classList).not.toContain('active');
49-
});
26+
let tag: NbTagComponent;
27+
let fixture: ComponentFixture<NbTagComponent>;
5028

51-
it('should be active', () => {
52-
tag.setActiveStyles();
53-
fixture.detectChanges();
54-
expect(fixture.nativeElement.classList).toContain('active');
55-
});
56-
57-
it('should return applied inactive state', () => {
58-
tag.setInactiveStyles();
59-
fixture.detectChanges();
60-
expect(fixture.nativeElement.classList).not.toContain('active');
29+
beforeEach(() => {
30+
TestBed.configureTestingModule({
31+
imports: [NbThemeModule.forRoot(), NbTagModule],
32+
declarations: [NbTagTestComponent],
6133
});
6234

35+
fixture = TestBed.createComponent(NbTagComponent);
36+
tag = fixture.componentInstance;
37+
fixture.detectChanges();
38+
});
39+
40+
it('should be created', () => {
41+
expect(tag).toBeTruthy();
42+
});
43+
44+
it('should return default selected property', () => {
45+
expect(fixture.nativeElement.classList).not.toContain('selected');
46+
});
47+
48+
it('should return toggled selection value', () => {
49+
tag._toggleSelection();
50+
fixture.detectChanges();
51+
expect(fixture.nativeElement.classList).toContain('selected');
52+
});
53+
54+
it('should be inactive by default', () => {
55+
expect(fixture.nativeElement.classList).not.toContain('active');
56+
});
57+
58+
it('should be active', () => {
59+
tag.setActiveStyles();
60+
fixture.detectChanges();
61+
expect(fixture.nativeElement.classList).toContain('active');
62+
});
63+
64+
it('should return applied inactive state', () => {
65+
tag.setInactiveStyles();
66+
fixture.detectChanges();
67+
expect(fixture.nativeElement.classList).not.toContain('active');
68+
});
6369
});
6470

6571
describe('Component: NbTagComponent bindings', () => {
66-
let fixture: ComponentFixture<NbTagTestComponent>;
67-
68-
beforeEach(() => {
69-
TestBed.configureTestingModule({
70-
imports: [NbThemeModule.forRoot(), NbTagModule],
71-
declarations: [NbTagTestComponent],
72-
});
73-
fixture = TestBed.createComponent(NbTagTestComponent);
74-
fixture.detectChanges();
75-
});
72+
let fixture: ComponentFixture<NbTagTestComponent>;
7673

77-
it('should be created', () => {
78-
const tagEl = fixture.nativeElement.querySelector('nb-tag');
79-
expect(tagEl).toBeTruthy();
74+
beforeEach(() => {
75+
TestBed.configureTestingModule({
76+
imports: [NbThemeModule.forRoot(), NbTagModule],
77+
declarations: [NbTagTestComponent],
8078
});
81-
82-
it('should emit remove event on delete keydown', () => {
83-
const removeSpy = createSpy('removeSpy');
84-
const tagEl = fixture.nativeElement.querySelector('nb-tag');
85-
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
86-
tagEl.dispatchEvent(new KeyboardEvent('keydown', { 'key': 'delete' }));
87-
expect(removeSpy).toHaveBeenCalled();
88-
});
89-
90-
it('should not emit remove event on delete keydown', () => {
91-
const removeSpy = createSpy('removeSpy');
92-
const tagEl = fixture.nativeElement.querySelector('nb-tag');
93-
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
94-
fixture.componentInstance.isRemovable = false;
95-
fixture.detectChanges();
96-
tagEl.dispatchEvent(new KeyboardEvent('keydown', { 'key': 'delete' }));
97-
expect(removeSpy).not.toHaveBeenCalled();
98-
});
99-
100-
it('should emit remove event on backspace keydown', () => {
101-
const removeSpy = createSpy('removeSpy');
102-
const tagEl = fixture.nativeElement.querySelector('nb-tag');
103-
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
104-
tagEl.dispatchEvent(new KeyboardEvent('keydown', { 'key': 'backspace' }));
105-
expect(removeSpy).toHaveBeenCalled();
106-
});
107-
108-
it('should not emit remove event on backspace keydown', () => {
109-
const removeSpy = createSpy('removeSpy');
110-
const tagEl = fixture.nativeElement.querySelector('nb-tag');
111-
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
112-
fixture.componentInstance.isRemovable = false;
113-
fixture.detectChanges();
114-
tagEl.dispatchEvent(new KeyboardEvent('keydown', { 'key': 'backspace' }));
115-
expect(removeSpy).not.toHaveBeenCalled();
116-
});
117-
118-
it('should emit selected change event', () => {
119-
const selectedSpy = createSpy('selected');
120-
fixture.componentInstance.nbTag.selectedChange.subscribe(selectedSpy);
121-
fixture.componentInstance.selected = true;
122-
fixture.detectChanges();
123-
expect(selectedSpy).toHaveBeenCalled();
124-
});
125-
79+
fixture = TestBed.createComponent(NbTagTestComponent);
80+
fixture.detectChanges();
81+
});
82+
83+
it('should be created', () => {
84+
const tagEl = fixture.nativeElement.querySelector('nb-tag');
85+
expect(tagEl).toBeTruthy();
86+
});
87+
88+
it('should emit remove event on delete keydown', () => {
89+
const removeSpy = createSpy('removeSpy');
90+
const tagEl = fixture.nativeElement.querySelector('nb-tag');
91+
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
92+
tagEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'delete' }));
93+
expect(removeSpy).toHaveBeenCalled();
94+
});
95+
96+
it('should not emit remove event on delete keydown', () => {
97+
const removeSpy = createSpy('removeSpy');
98+
const tagEl = fixture.nativeElement.querySelector('nb-tag');
99+
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
100+
fixture.componentInstance.isRemovable = false;
101+
fixture.detectChanges();
102+
tagEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'delete' }));
103+
expect(removeSpy).not.toHaveBeenCalled();
104+
});
105+
106+
it('should emit remove event on backspace keydown', () => {
107+
const removeSpy = createSpy('removeSpy');
108+
const tagEl = fixture.nativeElement.querySelector('nb-tag');
109+
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
110+
tagEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'backspace' }));
111+
expect(removeSpy).toHaveBeenCalled();
112+
});
113+
114+
it('should not emit remove event on backspace keydown', () => {
115+
const removeSpy = createSpy('removeSpy');
116+
const tagEl = fixture.nativeElement.querySelector('nb-tag');
117+
fixture.componentInstance.nbTag.remove.subscribe(removeSpy);
118+
fixture.componentInstance.isRemovable = false;
119+
fixture.detectChanges();
120+
tagEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'backspace' }));
121+
expect(removeSpy).not.toHaveBeenCalled();
122+
});
123+
124+
it('should emit selected change event', () => {
125+
const selectedSpy = createSpy('selected');
126+
fixture.componentInstance.nbTag.selectedChange.subscribe(selectedSpy);
127+
fixture.componentInstance.selected = true;
128+
fixture.detectChanges();
129+
expect(selectedSpy).toHaveBeenCalled();
130+
});
131+
132+
it('should render default remove icon inputs', () => {
133+
const tagEl: HTMLElement = fixture.nativeElement.querySelector('nb-tag');
134+
const iconEl: HTMLElement | null = tagEl.querySelector('nb-icon');
135+
136+
expect(iconEl).toBeTruthy();
137+
138+
expect(iconEl.getAttribute('ng-reflect-icon')).toBe('close-outline');
139+
expect(iconEl.getAttribute('ng-reflect-pack')).toBe('nebular-essentials');
140+
});
141+
142+
it('should render custom remove icon name', () => {
143+
fixture.componentInstance.removeIcon = 'minus-outline';
144+
fixture.detectChanges();
145+
146+
const tagEl: HTMLElement = fixture.nativeElement.querySelector('nb-tag');
147+
const iconEl: HTMLElement | null = tagEl.querySelector('nb-icon');
148+
149+
expect(iconEl).toBeTruthy();
150+
expect(iconEl.getAttribute('ng-reflect-icon')).toBe('minus-outline');
151+
expect(iconEl.getAttribute('ng-reflect-pack')).toBe('nebular-essentials');
152+
});
153+
154+
it('should render custom remove icon pack', () => {
155+
fixture.componentInstance.removeIcon = 'plus-outline';
156+
fixture.componentInstance.removeIconPack = 'eva';
157+
fixture.detectChanges();
158+
159+
const tagEl: HTMLElement = fixture.nativeElement.querySelector('nb-tag');
160+
const iconEl: HTMLElement | null = tagEl.querySelector('nb-icon');
161+
162+
expect(iconEl).toBeTruthy();
163+
expect(iconEl.getAttribute('ng-reflect-icon')).toBe('plus-outline');
164+
expect(iconEl.getAttribute('ng-reflect-pack')).toBe('eva');
165+
});
126166
});

0 commit comments

Comments
 (0)