Skip to content
This repository was archived by the owner on Dec 8, 2022. It is now read-only.

Commit 15a547c

Browse files
Fixed FOUC (#166)
* Added style loader to runtime * Added style loader to runtime * Created typescript module * Added types * Added space in attribute * Reduced timeout * Added unit tests * Fixed linting error * Updated skyux version * Updated to injectable * Implemented zones * Removed zones * Changed name of ready class * Returning a resolved promise * Made result optional
1 parent babe3f1 commit 15a547c

8 files changed

+111
-3
lines changed

lib/sky-pages-module-generator.js

+2
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ function getSource(skyAppConfig) {
3030
'SkyAppBootstrapper',
3131
'SkyAppConfig',
3232
'SkyAppWindowRef',
33+
'SkyAppStyleLoader',
3334
'SkyAuthTokenProvider'
3435
];
3536

3637
let runtimeProviders = [
3738
'SkyAppWindowRef',
39+
'SkyAppStyleLoader',
3840
'SkyAuthTokenProvider',
3941
`{
4042
provide: SkyAppConfig,

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@blackbaud/auth-client": "1.6.0",
4343
"@blackbaud/help-client": "1.0.1",
4444
"@ngtools/webpack": "1.2.14",
45+
"@types/fontfaceobserver": "0.0.5",
4546
"@types/jasmine": "2.5.40",
4647
"@types/node": "7.0.14",
4748
"angular2-template-loader": "0.5.0",

runtime/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from './bootstrapper';
44
export * from './config';
55
export * from './search-results-provider';
66
export * from './window-ref';
7+
export * from './style-loader';

runtime/style-loader.spec.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* tslint:disable:no-console */
2+
3+
import { SkyAppStyleLoader } from './style-loader';
4+
import * as FontFaceObserver from 'fontfaceobserver';
5+
6+
describe('Style loader', () => {
7+
it('should resolve a promise after loading fonts', (done) => {
8+
spyOn(FontFaceObserver.prototype, 'load').and.returnValue(
9+
Promise.resolve()
10+
);
11+
12+
const styleLoader = new SkyAppStyleLoader();
13+
14+
styleLoader.loadStyles()
15+
.then(() => {
16+
expect(styleLoader.isLoaded).toBe(true);
17+
done();
18+
});
19+
});
20+
21+
it('should pass errors to the resolve', (done) => {
22+
spyOn(Promise, 'all').and.callFake(() => {
23+
return Promise.reject(new Error('Fonts not loaded.'));
24+
});
25+
26+
const styleLoader = new SkyAppStyleLoader();
27+
28+
styleLoader.loadStyles()
29+
.then((response) => {
30+
expect(response.error.message).toBe('Fonts not loaded.');
31+
done();
32+
});
33+
});
34+
35+
it('should resolve the promise if the fonts are not loaded within the timeout', (done) => {
36+
const sampleObserver = new FontFaceObserver('SampleFont');
37+
38+
spyOn(Promise, 'all').and.callFake(() => {
39+
return sampleObserver.load(undefined, 1);
40+
});
41+
42+
const styleLoader = new SkyAppStyleLoader();
43+
44+
styleLoader.loadStyles()
45+
.then((result) => {
46+
expect(result.error).toBeDefined();
47+
done();
48+
});
49+
});
50+
});

runtime/style-loader.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as FontFaceObserver from 'fontfaceobserver';
2+
3+
import { Injectable } from '@angular/core';
4+
5+
@Injectable()
6+
export class SkyAppStyleLoader {
7+
public static readonly LOAD_TIMEOUT: number = 3000;
8+
public isLoaded: boolean = false;
9+
10+
public loadStyles(): Promise<any> {
11+
const fontAwesome = new FontFaceObserver('FontAwesome');
12+
const openSans = new FontFaceObserver('Open Sans');
13+
const oswald = new FontFaceObserver('Oswald');
14+
15+
return Promise
16+
.all([
17+
// Specify a character for FontAwesome since some browsers will fail to detect
18+
// when the font is loaded unless a known character with a different width
19+
// than the default is not specified.
20+
fontAwesome.load('\uf0fc', SkyAppStyleLoader.LOAD_TIMEOUT),
21+
openSans.load(undefined, SkyAppStyleLoader.LOAD_TIMEOUT),
22+
oswald.load(undefined, SkyAppStyleLoader.LOAD_TIMEOUT)
23+
])
24+
.then(() => {
25+
this.isLoaded = true;
26+
})
27+
.catch((error) => {
28+
// Errors loading the font should not stop the page from rendering.
29+
// Passing the error along in case the client wants to do something with it.
30+
return Promise.resolve({
31+
error: error
32+
});
33+
});
34+
}
35+
}

src/app/app.component.html

+4-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
<router-outlet></router-outlet>
1+
<div [ngClass]="{ 'skyux-app-loading': !isReady }">
2+
<router-outlet>
3+
</router-outlet>
4+
</div>

src/app/app.component.scss

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.skyux-app-loading {
2+
visibility: hidden;
3+
}

src/app/app.component.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import { BBHelp } from '@blackbaud/help-client';
2525
import {
2626
SkyAppConfig,
2727
SkyAppSearchResultsProvider,
28-
SkyAppWindowRef
28+
SkyAppWindowRef,
29+
SkyAppStyleLoader
2930
} from '@blackbaud/skyux-builder/runtime';
3031

3132
require('style-loader!@blackbaud/skyux/dist/css/sky.css');
@@ -70,12 +71,24 @@ function fixUpNav(nav: any, baseUrl: string) {
7071
templateUrl: './app.component.html'
7172
})
7273
export class AppComponent implements OnInit {
74+
public isReady = false;
75+
7376
constructor(
7477
private router: Router,
7578
private windowRef: SkyAppWindowRef,
7679
private config: SkyAppConfig,
80+
private styleLoader: SkyAppStyleLoader,
7781
@Optional() private searchProvider?: SkyAppSearchResultsProvider
78-
) { }
82+
) {
83+
this.styleLoader.loadStyles()
84+
.then((result?: any) => {
85+
this.isReady = true;
86+
87+
if (result && result.error) {
88+
console.log(result.error.message);
89+
}
90+
});
91+
}
7992

8093
public ngOnInit() {
8194
// Without this code, navigating to a new route doesn't cause the window to be

0 commit comments

Comments
 (0)