diff --git a/skyux-spa-visual-tests/src/app/icon/icon-visual.component.html b/skyux-spa-visual-tests/src/app/icon/icon-visual.component.html new file mode 100644 index 000000000..11edcef8d --- /dev/null +++ b/skyux-spa-visual-tests/src/app/icon/icon-visual.component.html @@ -0,0 +1,33 @@ +
+ + + + + + + + + + + + + + + + + + + + +
diff --git a/skyux-spa-visual-tests/src/app/icon/icon-visual.component.ts b/skyux-spa-visual-tests/src/app/icon/icon-visual.component.ts new file mode 100644 index 000000000..86d657f81 --- /dev/null +++ b/skyux-spa-visual-tests/src/app/icon/icon-visual.component.ts @@ -0,0 +1,9 @@ +import { + Component +} from '@angular/core'; + +@Component({ + selector: 'icon-visual', + templateUrl: './icon-visual.component.html' +}) +export class IconVisualComponent { } diff --git a/skyux-spa-visual-tests/src/app/icon/icon.visual-spec.ts b/skyux-spa-visual-tests/src/app/icon/icon.visual-spec.ts new file mode 100644 index 000000000..526f2a412 --- /dev/null +++ b/skyux-spa-visual-tests/src/app/icon/icon.visual-spec.ts @@ -0,0 +1,17 @@ +import { + SkyVisualTest +} from '../../../config/utils/visual-test-commands'; + +describe('icon', () => { + it('should show the icon', () => { + return SkyVisualTest + .setupTest('icon') + .then(() => { + SkyVisualTest.moveCursorOffScreen(); + return SkyVisualTest.compareScreenshot({ + screenshotName: 'icon', + selector: '#screenshot-icon' + }); + }); + }); +}); diff --git a/skyux-spa-visual-tests/src/app/icon/index.html b/skyux-spa-visual-tests/src/app/icon/index.html new file mode 100644 index 000000000..6c051cf61 --- /dev/null +++ b/skyux-spa-visual-tests/src/app/icon/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core.ts b/src/core.ts index cbce611a5..d662e66a0 100644 --- a/src/core.ts +++ b/src/core.ts @@ -33,6 +33,7 @@ import { SkyFilterModule } from './modules/filter'; import { SkyFluidGridModule } from './modules/fluid-grid/fluid-grid.module'; import { SkyFlyoutModule } from './modules/flyout/flyout.module'; import { SkyGridModule } from './modules/grid'; +import { SkyIconModule } from './modules/icon'; import { SkyHelpInlineModule } from './modules/help-inline'; import { SkyInfiniteScrollModule } from './modules/infinite-scroll'; import { SkyKeyInfoModule } from './modules/key-info'; @@ -95,6 +96,7 @@ import { SkyWaitModule } from './modules/wait'; SkyFluidGridModule, SkyFlyoutModule, SkyGridModule, + SkyIconModule, SkyHelpInlineModule, SkyInfiniteScrollModule, SkyKeyInfoModule, @@ -160,6 +162,7 @@ export * from './modules/fluid-grid'; export * from './modules/flyout'; export * from './modules/format'; export * from './modules/grid'; +export * from './modules/icon'; export * from './modules/help-inline'; export * from './modules/infinite-scroll'; export * from './modules/key-info'; diff --git a/src/demo.ts b/src/demo.ts index f4e2e0d45..c192beadf 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -38,6 +38,7 @@ import { SkyFlyoutDemoComponent, SkyGridDemoComponent, SkyHelpInlineDemoComponent, + SkyIconDemoComponent, SkyInfiniteScrollDemoComponent, SkyKeyInfoDemoComponent, SkyLabelDemoComponent, @@ -119,6 +120,7 @@ const components = [ SkyFlyoutDemoComponent, SkyFlyoutDemoInternalComponent, SkyGridDemoComponent, + SkyIconDemoComponent, SkyHelpInlineDemoComponent, SkyInfiniteScrollDemoComponent, SkyKeyInfoDemoComponent, diff --git a/src/demos/demo.service.ts b/src/demos/demo.service.ts index 8d529eb43..f0de32eeb 100644 --- a/src/demos/demo.service.ts +++ b/src/demos/demo.service.ts @@ -24,6 +24,7 @@ import { SkyFlyoutDemoComponent, SkyGridDemoComponent, SkyHelpInlineDemoComponent, + SkyIconDemoComponent, SkyInfiniteScrollDemoComponent, SkyKeyInfoDemoComponent, SkyLabelDemoComponent, @@ -455,6 +456,22 @@ export class SkyDemoService { } ] }, + { + name: 'Icon', + component: SkyIconDemoComponent, + files: [ + { + name: 'icon-demo.component.html', + fileContents: require('!!raw-loader!./icon/icon-demo.component.html') + }, + { + name: 'icon-demo.component.ts', + fileContents: require('!!raw-loader!./icon/icon-demo.component.ts'), + componentName: 'SkyIconDemoComponent', + bootstrapSelector: 'sky-icon-demo' + } + ] + }, { name: 'Infinite scroll', component: SkyInfiniteScrollDemoComponent, diff --git a/src/demos/icon/icon-demo.component.html b/src/demos/icon/icon-demo.component.html new file mode 100644 index 000000000..c01165ad8 --- /dev/null +++ b/src/demos/icon/icon-demo.component.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/demos/icon/icon-demo.component.ts b/src/demos/icon/icon-demo.component.ts new file mode 100644 index 000000000..ee0d628a0 --- /dev/null +++ b/src/demos/icon/icon-demo.component.ts @@ -0,0 +1,9 @@ +import { + Component +} from '@angular/core'; + +@Component({ + selector: 'sky-icon-demo', + templateUrl: './icon-demo.component.html' +}) +export class SkyIconDemoComponent { } diff --git a/src/demos/icon/index.ts b/src/demos/icon/index.ts new file mode 100644 index 000000000..5b6cb9876 --- /dev/null +++ b/src/demos/icon/index.ts @@ -0,0 +1 @@ +export * from './icon-demo.component'; diff --git a/src/demos/index.ts b/src/demos/index.ts index 2bcbeddd9..465d6fe7a 100644 --- a/src/demos/index.ts +++ b/src/demos/index.ts @@ -17,6 +17,7 @@ export * from './fluid-grid'; export * from './flyout'; export * from './grid'; export * from './help-inline'; +export * from './icon'; export * from './infinite-scroll'; export * from './key-info'; export * from './label'; diff --git a/src/modules/icon/fixtures/icon.component.fixture.html b/src/modules/icon/fixtures/icon.component.fixture.html new file mode 100644 index 000000000..ed7b283d8 --- /dev/null +++ b/src/modules/icon/fixtures/icon.component.fixture.html @@ -0,0 +1,5 @@ + + diff --git a/src/modules/icon/fixtures/icon.component.fixture.ts b/src/modules/icon/fixtures/icon.component.fixture.ts new file mode 100644 index 000000000..6c18e25ef --- /dev/null +++ b/src/modules/icon/fixtures/icon.component.fixture.ts @@ -0,0 +1,13 @@ +import { + Component +} from '@angular/core'; + +@Component({ + selector: 'sky-test-cmp', + templateUrl: './icon.component.fixture.html' +}) +export class IconTestComponent { + public icon = 'circle'; + public size = '3x'; + public fixedWidth = false; +} diff --git a/src/modules/icon/icon.component.html b/src/modules/icon/icon.component.html new file mode 100644 index 000000000..16aefe755 --- /dev/null +++ b/src/modules/icon/icon.component.html @@ -0,0 +1,6 @@ + diff --git a/src/modules/icon/icon.component.spec.ts b/src/modules/icon/icon.component.spec.ts new file mode 100644 index 000000000..d77d278e7 --- /dev/null +++ b/src/modules/icon/icon.component.spec.ts @@ -0,0 +1,68 @@ +import { + ComponentFixture, + TestBed +} from '@angular/core/testing'; + +import { + SkyIconModule +} from './icon.module'; + +import { + IconTestComponent +} from './fixtures/icon.component.fixture'; + +import { + expect +} from '@blackbaud/skyux-builder/runtime/testing/browser'; + +describe('Icon component', () => { + let fixture: ComponentFixture; + let cmp: IconTestComponent; + let element: HTMLElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + IconTestComponent + ], + imports: [ + SkyIconModule + ] + }); + + fixture = TestBed.createComponent(IconTestComponent); + cmp = fixture.componentInstance as IconTestComponent; + element = fixture.nativeElement as HTMLElement; + }); + + it('should display an icon based on the given icon', () => { + fixture.detectChanges(); + expect(element.querySelector('.sky-icon')).toHaveCssClass('fa-circle'); + expect(element.querySelector('.sky-icon')).toHaveCssClass('fa-3x'); + expect(element.querySelector('.sky-icon')).not.toHaveCssClass('fa-fw'); + expect(element.querySelector('.sky-icon').getAttribute('aria-hidden')).toBe('true'); + expect(element.querySelector('.sky-icon').classList.length).toBe(4); + }); + + it('should display a different icon with a different size and a fixedWidth', () => { + cmp.icon = 'broom'; + cmp.size = '5x'; + cmp.fixedWidth = true; + fixture.detectChanges(); + expect(cmp.icon).toBe('broom'); + expect(element.querySelector('.sky-icon')).toHaveCssClass('fa-broom'); + expect(element.querySelector('.sky-icon')).toHaveCssClass('fa-5x'); + expect(element.querySelector('.sky-icon')).toHaveCssClass('fa-fw'); + expect(element.querySelector('.sky-icon').classList.length).toBe(5); + expect(element.querySelector('.sky-icon').getAttribute('aria-hidden')).toBe('true'); + }); + + it('should show an icon without optional inputs', () => { + cmp.icon = 'spinner'; + cmp.size = undefined; + cmp.fixedWidth = undefined; + fixture.detectChanges(); + expect(element.querySelector('.sky-icon')).toHaveCssClass('fa-spinner'); + expect(element.querySelector('.sky-icon').classList.length).toBe(3); + }); +}); diff --git a/src/modules/icon/icon.component.ts b/src/modules/icon/icon.component.ts new file mode 100644 index 000000000..07a9783a2 --- /dev/null +++ b/src/modules/icon/icon.component.ts @@ -0,0 +1,33 @@ +import { + ChangeDetectionStrategy, + Component, + Input +} from '@angular/core'; + +@Component({ + selector: 'sky-icon', + templateUrl: './icon.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SkyIconComponent { + @Input() + public icon: string; + + @Input() + public size: string; + + @Input() + public fixedWidth: boolean; + + public classList(): string[] { + let list: string[] = []; + list.push('fa-' + this.icon); + if (this.size) { + list.push('fa-' + this.size); + } + if (this.fixedWidth) { + list.push('fa-fw'); + } + return list; + } +} diff --git a/src/modules/icon/icon.module.ts b/src/modules/icon/icon.module.ts new file mode 100644 index 000000000..8901c16b2 --- /dev/null +++ b/src/modules/icon/icon.module.ts @@ -0,0 +1,24 @@ +import { + NgModule +} from '@angular/core'; + +import { + CommonModule +} from '@angular/common'; + +import { + SkyIconComponent +} from './icon.component'; + +@NgModule({ + declarations: [ + SkyIconComponent + ], + imports: [ + CommonModule + ], + exports: [ + SkyIconComponent + ] +}) +export class SkyIconModule { } diff --git a/src/modules/icon/index.ts b/src/modules/icon/index.ts new file mode 100644 index 000000000..4837ca018 --- /dev/null +++ b/src/modules/icon/index.ts @@ -0,0 +1,2 @@ +export * from './icon.component'; +export * from './icon.module';