Skip to content

Commit

Permalink
feat: accept icon config in components which use nb-icon (#1935)
Browse files Browse the repository at this point in the history
  • Loading branch information
yggg authored Aug 29, 2019
1 parent 8295c32 commit c9b9d32
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 37 deletions.
13 changes: 7 additions & 6 deletions src/framework/theme/components/actions/actions.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { convertToBoolProperty } from '../helpers';
import { NbComponentSize } from '../component-size';
import { NbComponentStatus } from '../component-status';
import { NbBadgePosition } from '../badge/badge.component';
import { NbIconConfig } from '../icon/icon.component';

/**
* Action item, display a link with an icon, or any other content provided instead.
Expand All @@ -23,20 +24,20 @@ import { NbBadgePosition } from '../badge/badge.component';
[routerLink]="link"
[title]="title"
*ngIf="link">
<nb-icon [icon]="icon"></nb-icon>
<nb-icon [config]="icon"></nb-icon>
</a>
<a class="icon-container"
[href]="href"
[title]="title"
*ngIf="href && !link">
<nb-icon [icon]="icon"></nb-icon>
<nb-icon [config]="icon"></nb-icon>
</a>
<a class="icon-container"
href="#"
[title]="title"
*ngIf="!href && !link"
(click)="$event.preventDefault()">
<nb-icon [icon]="icon"></nb-icon>
<nb-icon [config]="icon"></nb-icon>
</a>
</ng-container>
Expand Down Expand Up @@ -72,10 +73,10 @@ export class NbActionComponent {
@Input() title: string = '';

/**
* Icon name
* @type string
* Icon name or config object
* @type {string | NbIconConfig}
*/
@Input() icon: string;
@Input() icon: string | NbIconConfig;

/**
* Visually disables the item
Expand Down
36 changes: 35 additions & 1 deletion src/framework/theme/components/icon/icon.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { NbComponentStatus } from '../component-status';
import { NbIconLibraries } from './icon-libraries';

export interface NbIconConfig {
icon: string;
pack?: string;
status?: NbComponentStatus;
options?: { [name: string]: any };
}

/**
* Icon component. Allows to render both `svg` and `font` icons.
* Starting from Nebular 4.0 uses [Eva Icons](https://akveo.github.io/eva-icons/) pack by default.
Expand Down Expand Up @@ -102,7 +109,7 @@ import { NbIconLibraries } from './icon-libraries';
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NbIconComponent implements OnChanges, OnInit {
export class NbIconComponent implements NbIconConfig, OnChanges, OnInit {

protected iconDef;
protected prevClasses = [];
Expand Down Expand Up @@ -159,6 +166,33 @@ export class NbIconComponent implements OnChanges, OnInit {
*/
@Input() status: NbComponentStatus;

/**
* Sets all icon configurable properties via config object.
* If passed value is a string set icon name.
* @docs-private
*/
@Input()
get config(): string | NbIconConfig {
return this._config;
}
set config(value: string | NbIconConfig) {
if (!value) {
return;
}

this._config = value;

if (typeof value === 'string') {
this.icon = value;
} else {
this.icon = value.icon;
this.pack = value.pack;
this.status = value.status;
this.options = value.options;
}
}
protected _config: string | NbIconConfig;

constructor(
protected sanitizer: DomSanitizer,
protected iconLibrary: NbIconLibraries,
Expand Down
66 changes: 66 additions & 0 deletions src/framework/theme/components/icon/icon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/

import { NbFontIcon, NbSvgIcon } from './icon';
import { TestBed } from '@angular/core/testing';
import { NbIconModule, NbIconComponent, NbIconConfig } from '@nebular/theme';


describe('icon', () => {
Expand Down Expand Up @@ -73,3 +75,67 @@ describe('icon', () => {
expect(svgIcon.getClasses()).toEqual([]);
});
});

describe('NbIconComponent', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ NbIconModule ],
});
});

it('should set icon name when string passed as a config', () => {
const iconComponent: NbIconComponent = TestBed.createComponent(NbIconComponent).componentInstance;
const iconName = 'some-icon';

iconComponent.config = iconName;

expect(iconComponent.icon).toEqual(iconName);
});

it('should set icon when object with icon passed as a config', () => {
const iconComponent: NbIconComponent = TestBed.createComponent(NbIconComponent).componentInstance;
const iconConfig: NbIconConfig = { icon: 'some-icon' };

iconComponent.config = iconConfig;

expect(iconComponent.icon).toEqual(iconConfig.icon);
});

it('should set pack when object with pack passed as a config', () => {
const iconComponent: NbIconComponent = TestBed.createComponent(NbIconComponent).componentInstance;
const iconConfig: NbIconConfig = { icon: 'some-icon', pack: 'some-pack' };

iconComponent.config = iconConfig;

expect(iconComponent.pack).toEqual(iconConfig.pack);
});

it('should set status when object with status passed as a config', () => {
const iconComponent: NbIconComponent = TestBed.createComponent(NbIconComponent).componentInstance;
const iconConfig: NbIconConfig = { icon: 'some-icon', status: 'danger' };

iconComponent.config = iconConfig;

expect(iconComponent.status).toEqual(iconConfig.status);
});

it('should set options when object with options passed as a config', () => {
const iconComponent: NbIconComponent = TestBed.createComponent(NbIconComponent).componentInstance;
const options = { someProp: 'someVal' };
const iconConfig: NbIconConfig = { icon: 'some-icon', options };

iconComponent.config = iconConfig;

expect(iconComponent.options).toEqual(iconConfig.options);
});

it('should do nothing when falsy value passed as a config', () => {
const iconComponent: NbIconComponent = TestBed.createComponent(NbIconComponent).componentInstance;
const config: NbIconConfig = { icon: 'icon', pack: 'pack', status: 'danger', options: { opt: 'opt' } };
iconComponent.config = config;

iconComponent.config = null;
expect(iconComponent.config).toEqual(config);
});
});
10 changes: 5 additions & 5 deletions src/framework/theme/components/menu/menu-item.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<span *ngIf="menuItem.group">
<nb-icon class="menu-icon" [icon]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<nb-icon class="menu-icon" [config]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
{{ menuItem.title }}
</span>
<a *ngIf="menuItem.link && !menuItem.url && !menuItem.children && !menuItem.group"
Expand All @@ -12,7 +12,7 @@
[class.active]="menuItem.selected"
(mouseenter)="onHoverItem(menuItem)"
(click)="onItemClick(menuItem);">
<nb-icon class="menu-icon" [icon]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<nb-icon class="menu-icon" [config]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<span class="menu-title">{{ menuItem.title }}</span>
</a>
<a *ngIf="menuItem.url && !menuItem.children && !menuItem.link && !menuItem.group"
Expand All @@ -22,7 +22,7 @@
[class.active]="menuItem.selected"
(mouseenter)="onHoverItem(menuItem)"
(click)="onSelectItem(menuItem)">
<nb-icon class="menu-icon" [icon]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<nb-icon class="menu-icon" [config]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<span class="menu-title">{{ menuItem.title }}</span>
</a>
<a *ngIf="!menuItem.children && !menuItem.link && !menuItem.url && !menuItem.group"
Expand All @@ -31,7 +31,7 @@
[class.active]="menuItem.selected"
(mouseenter)="onHoverItem(menuItem)"
(click)="$event.preventDefault(); onItemClick(menuItem);">
<nb-icon class="menu-icon" [icon]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<nb-icon class="menu-icon" [config]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<span class="menu-title">{{ menuItem.title }}</span>
</a>
<a *ngIf="menuItem.children"
Expand All @@ -41,7 +41,7 @@
[class.active]="menuItem.selected"
(mouseenter)="onHoverItem(menuItem)"
href="#">
<nb-icon class="menu-icon" [icon]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<nb-icon class="menu-icon" [config]="menuItem.icon" *ngIf="menuItem.icon"></nb-icon>
<span class="menu-title">{{ menuItem.title }}</span>
<nb-icon class="expand-state" [icon]="getExpandStateIcon()" pack="nebular-essentials"></nb-icon>
</a>
Expand Down
7 changes: 4 additions & 3 deletions src/framework/theme/components/menu/menu.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Params } from '@angular/router';
import { Observable, BehaviorSubject, ReplaySubject, Subject } from 'rxjs';
import { share } from 'rxjs/operators';
import { isFragmentContain, isFragmentEqual, isUrlPathContain, isUrlPathEqual } from './url-matching-helpers';
import { NbIconConfig } from '../icon/icon.component';

export interface NbMenuBag { tag: string; item: NbMenuItem }

Expand Down Expand Up @@ -49,10 +50,10 @@ export class NbMenuItem {
*/
url?: string;
/**
* Icon class name
* @type {string}
* Icon class name or icon config object
* @type {string | NbIconConfig}
*/
icon?: string;
icon?: string | NbIconConfig;
/**
* Expanded by default
* @type {boolean}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import { convertToBoolProperty } from '../helpers';
class="route-tab disabled"
tabindex="-1">
<a tabindex="-1" class="tab-link">
<nb-icon *ngIf="tab.icon" [icon]="tab.icon"></nb-icon>
<nb-icon *ngIf="tab.icon" [config]="tab.icon"></nb-icon>
<span *ngIf="tab.title" class="tab-text">{{ tab.title }}</span>
</a>
</li>
Expand Down
9 changes: 5 additions & 4 deletions src/framework/theme/components/tabset/tabset.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ActivatedRoute } from '@angular/router';
import { convertToBoolProperty } from '../helpers';
import { NbComponentStatus } from '../component-status';
import { NbBadgePosition } from '../badge/badge.component';
import { NbIconConfig } from '../icon/icon.component';

/**
* Specific tab container.
Expand Down Expand Up @@ -50,10 +51,10 @@ export class NbTabComponent {
@Input() tabTitle: string;

/**
* Tab icon
* @type {string}
* Tab icon name or icon config object
* @type {string | NbIconConfig}
*/
@Input() tabIcon: string;
@Input() tabIcon: string | NbIconConfig;

/**
* Item is disabled and cannot be opened.
Expand Down Expand Up @@ -248,7 +249,7 @@ export class NbTabComponent {
[attr.tabindex]="tab.disabled ? -1 : 0"
class="tab">
<a href (click)="$event.preventDefault()" tabindex="-1" class="tab-link">
<nb-icon *ngIf="tab.tabIcon" [icon]="tab.tabIcon"></nb-icon>
<nb-icon *ngIf="tab.tabIcon" [config]="tab.tabIcon"></nb-icon>
<span *ngIf="tab.tabTitle" class="tab-text">{{ tab.tabTitle }}</span>
</a>
<nb-badge *ngIf="tab.badgeText"
Expand Down
2 changes: 1 addition & 1 deletion src/framework/theme/components/toastr/toast.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="icon-container" *ngIf="hasIcon && icon">
<nb-icon [icon]="icon" [pack]="iconPack"></nb-icon>
<nb-icon [config]="iconConfig"></nb-icon>
</div>
<div class="content-container">
<span class="title subtitle">{{ toast.title }}</span>
Expand Down
24 changes: 23 additions & 1 deletion src/framework/theme/components/toastr/toast.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { Component, EventEmitter, HostBinding, HostListener, Input, Output } from '@angular/core';

import { NbToast } from './model';
import { NbIconConfig } from '../icon/icon.component';

/**
* The `NbToastComponent` is responsible for rendering each toast with appropriate styles.
Expand Down Expand Up @@ -118,14 +119,35 @@ export class NbToastComponent {
return !!this.icon;
}

get icon(): string {
get icon(): string | NbIconConfig {
return this.toast.config.icon;
}

/* @deprecated Use pack property of icon config */
get iconPack(): string {
return this.toast.config.iconPack;
}

/*
@breaking-change 5 remove
@deprecated
*/
get iconConfig(): NbIconConfig {
const toastConfig = this.toast.config;
const isIconName = typeof this.icon === 'string';

if (!isIconName) {
return toastConfig.icon as NbIconConfig;
}

const iconConfig: NbIconConfig = { icon: toastConfig.icon as string };
if (toastConfig.iconPack) {
iconConfig.pack = toastConfig.iconPack;
}

return iconConfig;
}

@HostListener('click')
onClick() {
this.destroy.emit();
Expand Down
13 changes: 9 additions & 4 deletions src/framework/theme/components/toastr/toastr-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { InjectionToken } from '@angular/core';

import { NbGlobalLogicalPosition, NbGlobalPosition } from '../cdk/overlay/position-helper';
import { NbComponentStatus } from '../component-status';
import { NbIconConfig } from '../icon/icon.component';

type IconToClassMap = {
[status in NbComponentStatus]: string;
Expand Down Expand Up @@ -56,11 +57,13 @@ export class NbToastrConfig {
* */
hasIcon: boolean = true;
/**
* Icon name that can be provided to render custom icon.
* Icon name or icon config object that can be provided to render custom icon.
* */
icon: string = 'email';
icon: string | NbIconConfig = 'email';
/**
* Icon pack to look for the icon in.
* @deprecated Set pack via icon config object passed to icon property
* @breaking-change 5.0.0
* */
iconPack: string;
/**
Expand All @@ -81,8 +84,10 @@ export class NbToastrConfig {

protected patchIcon(config: Partial<NbToastrConfig>) {
if (!('icon' in config)) {
config.icon = this.icons[config.status || 'primary'];
config.iconPack = 'nebular-essentials';
config.icon = {
icon: this.icons[config.status || 'primary'],
pack: 'nebular-essentials',
};
}
}
}
5 changes: 3 additions & 2 deletions src/framework/theme/components/tooltip/tooltip.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
import { NbComponentStatus } from '../component-status';
import { NbRenderableContainer } from '../cdk/overlay/overlay-container';
import { NbPosition } from '../cdk/overlay/overlay-position';
import { NbIconConfig } from '../icon/icon.component';


/**
Expand Down Expand Up @@ -48,7 +49,7 @@ import { NbPosition } from '../cdk/overlay/overlay-position';
template: `
<span class="arrow"></span>
<div class="content">
<nb-icon *ngIf="context?.icon" [icon]="context.icon"></nb-icon>
<nb-icon *ngIf="context?.icon" [config]="context.icon"></nb-icon>
<span *ngIf="content">{{ content }}</span>
</div>
`,
Expand Down Expand Up @@ -87,7 +88,7 @@ export class NbTooltipComponent implements NbRenderableContainer {
}

@Input()
context: { icon?: string, status?: '' | NbComponentStatus } = {};
context: { icon?: string | NbIconConfig, status?: '' | NbComponentStatus } = {};

get statusClass() {
return this.context.status ? `status-${this.context.status}` : '';
Expand Down
Loading

0 comments on commit c9b9d32

Please sign in to comment.