Skip to content

Commit 71f8e21

Browse files
authored
feat(scullycontent): add a detectable Id to scully rendering errors (#230)
* feat(scullycontent): add a detectable Id to scully rendering errors This will add a `id="___scully-parsing-error___"` to scully rendering errors, so that it will be easier to detect those during CI/CD steps. closes #228 * Update scully-content.component.ts fix typo
1 parent bf046c7 commit 71f8e21

2 files changed

Lines changed: 57 additions & 12 deletions

File tree

projects/scullyio/ng-lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@scullyio/ng-lib",
3-
"version": "0.0.13",
3+
"version": "0.0.14",
44
"repository": {
55
"type": "GIT",
66
"url": "https://github.com/scullyio/scully/tree/master/projects/scullyio/ng-lib"

projects/scullyio/ng-lib/src/lib/scully-content/scully-content.component.ts

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
3939
@Input() type = 'MD';
4040

4141
elm = this.elmRef.nativeElement as HTMLElement;
42-
mutationSubscription: Subscription;
42+
// mutationSubscription: Subscription;
4343
routes = this.srs.available$.pipe(take(1)).toPromise();
4444

4545
constructor(
@@ -53,10 +53,15 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
5353
/** make sure the idle-check is loaded. */
5454
this.idle.init();
5555
if (this.elm) {
56+
/** this will only fire in a browser environment */
5657
this.handlePage();
5758
}
5859
}
5960

61+
/**
62+
* Loads the static content from scully into the view
63+
* Will fetch the content from sibling links with xmlHTTPrequest
64+
*/
6065
private async handlePage() {
6166
const template = document.createElement('template');
6267
// tslint:disable-next-line: no-string-literal
@@ -66,17 +71,25 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
6671
template.innerHTML = window['scullyContent'];
6772
} else {
6873
const curPage = location.href;
74+
/**
75+
* NOTE
76+
* when updateting the texts for the errors, make sure you leave the
77+
* `id="___scully-parsing-error___"`
78+
* in there. That way users can detect rendering errors in their CI
79+
* on a reliable way.
80+
*/
6981
await fetchHttp(curPage, 'text')
7082
.then((html: string) => {
7183
try {
7284
template.innerHTML = html.split(scullyBegin)[1].split(scullyEnd)[0];
7385
} catch (e) {
74-
template.innerHTML = `<h2>Sorry, could not parse static page content</h2>
86+
template.innerHTML = `<h2 id="___scully-parsing-error___">Sorry, could not parse static page content</h2>
7587
<p>This might happen if you are not using the static generated pages.</p>`;
7688
}
7789
})
7890
.catch(e => {
79-
template.innerHTML = '<h2>Sorry, could not load static page content</h2>';
91+
template.innerHTML =
92+
'<h2 id="___scully-parsing-error___">Sorry, could not load static page content</h2>';
8093
console.error('problem during loading static scully content', e);
8194
});
8295
}
@@ -86,24 +99,45 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
8699
parent.insertBefore(begin, this.elm);
87100
parent.insertBefore(template.content, this.elm);
88101
parent.insertBefore(end, this.elm);
102+
/** upgrade all hrefs to simulated routelinks */
89103
document.querySelectorAll('[href]').forEach(this.upgradeToRoutelink.bind(this));
90104
}
91105

106+
/**
107+
* upgrade a **href** attributes to links that respect the Angular router
108+
* and don't do a full page reload. Only works on links that are found in the
109+
* Scully route config file.
110+
* @param elm the element containing the **hrefs**
111+
*/
92112
async upgradeToRoutelink(elm: HTMLElement) {
93113
const routes = await this.routes;
94114
const lnk = elm.getAttribute('href').toLowerCase();
95115
const route = routes.find(r => r.route.toLowerCase() === lnk);
116+
/** only upgrade routes known by scully. */
96117
if (lnk && route) {
97-
elm.onclick = (ev: MouseEvent) => {
118+
elm.onclick = async (ev: MouseEvent) => {
98119
const splitRoute = route.route.split(`/`);
99120
const curSplit = location.pathname.split('/');
100121
// loose last "part" of route
101122
curSplit.pop();
102123

103124
ev.preventDefault();
104-
this.router.navigate(splitRoute).catch(e => console.error('routing error', e));
125+
const routed = await this.router.navigate(splitRoute).catch(e => {
126+
console.error('routing error', e);
127+
return false;
128+
});
129+
if (!routed) {
130+
return;
131+
}
132+
/** delete the content, as it is now out of date! */
133+
window['scullyContent'] = undefined;
105134
/** check for the same route with different "data", and NOT a level higher (length) */
106135
if (curSplit.every((part, i) => splitRoute[i] === part) && splitRoute.length > curSplit.length) {
136+
/**
137+
* as Angular doesn't destroy the component if we stay on the same page,
138+
* we have to manually delete old content. Also we need to kick of loading
139+
* the new content. handlePage() takes care of that.
140+
*/
107141
setTimeout(() => {
108142
const p = this.elm.parentElement;
109143
let cur = findComments(p, 'scullyContent-begin')[0] as HTMLElement;
@@ -114,21 +148,26 @@ export class ScullyContentComponent implements OnInit, OnDestroy {
114148
cur = next;
115149
} while (next && next !== this.elm);
116150
// tslint:disable-next-line: no-string-literal
117-
window['scullyContent'] = undefined;
118151
this.handlePage();
119-
}, 20);
152+
}, 10); // a small delay, so we are sure the angular parts in the page are settled enough
120153
}
121154
};
122155
}
123156
}
124157

125158
ngOnDestroy() {
126-
if (this.mutationSubscription) {
127-
this.mutationSubscription.unsubscribe();
128-
}
159+
// if (this.mutationSubscription) {
160+
// this.mutationSubscription.unsubscribe();
161+
// }
129162
}
130163
}
131164

165+
/**
166+
* Returns an observable that fires a mutation when the domMutationObserves does that.
167+
* if flattens the mutations to make handling easier, so you only get 1 mutationRecord at a time.
168+
* @param elm the elm to obse with a mutationObserver
169+
* @param config the config for the mutationobserver
170+
*/
132171
export function fromMutationObserver(
133172
elm: HTMLElement,
134173
config: MutationObserverInit
@@ -140,6 +179,12 @@ export function fromMutationObserver(
140179
});
141180
}
142181

182+
/**
183+
* Returns an array of nodes coninting all the html comments in the element.
184+
* When a searchText is given this is narrowed down to only comments that contian this text
185+
* @param rootElem Element to search nto
186+
* @param searchText optional string that needs to be in a HTML comment
187+
*/
143188
function findComments(rootElem: HTMLElement, searchText?: string) {
144189
const comments = [];
145190
// Fourth argument, which is actually obsolete according to the DOM4 standard, seems required in IE 11
@@ -150,7 +195,7 @@ function findComments(rootElem: HTMLElement, searchText?: string) {
150195
acceptNode: node => {
151196
// Logic to determine whether to accept, reject or skip node
152197
// In this case, only accept nodes that have content
153-
// other than whitespace
198+
// that is containing our searchText, by rejecting any other nodes.
154199
if (searchText && node.nodeValue && !node.nodeValue.includes(searchText)) {
155200
return NodeFilter.FILTER_REJECT;
156201
}

0 commit comments

Comments
 (0)