Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor module to include a configuration when calling #forRoot #77

Merged
merged 13 commits into from
Jun 10, 2021
Merged
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ After that, you can use the `ngx-skeleton-loader` components in your templates,

Also, you can import the module in your app by calling `NgxSkeletonLoaderModule.forRoot()` when adding it. So it will be available across your Angular application.

Importing the module this way also allows you to globally configure the default values for the `ngx-skeleton-loader` components in your application, in case you need some different default values for your app.

```typescript
...
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
Expand All @@ -98,7 +100,7 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
],
imports: [
...
NgxSkeletonLoaderModule.forRoot(),
NgxSkeletonLoaderModule.forRoot({ animation: 'pulse', loadingText: 'This item is actually loading...' }),
...
],
providers: [],
Expand All @@ -112,6 +114,7 @@ export class YourAppComponent {}
```html
<div class="item">
<ngx-skeleton-loader count="5" appearance="circle"></ngx-skeleton-loader>
<!-- above line will produce the rendering of 5 circles with the pulse animation and the aria-valuetext attribute set with "This item is actually loading..." -->
</div>
```

Expand All @@ -125,7 +128,8 @@ You can also define which appearance want to use in your skeleton loader by pass

### Options

- `''` - _default_: it will use it `''` as appearance. At the end, it will render like a line, but line is not a expected appearance to be passed;
- `''` - _default_: it will use it `''` as appearance. At the end, it will render like a line;
- `line`: it will render like a line. This is the same behavior as passing an empty string;
- `circle`: it will use `circle` as appearance. Great for avatar skeletons, for example :);

## Animations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { InjectionToken } from '@angular/core';

export type NgxSkeletonLoaderConfigTheme = {
// This is required since ngStyle is using `any` as well
// More details in https://angular.io/api/common/NgStyle
// tslint:disable-next-line: no-any
[k: string]: any;
} | null;

export interface NgxSkeletonLoaderConfig {
HunteRoi marked this conversation as resolved.
Show resolved Hide resolved
appearance: 'circle' | 'line' | '';
animation: 'progress' | 'progress-dark' | 'pulse' | 'false' | false;
theme: NgxSkeletonLoaderConfigTheme;
loadingText: string;
count: number;
}

export const NGX_SKELETON_LOADER_CONFIG = new InjectionToken<NgxSkeletonLoaderConfig>('ngx-skeleton-loader.config');
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, PLATFORM_ID } from '@angular/core';
import { async as waitForAsync, TestBed } from '@angular/core/testing';
import { NGX_SKELETON_LOADER_CONFIG } from './ngx-skeleton-loader-config.types';

import { NgxSkeletonLoaderComponent } from './ngx-skeleton-loader.component';

Expand Down Expand Up @@ -59,6 +60,10 @@ import { NgxSkeletonLoaderComponent } from './ngx-skeleton-loader.component';
<ngx-skeleton-loader appearance="circle" [theme]="{ width: '70px', height: '70px', 'border-radius': '10px' }">
</ngx-skeleton-loader>
</div>

<div class="skeletons-with-provided-config">
<ngx-skeleton-loader></ngx-skeleton-loader>
</div>
</div>
`,
})
Expand Down Expand Up @@ -103,17 +108,17 @@ describe('NgxSkeletonLoaderComponent', () => {
it('should console errors if `appearance` is an invalid option and is running in development mode', () => {
expect(console.error).toHaveBeenCalledWith(
// tslint:disable-next-line: max-line-length
`\`NgxSkeletonLoaderComponent\` need to receive 'appearance' as: circle or empty string. Forcing default to "''".`,
`\`NgxSkeletonLoaderComponent\` need to receive 'appearance' as: circle or line or empty string. Forcing default to "''".`,
);
});

it('should add all relevant WAI-ARIA `aria-` attributes in all ngx-skeleton-loader', () => {
expect(fixture.nativeElement.querySelectorAll('[aria-busy="true"]').length).toBe(14);
expect(fixture.nativeElement.querySelectorAll('[aria-valuemin="0"]').length).toBe(14);
expect(fixture.nativeElement.querySelectorAll('[aria-valuemax="100"]').length).toBe(14);
expect(fixture.nativeElement.querySelectorAll('[aria-valuetext]').length).toBe(14);
expect(fixture.nativeElement.querySelectorAll('[role="progressbar"]').length).toBe(14);
expect(fixture.nativeElement.querySelectorAll('[tabindex="0"]').length).toBe(14);
expect(fixture.nativeElement.querySelectorAll('[aria-busy="true"]').length).toBe(15);
expect(fixture.nativeElement.querySelectorAll('[aria-valuemin="0"]').length).toBe(15);
expect(fixture.nativeElement.querySelectorAll('[aria-valuemax="100"]').length).toBe(15);
expect(fixture.nativeElement.querySelectorAll('[aria-valuetext]').length).toBe(15);
expect(fixture.nativeElement.querySelectorAll('[role="progressbar"]').length).toBe(15);
expect(fixture.nativeElement.querySelectorAll('[tabindex="0"]').length).toBe(15);
});

it('should use progress as default animation if `animation` is not passed as component attribute', () => {
Expand Down Expand Up @@ -190,3 +195,25 @@ describe('NgxSkeletonLoaderComponent', () => {
});
});
});

describe('NgxSkeletonLoaderComponent2', () => {
willmendesneto marked this conversation as resolved.
Show resolved Hide resolved
// tslint:disable-next-line: no-any
let fixture: any;
HunteRoi marked this conversation as resolved.
Show resolved Hide resolved

beforeEach(
waitForAsync(() => {
fixture = TestBed.configureTestingModule({
declarations: [ContainerComponent, NgxSkeletonLoaderComponent],
providers: [
{ provide: PLATFORM_ID, useValue: 'browser' },
{ provide: NGX_SKELETON_LOADER_CONFIG, useValue: { appearance: 'circle', count: 3 }}
],
}).createComponent(ContainerComponent);
fixture.detectChanges();
}),
);

it('should render skeleton with the provided config', () => {
expect(fixture.nativeElement.querySelectorAll('.skeletons-with-provided-config .loader.circle').length).toBe(3);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ import {
ChangeDetectionStrategy,
OnChanges,
SimpleChanges,
Optional,
Inject,
} from '@angular/core';
import { start, end } from 'perf-marks/marks';
import {
NgxSkeletonLoaderConfig,
NgxSkeletonLoaderConfigTheme,
NGX_SKELETON_LOADER_CONFIG,
} from './ngx-skeleton-loader-config.types';

@Component({
selector: 'ngx-skeleton-loader',
Expand All @@ -24,24 +31,32 @@ export class NgxSkeletonLoaderComponent implements OnInit, AfterViewInit, OnDest
static ngAcceptInputType_animation: boolean | string;

@Input()
count = 1;
count: NgxSkeletonLoaderConfig['count'];
HunteRoi marked this conversation as resolved.
Show resolved Hide resolved

@Input()
loadingText = 'Loading...';
loadingText: NgxSkeletonLoaderConfig['loadingText'];

@Input()
appearance: 'circle' | '' = '';
appearance: NgxSkeletonLoaderConfig['appearance'];

@Input()
animation: 'progress' | 'progress-dark' | 'pulse' | 'false' | false = 'progress';
animation: NgxSkeletonLoaderConfig['animation'];

// This is required since ngStyle is using `any` as well
// More details in https://angular.io/api/common/NgStyle
// tslint:disable-next-line: no-any
@Input() theme: { [k: string]: any } = {};
@Input()
theme: NgxSkeletonLoaderConfigTheme;

// tslint:disable-next-line: no-any
items: Array<any> = [];
items: Array<any>;
HunteRoi marked this conversation as resolved.
Show resolved Hide resolved

constructor(@Inject(NGX_SKELETON_LOADER_CONFIG) @Optional() config?: NgxSkeletonLoaderConfig) {
const { appearance = 'line', animation = 'progress', theme = null, loadingText = 'Loading...', count = 1 } = config || {};
this.appearance = appearance;
this.animation = animation;
this.theme = theme;
this.loadingText = loadingText;
this.count = count;
this.items = [];
}

ngOnInit() {
start('NgxSkeletonLoader:Rendered');
Expand All @@ -61,7 +76,6 @@ export class NgxSkeletonLoaderComponent implements OnInit, AfterViewInit, OnDest
}
this.count = 1;
}

this.items.length = this.count;

const allowedAnimations = ['progress', 'progress-dark', 'pulse', 'false'];
Expand All @@ -77,11 +91,11 @@ export class NgxSkeletonLoaderComponent implements OnInit, AfterViewInit, OnDest
this.animation = 'progress';
}

if (['circle', ''].indexOf(String(this.appearance)) === -1) {
if (['circle', 'line', ''].indexOf(String(this.appearance)) === -1) {
// Shows error message only in Development
if (isDevMode()) {
console.error(
`\`NgxSkeletonLoaderComponent\` need to receive 'appearance' as: circle or empty string. Forcing default to "''".`,
`\`NgxSkeletonLoaderComponent\` need to receive 'appearance' as: circle or line or empty string. Forcing default to "''".`,
);
}
this.appearance = '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { NgxSkeletonLoaderModule } from './ngx-skeleton-loader.module';
})
class ContainerComponent {}

describe('NgxSkeletonLoaderModule.forRoot() method', () => {
describe('NgxSkeletonLoaderModule method', () => {
// tslint:disable-next-line: no-any
let fixture: any;

Expand Down Expand Up @@ -55,3 +55,21 @@ describe('NgxSkeletonLoaderModule.forRoot() method', () => {
expect(console.info).toHaveBeenCalledTimes(0);
});
});

describe('NgxSkeletonLoaderModule.forRoot() method', () => {
HunteRoi marked this conversation as resolved.
Show resolved Hide resolved
// tslint:disable-next-line: no-any
let fixture: any;

beforeEach(waitForAsync(() => {
fixture = TestBed.configureTestingModule({
imports: [NgxSkeletonLoaderModule.forRoot({ appearance: 'circle', count: 3 })],
declarations: [ContainerComponent],
providers: [{ provide: PLATFORM_ID, useValue: 'browser' }],
}).createComponent(ContainerComponent);
fixture.detectChanges();
}));

it('should render the component properly using given forRoot() config', () => {
expect(fixture.nativeElement.querySelectorAll('.skeletons-defaults .loader.circle').length).toBe(3);
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { NgxSkeletonLoaderComponent } from './ngx-skeleton-loader.component';
import { CommonModule } from '@angular/common';

import { NgxSkeletonLoaderComponent } from './ngx-skeleton-loader.component';
import { NgxSkeletonLoaderConfig, NGX_SKELETON_LOADER_CONFIG } from './ngx-skeleton-loader-config.types';

@NgModule({
declarations: [NgxSkeletonLoaderComponent],
imports: [CommonModule],
exports: [NgxSkeletonLoaderComponent],
})

export class NgxSkeletonLoaderModule {
static forRoot(): ModuleWithProviders<NgxSkeletonLoaderModule> {
static forRoot(config?: Partial<NgxSkeletonLoaderConfig>): ModuleWithProviders<NgxSkeletonLoaderModule> {
return {
ngModule: NgxSkeletonLoaderModule,
providers: [{ provide: NGX_SKELETON_LOADER_CONFIG, useValue: config }],
};
}
}
1 change: 1 addition & 0 deletions projects/ngx-skeleton-loader/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

export * from './lib/ngx-skeleton-loader.component';
export * from './lib/ngx-skeleton-loader.module';
export * from './lib/ngx-skeleton-loader-config.types';