Skip to content

Commit 65f8813

Browse files
Scott DoverScott Dover
authored andcommitted
chore: generalize webviewHtml creation
Signed-off-by: Scott Dover <[email protected]>
1 parent 46b254c commit 65f8813

File tree

3 files changed

+81
-41
lines changed

3 files changed

+81
-41
lines changed

client/src/panels/DataViewer.ts

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { Column } from "../connection/rest/api/compute";
1010
import { WebView } from "./WebviewManager";
1111

1212
class DataViewer extends WebView {
13-
private _uid: string;
14-
private _extensionUri: Uri;
1513
private _paginator: PaginatedResultSet<{ data: TableData; error?: Error }>;
1614
private _fetchColumns: () => Column[];
1715

@@ -21,22 +19,13 @@ class DataViewer extends WebView {
2119
paginator: PaginatedResultSet<{ data: TableData; error?: Error }>,
2220
fetchColumns: () => Column[],
2321
) {
24-
super();
25-
this._uid = uid;
26-
this._extensionUri = extensionUri;
22+
super(extensionUri, uid);
2723
this._paginator = paginator;
2824
this._fetchColumns = fetchColumns;
2925
}
3026

31-
public render(): WebView {
32-
const policies = [
33-
`default-src 'none';`,
34-
`font-src ${this.panel.webview.cspSource} data:;`,
35-
`img-src ${this.panel.webview.cspSource} data:;`,
36-
`script-src ${this.panel.webview.cspSource};`,
37-
`style-src ${this.panel.webview.cspSource};`,
38-
];
39-
const messages = {
27+
public l10nMessages() {
28+
return {
4029
"Ascending (add to sorting)": l10n.t("Ascending (add to sorting)"),
4130
Ascending: l10n.t("Ascending"),
4231
"Descending (add to sorting)": l10n.t("Descending (add to sorting)"),
@@ -45,33 +34,18 @@ class DataViewer extends WebView {
4534
"Remove sorting": l10n.t("Remove sorting"),
4635
Sort: l10n.t("Sort"),
4736
};
48-
this.panel.webview.html = `
49-
<!DOCTYPE html>
50-
<html lang="en">
51-
<head>
52-
<meta charset="UTF-8" />
53-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
54-
<meta http-equiv="Content-Security-Policy" content="${policies.join(
55-
" ",
56-
)}" />
57-
<link rel="stylesheet" href="${this.webviewUri(
58-
this._extensionUri,
59-
"DataViewer.css",
60-
)}">
61-
<title>${this._uid}</title>
62-
</head>
63-
<body>
64-
<div class="data-viewer"></div>
65-
<script type="module" src="${this.webviewUri(
66-
this._extensionUri,
67-
"DataViewer.js",
68-
)}"></script>
69-
<script type="application/json" id="l10n-messages">${JSON.stringify(messages)}</script>
70-
</body>
71-
</html>
72-
`;
37+
}
38+
39+
public styles() {
40+
return ["DataViewer.css"];
41+
}
42+
43+
public scripts() {
44+
return ["DataViewer.js"];
45+
}
7346

74-
return this;
47+
public body() {
48+
return `<div class="data-viewer"></div>`;
7549
}
7650

7751
public async processMessage(

client/src/panels/WebviewManager.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,71 @@ export abstract class WebView {
2525
private _disposables: Disposable[] = [];
2626
private _onDispose: () => void;
2727

28+
public constructor(
29+
protected readonly extensionUri: Uri,
30+
protected readonly title: string,
31+
) {}
32+
2833
set onDispose(disposeCallback: () => void) {
2934
this._onDispose = disposeCallback;
3035
}
3136

32-
abstract render(): WebView;
37+
abstract body(): string;
38+
abstract l10nMessages?(): Record<string, string>;
39+
abstract scripts?(): string[];
40+
abstract styles?(): string[];
41+
public render(): WebView {
42+
const policies = [
43+
`default-src 'none';`,
44+
`font-src ${this.panel.webview.cspSource} data:;`,
45+
`img-src ${this.panel.webview.cspSource} data:;`,
46+
`script-src ${this.panel.webview.cspSource};`,
47+
`style-src ${this.panel.webview.cspSource};`,
48+
];
49+
const styles = (this?.styles() || [])
50+
.map(
51+
(style) =>
52+
`<link rel="stylesheet" href="${this.webviewUri(
53+
this.extensionUri,
54+
style,
55+
)}">`,
56+
)
57+
.join("");
58+
const scripts = (this?.scripts() || [])
59+
.map(
60+
(script) =>
61+
`<script type="module" src="${this.webviewUri(
62+
this.extensionUri,
63+
script,
64+
)}"></script>`,
65+
)
66+
.join("");
67+
68+
this.panel.webview.html = `<!DOCTYPE html>
69+
<html lang="en">
70+
<head>
71+
<meta charset="UTF-8" />
72+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
73+
<meta http-equiv="Content-Security-Policy" content="${policies.join(
74+
" ",
75+
)}" />
76+
${styles}
77+
<title>${this.title}</title>
78+
</head>
79+
<body>
80+
${this.body()}
81+
${scripts}
82+
${
83+
this?.l10nMessages
84+
? `<script type="application/json" id="l10n-messages">${JSON.stringify(this?.l10nMessages())}</script>`
85+
: ""
86+
}
87+
</body>
88+
</html>`;
89+
90+
return this;
91+
}
92+
3393
abstract processMessage(event: Event): void;
3494

3595
public withPanel(webviewPanel: WebviewPanel): WebView {

client/src/webview/localize.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// `localize` provides functionality to use localized strings in react webviews
5+
// This expects for translations to be defined in a script block with an id of
6+
// `l10-messages`.
17
let localizedTerms = null;
28
const localize = (term: string) => {
39
if (!localizedTerms) {

0 commit comments

Comments
 (0)