Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,59 @@ export class YourAppComponent {}
</div>
```

#### Extending `theme` via `NgxSkeletonLoaderModule.forRoot()`

> By default when using `NgxSkeletonLoaderModule.forRoot({ theme: /* ...list of CSS atributes */} })` the application is using this value as source of truth, overriding any local theming passed to `<ngx-skeleton-loader>` component via `[theme]` input. Check these steps in case you need to change this behaviour in your app

This method is also accepting the option of having a global theme and local theme inputs. You can enable it by passing `NgxSkeletonLoaderModule.forRoot({ theme: { extendsFromRoot: true, /* ...list of CSS atributes */} })` in your module. Quite simple, right? 😄

By using that configuration in yuor application, you should also be aware that:

- By default, every `<ngx-skeleton-loader>` component will use `theme` coming from `NgxSkeletonLoaderModule.forRoot()` as the source of truth
- If there's any CSS attribute on the component locally which overrides the CSS spec, it combines both themes, but overriding global CSS attributes in favor of local ones.

As an example:

```typescript
...
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
...

@NgModule({
declarations: [
YourAppComponent
],
imports: [
...
NgxSkeletonLoaderModule.forRoot({
theme: {
// Enabliong theme combination
extendsFromRoot: true,
// ... list of CSS theme attributes
height: '30px',
},
}),,
...
],
providers: [],
bootstrap: [YourAppComponent]
})

export class YourAppComponent {}

```

```html
<div class="item">
<ngx-skeleton-loader></ngx-skeleton-loader>
<!-- above line will produce a skeleton component using `height: 30px;`" -->
<ngx-skeleton-loader [theme]="{background: 'blue'}"></ngx-skeleton-loader>
<!-- above line will produce a skeleton component using `height: 30px; background: red;`" -->
<ngx-skeleton-loader [theme]="{height: '50px', background: 'red'}"></ngx-skeleton-loader>
<!-- above line will produce a skeleton component using `height: 50px; background: red;`" -->
</div>
```

## WAI-ARIA values

- loadingText - _default_ `Loading...`: attribute that defines the text value for `aria-valuetext` attribute. Defaults to "Loading..."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { InjectionToken } from '@angular/core';

export type NgxSkeletonLoaderConfigTheme = {
// It enforces a combination of `fromRoot` styles with component `styles` attribute
extendsFromRoot?: boolean;
// This is required since ngStyle is using `any` as well
// More details in https://angular.io/api/common/NgStyle
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class NgxSkeletonLoaderComponent implements OnInit, AfterViewInit, OnDest
// eslint-disable-next-line @typescript-eslint/no-explicit-any
items: Array<any>;

constructor(@Inject(NGX_SKELETON_LOADER_CONFIG) @Optional() config?: NgxSkeletonLoaderConfig) {
constructor(@Inject(NGX_SKELETON_LOADER_CONFIG) @Optional() private config?: NgxSkeletonLoaderConfig) {
const {
appearance = 'line',
animation = 'progress',
Expand Down Expand Up @@ -123,6 +123,11 @@ export class NgxSkeletonLoaderComponent implements OnInit, AfterViewInit, OnDest
}
this.appearance = '';
}

if (Boolean(this.config?.theme?.extendsFromRoot) && this.theme !== null) {
// Shows error message only in Development
this.theme = { ...this.config!.theme, ...this.theme };
}
}

ngOnChanges(changes: SimpleChanges) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { NgxSkeletonLoaderModule } from './ngx-skeleton-loader.module';
template: `
<div>
<div class="skeletons-defaults">
<ngx-skeleton-loader></ngx-skeleton-loader>
<ngx-skeleton-loader [theme]="{ width: '70px' }"></ngx-skeleton-loader>
</div>
<div class="skeletons-extended-theme">
<ngx-skeleton-loader [theme]="{ width: '100px', background: 'blue' }"></ngx-skeleton-loader>
</div>
</div>
`,
Expand All @@ -19,20 +22,44 @@ describe('NgxSkeletonLoaderModule method', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let fixture: any;

beforeEach(
waitForAsync(() => {
spyOn(console, 'error');
spyOn(console, 'log');
spyOn(console, 'warn');
spyOn(console, 'info');
fixture = TestBed.configureTestingModule({
imports: [NgxSkeletonLoaderModule.forRoot({ appearance: 'circle', count: 3 })],
declarations: [ContainerComponent],
providers: [{ provide: PLATFORM_ID, useValue: 'browser' }],
}).createComponent(ContainerComponent);
fixture.detectChanges();
}),
);
beforeEach(waitForAsync(() => {
spyOn(console, 'error');
spyOn(console, 'log');
spyOn(console, 'warn');
spyOn(console, 'info');
fixture = TestBed.configureTestingModule({
imports: [
NgxSkeletonLoaderModule.forRoot({
appearance: 'circle',
count: 3,
theme: {
extendsFromRoot: true,
background: 'red',
},
}),
],
declarations: [ContainerComponent],
providers: [{ provide: PLATFORM_ID, useValue: 'browser' }],
}).createComponent(ContainerComponent);
fixture.detectChanges();
}));

describe('When #forRoot receives a `theme`', () => {
it('should render skeleton extending theme styles from root and overriding config theming in favour of local theme if local config has any similar CSS attribute', () => {
const skeletonWithTheming = fixture.nativeElement.querySelector(
'.skeletons-extended-theme .skeleton-loader.circle',
).attributes as NamedNodeMap;

expect((skeletonWithTheming.getNamedItem('style') as Attr).value).toBe('background: blue; width: 100px;');
});

it('should render skeleton with styles extending/combining theme styles from root if CSS attributes are not similar', () => {
const skeletonWithTheming = fixture.nativeElement.querySelector('.skeletons-defaults .skeleton-loader.circle')
.attributes as NamedNodeMap;

expect((skeletonWithTheming.getNamedItem('style') as Attr).value).toBe('background: red; width: 70px;');
});
});

it('should render the component properly using given forRoot() config', () => {
expect(fixture.nativeElement.querySelectorAll('.skeletons-defaults .skeleton-loader.circle').length).toBe(3);
Expand Down
Loading