Skip to content

Commit 516eb63

Browse files
authored
fix: support for prerequisites (#486)
1 parent e39c937 commit 516eb63

File tree

3 files changed

+167
-90
lines changed

3 files changed

+167
-90
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// COPIED FROM https://github.com/patternfly/patternfly-quickstarts/blob/main/packages/dev/src/quickstarts-data/asciidoc/procedure-parser.ts until it's published to NPMJS
2+
/* eslint-disable */
3+
4+
import { QuickStart, QuickStartTask } from '@patternfly/quickstarts';
5+
6+
export const ProcQuickStartParser = (
7+
quickStart: QuickStart & {
8+
spec: {
9+
tasks: undefined | QuickStartTask[] | string[];
10+
};
11+
},
12+
environmentVariables?: { [name: string]: string },
13+
) => {
14+
const replaceEnvironmentVariables = (s: string | undefined) =>
15+
s?.replace(/\${(\w+)}/, (substring, name) => {
16+
return environmentVariables ? ([name] ? environmentVariables[name] : substring) : substring;
17+
});
18+
19+
quickStart.spec.tasks = quickStart.spec.tasks?.map((task: QuickStartTask | string, index) => {
20+
let proc: string;
21+
let answer: QuickStartTask;
22+
if (typeof task === 'string') {
23+
proc = task;
24+
answer = {};
25+
} else {
26+
// @ts-ignore
27+
proc = task.proc;
28+
answer = task;
29+
// @ts-ignore
30+
delete task.proc;
31+
}
32+
33+
let description = '',
34+
procedure,
35+
verification,
36+
title,
37+
summaryFailed,
38+
success,
39+
reviewFailed: string | undefined,
40+
prerequisites;
41+
if (proc) {
42+
const taskDOM = document.createElement('div');
43+
taskDOM.innerHTML = proc;
44+
45+
// remove the screencapture images
46+
taskDOM.querySelectorAll('.imageblock.screencapture').forEach((node) => {
47+
node.parentElement?.removeChild(node);
48+
});
49+
50+
title = taskDOM
51+
.querySelector('h1:first-child,h2:first-child,h3:first-child,h4:first-child,h5:first-child')
52+
?.innerHTML.trim();
53+
let sectionBody = taskDOM.querySelector('.sectionbody');
54+
if (!sectionBody?.hasChildNodes()) {
55+
// possibly in other templates, where we want to look for article
56+
sectionBody = taskDOM.querySelector('article');
57+
}
58+
if (sectionBody) {
59+
for (let i = 0; i < sectionBody.children.length || 0; i++) {
60+
/**
61+
child typically looks like:
62+
63+
<div class="paragraph|olist|ulist|admonitionblock">
64+
<div class="title">Procedure|Prerequisites|Verification|Note|Warning</div>
65+
<ol|ul class="arabic">
66+
<li>
67+
<li>...
68+
</ol|ul>
69+
</div>
70+
71+
And the below code extracts the <ol> or <ul>
72+
Except for when there is no <div class="title|heading"/>, then the description is extracted
73+
in the else if below
74+
*/
75+
const child = sectionBody.children.item(i);
76+
// find the title
77+
const sectionTitle = child?.querySelector('.heading,.title');
78+
// should this section be assigned to a specific section
79+
const sectionTitleText = sectionTitle?.textContent?.trim();
80+
const isKnownSection = ['Procedure', 'Verification', 'Prerequisites'].includes(
81+
sectionTitle?.textContent?.trim(),
82+
);
83+
if (isKnownSection) {
84+
switch (sectionTitleText) {
85+
case 'Procedure':
86+
procedure = child?.querySelector(':not(.heading):not(.title)')?.outerHTML.trim();
87+
break;
88+
case 'Verification':
89+
verification = child?.querySelector(':not(.heading):not(.title)')?.outerHTML.trim();
90+
break;
91+
case 'Prerequisites':
92+
prerequisites = child
93+
?.querySelector(':not(.heading):not(.title)')
94+
?.outerHTML.trim();
95+
break;
96+
}
97+
} else if (!procedure) {
98+
// Otherwise if it comes before a procedure it's part of the description
99+
description = description + child?.outerHTML.trim();
100+
}
101+
}
102+
}
103+
success = taskDOM.querySelector('.qs-summary.success')?.innerHTML.trim();
104+
reviewFailed = taskDOM.querySelector('.qs-review.failed')?.innerHTML.trim();
105+
summaryFailed = taskDOM.querySelector('.qs-summary.failed')?.innerHTML.trim();
106+
}
107+
108+
answer.title = replaceEnvironmentVariables(answer.title || title);
109+
answer.description = replaceEnvironmentVariables(
110+
answer.description || `${description} ${prerequisites || ''} ${procedure}`,
111+
);
112+
answer.review = answer.review || {};
113+
answer.review.instructions = replaceEnvironmentVariables(
114+
answer.review?.instructions || verification || 'Have you completed these steps?',
115+
);
116+
answer.review.failedTaskHelp = replaceEnvironmentVariables(
117+
answer.review.failedTaskHelp ||
118+
reviewFailed ||
119+
'This task isn’t verified yet. Try the task again.',
120+
);
121+
answer.summary = answer.summary || {};
122+
answer.summary.success = replaceEnvironmentVariables(
123+
answer.summary.success || success || 'You have completed this task!',
124+
);
125+
answer.summary.failed = replaceEnvironmentVariables(
126+
answer.summary.failed || summaryFailed || 'Try the steps again.',
127+
);
128+
return answer;
129+
});
130+
return quickStart;
131+
};

.build/src/app/procedure-parser.ts

+34-88
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,44 @@
1-
import {QuickStart, QuickStartTask} from "@patternfly/quickstarts";
1+
/* eslint-disable */
22

3-
export type GuidesQuickStart = QuickStart & {
4-
metadata?: {
5-
annotations?: {
6-
draft?: boolean,
7-
order?: number
8-
}
9-
}
10-
spec: {
11-
tasks: undefined | QuickStartTask[] | string[]
12-
}
13-
}
3+
import { QuickStart, QuickStartTask } from '@patternfly/quickstarts';
4+
import {ProcQuickStartParser} from "@app/patternfly-procedure-parser";
145

15-
16-
export const ProcQuickStartParser = (
17-
quickStart: GuidesQuickStart,
6+
export const ProcQuickStartParserWithImageSupport = (
7+
quickStart: QuickStart & {
8+
spec: {
9+
tasks: undefined | QuickStartTask[] | string[];
10+
};
11+
},
1812
basePath: string,
19-
environmentVariables?: { [name: string]: string }
13+
environmentVariables?: { [name: string]: string },
2014
) => {
21-
const replaceEnvironmentVariables = (s: string | undefined) =>
22-
s?.replace(/\${(\w+)}/, (substring, name) => {
23-
return environmentVariables ? [name]
24-
? environmentVariables[name]
25-
: substring : substring;
26-
});
2715

28-
quickStart.spec.tasks = quickStart.spec.tasks?.map((task: QuickStartTask | string, index) => {
29-
let proc: string;
30-
let answer: QuickStartTask;
31-
if (typeof task === "string") {
32-
proc = task;
33-
answer = {};
34-
} else {
35-
proc = task["proc"]
36-
answer = task;
37-
delete task["proc"];
38-
}
16+
// Use the upstream parser
17+
const resource = ProcQuickStartParser(quickStart, environmentVariables);
3918

40-
let procedure, verification, title, summaryFailed, success, reviewFailed: string | undefined;
41-
let description = "";
42-
if (proc) {
43-
const parser = new DOMParser();
44-
proc = proc.replace("<img src=\"\./images", "<img src=\"" + basePath + "/images");
45-
const taskDOM = parser.parseFromString(proc, 'text/html');
19+
// add image path fixing
20+
function fixImagePath(str: string): string;
21+
function fixImagePath(str: undefined | string): undefined | string;
22+
function fixImagePath (str: string | undefined): string | undefined {
23+
return str === undefined ? undefined :str.replace("<img src=\"\./images", "<img src=\"" + basePath + "/images");
24+
}
4625

47-
// remove the screencapture images
48-
taskDOM.querySelectorAll(".imageblock.screencapture").forEach(node => {
49-
node.parentElement?.removeChild(node);
50-
});
26+
resource.spec.tasks = resource.spec.tasks?.map(task => {
5127

52-
title = taskDOM.querySelector("h1:first-child,h2:first-child,h3:first-child,h4:first-child,h5:first-child")?.innerHTML.trim();
53-
let sectionBody = taskDOM.querySelector(".sectionbody");
54-
if (!sectionBody?.hasChildNodes()) {
55-
// possibly in other templates, where we want to look for article
56-
sectionBody = taskDOM.querySelector("article");
57-
}
58-
if (sectionBody) {
59-
for (let i = 0; i < sectionBody.children.length || 0; i++) {
60-
const child = sectionBody.children.item(i);
61-
// find the title
62-
const title = child?.querySelector(".heading,.title");
63-
if (title) {
64-
switch (title?.textContent?.trim()) {
65-
case "Procedure":
66-
procedure = child?.querySelector(":not(.heading):not(.title)")?.outerHTML.trim();
67-
break;
68-
case "Verification":
69-
verification = child?.querySelector(":not(.heading):not(.title)")?.outerHTML.trim();
70-
break;
71-
}
72-
} else if (!procedure) {
73-
// Otherwise if it comes before a procedure it's part of the description
74-
description += child?.innerHTML.trim();
75-
}
76-
}
77-
}
78-
success = taskDOM.querySelector(".qs-summary.success")?.innerHTML.trim();
79-
reviewFailed = taskDOM.querySelector(".qs-review.failed")?.innerHTML.trim();
80-
summaryFailed = taskDOM.querySelector(".qs-summary.failed")?.innerHTML.trim();
28+
task.description = fixImagePath(task.description);
29+
if (task.summary !== undefined) {
30+
task.summary.success = fixImagePath(task.summary.success);
31+
task.summary.failed = fixImagePath(task.summary.failed);
8132
}
82-
83-
84-
answer.title = replaceEnvironmentVariables(answer.title || title)
85-
answer.description = replaceEnvironmentVariables(answer.description || `${description} ${procedure}`);
86-
answer.review = answer.review || {};
87-
answer.review.instructions = replaceEnvironmentVariables(answer.review?.instructions || verification || "Have you completed these steps?")
88-
answer.review.failedTaskHelp = replaceEnvironmentVariables(answer.review.failedTaskHelp || reviewFailed || "This task isn’t verified yet. Try the task again.");
89-
answer.summary = answer.summary || {};
90-
answer.summary.success = replaceEnvironmentVariables(answer.summary.success ||
91-
success
92-
|| "You have completed this task!");
93-
answer.summary.failed = replaceEnvironmentVariables(answer.summary.failed || summaryFailed
94-
|| "Try the steps again.");
95-
return answer;
33+
if (task.review !== undefined) {
34+
task.review.failedTaskHelp = fixImagePath(task.review.failedTaskHelp);
35+
task.review.instructions = fixImagePath(task.review.instructions);
36+
}
37+
task.title = fixImagePath(task.title);
38+
return task;
9639
});
97-
return quickStart;
98-
};
40+
resource.spec.description = fixImagePath(resource.spec.description)
41+
resource.spec.introduction = fixImagePath(resource.spec.introduction)
42+
resource.spec.conclusion = fixImagePath(resource.spec.conclusion);
43+
return resource;
44+
};

.build/src/app/quickstartLoader.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GuidesQuickStart, ProcQuickStartParser } from "@app/procedure-parser";
1+
import { GuidesQuickStart, ProcQuickStartParserWithImageSupport } from "@app/procedure-parser";
22
import React, { useState, useEffect, FunctionComponent } from "react";
33
import { QuickStart } from "@patternfly/quickstarts";
44
import {useAssets} from "@rhoas/app-services-ui-shared";
@@ -35,7 +35,7 @@ export const loadJSONQuickStarts = async (
3535
}
3636
return true;
3737
})
38-
.map((content) => ProcQuickStartParser(content, basePath));
38+
.map((content) => ProcQuickStartParserWithImageSupport(content, basePath));
3939
};
4040

4141
export interface QuickStartLoaderProps {

0 commit comments

Comments
 (0)