Skip to content

Commit

Permalink
Use new toolbar machinery for html viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
fcollonval committed Jul 5, 2021
1 parent 473479a commit 420d0f7
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 41 deletions.
5 changes: 4 additions & 1 deletion packages/htmlviewer-extension/package.json
Expand Up @@ -24,6 +24,7 @@
"style": "style/index.css",
"files": [
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
"schema/*.json",
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
"style/index.js"
],
Expand All @@ -38,6 +39,7 @@
"@jupyterlab/apputils": "^3.1.0-beta.0",
"@jupyterlab/docregistry": "^3.1.0-beta.0",
"@jupyterlab/htmlviewer": "^3.1.0-beta.0",
"@jupyterlab/settingregistry": "^3.1.0-beta.0",
"@jupyterlab/translation": "^3.1.0-beta.0",
"@jupyterlab/ui-components": "^3.1.0-beta.0"
},
Expand All @@ -49,7 +51,8 @@
"access": "public"
},
"jupyterlab": {
"extension": true
"extension": true,
"schemaDir": "schema"
},
"styleModule": "style/index.js"
}
71 changes: 71 additions & 0 deletions packages/htmlviewer-extension/schema/plugin.json
@@ -0,0 +1,71 @@
{
"title": "HTML Viewer",
"description": "HTML Viewer settings.",
"jupyter.lab.toolbars": {
"HTML Viewer": [
{ "name": "refresh", "rank": 10 },
{ "name": "trust", "rank": 20 }
]
},
"jupyter.lab.transform": true,
"properties": {
"toolbar": {
"title": "HTML viewer toolbar items",
"description": "Note: To disable a toolbar item,\ncopy it to User Preferences and add the\n\"disabled\" key. The following example will disable the refresh item:\n{\n \"toolbar\": [\n {\n \"name\": \"refresh\",\n \"disabled\": true\n }\n ]\n}\n\nToolbar description:",
"items": {
"$ref": "#/definitions/toolbarItem"
},
"type": "array",
"default": []
}
},
"additionalProperties": false,
"type": "object",
"definitions": {
"toolbarItem": {
"properties": {
"name": {
"title": "Unique name",
"type": "string"
},
"args": {
"title": "Command arguments",
"type": "object"
},
"command": {
"title": "Command id",
"type": "string",
"default": ""
},
"disabled": {
"title": "Whether the item is disabled or not",
"type": "boolean",
"default": false
},
"icon": {
"title": "Item icon id",
"description": "If defined, it will override the command icon",
"type": "string"
},
"label": {
"title": "Item icon label",
"description": "If defined, it will override the command label",
"type": "string"
},
"type": {
"title": "Item type",
"type": "string",
"enum": ["command", "spacer"]
},
"rank": {
"title": "Item rank",
"type": "number",
"minimum": 0
}
},
"required": ["name"],
"additionalProperties": false,
"type": "object"
}
}
}
67 changes: 57 additions & 10 deletions packages/htmlviewer-extension/src/index.tsx
Expand Up @@ -12,16 +12,28 @@ import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { ICommandPalette, WidgetTracker } from '@jupyterlab/apputils';
import {
createToolbarFactory,
ICommandPalette,
IToolbarWidgetRegistry,
WidgetTracker
} from '@jupyterlab/apputils';
import { DocumentRegistry } from '@jupyterlab/docregistry';
import {
HTMLViewer,
HTMLViewerFactory,
IHTMLViewerTracker
IHTMLViewerTracker,
ToolbarItems
} from '@jupyterlab/htmlviewer';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITranslator } from '@jupyterlab/translation';
import { html5Icon } from '@jupyterlab/ui-components';

/**
* Factory name
*/
const FACTORY = 'HTML Viewer';

/**
* Command IDs used by the plugin.
*/
Expand All @@ -37,7 +49,12 @@ const htmlPlugin: JupyterFrontEndPlugin<IHTMLViewerTracker> = {
id: '@jupyterlab/htmlviewer-extension:plugin',
provides: IHTMLViewerTracker,
requires: [ITranslator],
optional: [ICommandPalette, ILayoutRestorer],
optional: [
ICommandPalette,
ILayoutRestorer,
ISettingRegistry,
IToolbarWidgetRegistry
],
autoStart: true
};

Expand All @@ -48,10 +65,35 @@ function activateHTMLViewer(
app: JupyterFrontEnd,
translator: ITranslator,
palette: ICommandPalette | null,
restorer: ILayoutRestorer | null
restorer: ILayoutRestorer | null,
settingRegistry: ISettingRegistry | null,
toolbarRegistry: IToolbarWidgetRegistry | null
): IHTMLViewerTracker {
// Add an HTML file type to the docregistry.
let toolbarFactory:
| ((widget: HTMLViewer) => DocumentRegistry.IToolbarItem[])
| undefined;
const trans = translator.load('jupyterlab');

if (toolbarRegistry) {
toolbarRegistry.registerFactory<HTMLViewer>(FACTORY, 'refresh', widget =>
ToolbarItems.createRefreshButton(widget, translator)
);
toolbarRegistry.registerFactory<HTMLViewer>(FACTORY, 'trust', widget =>
ToolbarItems.createTrustButton(widget, translator)
);

if (settingRegistry) {
toolbarFactory = createToolbarFactory(
toolbarRegistry,
settingRegistry,
FACTORY,
htmlPlugin.id,
translator
);
}
}

// Add an HTML file type to the docregistry.
const ft: DocumentRegistry.IFileType = {
name: 'html',
contentType: 'file',
Expand All @@ -65,10 +107,12 @@ function activateHTMLViewer(

// Create a new viewer factory.
const factory = new HTMLViewerFactory({
name: trans.__('HTML Viewer'),
name: FACTORY,
fileTypes: ['html'],
defaultFor: ['html'],
readOnly: true
readOnly: true,
toolbarFactory,
translator
});

// Create a widget tracker for HTML documents.
Expand Down Expand Up @@ -108,19 +152,22 @@ function activateHTMLViewer(
// allowing script executions in its context.
app.commands.addCommand(CommandIDs.trustHTML, {
label: trans.__('Trust HTML File'),
caption: trans.__(`Whether the HTML file is trusted.
Trusting the file allows scripts to run in it,
which may result in security risks.
Only enable for files you trust.`),
isEnabled: () => !!tracker.currentWidget,
isToggled: () => {
const current = tracker.currentWidget;
if (!current) {
return false;
}
const sandbox = current.content.sandbox;
return sandbox.indexOf('allow-scripts') !== -1;
return current.trusted;
},
execute: () => {
const current = tracker.currentWidget;
if (!current) {
return false;
return;
}
current.trusted = !current.trusted;
}
Expand Down
7 changes: 6 additions & 1 deletion packages/htmlviewer-extension/tsconfig.json
Expand Up @@ -4,7 +4,9 @@
"outDir": "lib",
"rootDir": "src"
},
"include": ["src/*"],
"include": [
"src/*"
],
"references": [
{
"path": "../application"
Expand All @@ -18,6 +20,9 @@
{
"path": "../htmlviewer"
},
{
"path": "../settingregistry"
},
{
"path": "../translation"
},
Expand Down
1 change: 1 addition & 0 deletions packages/htmlviewer/package.json
Expand Up @@ -40,6 +40,7 @@
"@jupyterlab/ui-components": "^3.1.0-beta.0",
"@lumino/coreutils": "^1.5.3",
"@lumino/signaling": "^1.4.3",
"@lumino/widgets": "^1.19.0",
"react": "^17.0.1"
},
"devDependencies": {
Expand Down
102 changes: 73 additions & 29 deletions packages/htmlviewer/src/index.tsx
Expand Up @@ -26,6 +26,7 @@ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
import { refreshIcon } from '@jupyterlab/ui-components';
import { Token } from '@lumino/coreutils';
import { ISignal, Signal } from '@lumino/signaling';
import { Widget } from '@lumino/widgets';
import * as React from 'react';

/**
Expand Down Expand Up @@ -74,7 +75,6 @@ export class HTMLViewer
content: new IFrame({ sandbox: ['allow-same-origin'] })
});
this.translator = options.translator || nullTranslator;
const trans = this.translator.load('jupyterlab');
this.content.addClass(CSS_CLASS);

void this.context.ready.then(() => {
Expand All @@ -86,31 +86,6 @@ export class HTMLViewer
});
this._monitor.activityStopped.connect(this.update, this);
});

// Make a refresh button for the toolbar.
this.toolbar.addItem(
'refresh',
new ToolbarButton({
icon: refreshIcon,
onClick: async () => {
if (!this.context.model.dirty) {
await this.context.revert();
this.update();
}
},
tooltip: trans.__('Rerender HTML Document')
})
);
// Make a trust button for the toolbar.
this.toolbar.addItem(
'trust',
ReactWidget.create(
<Private.TrustButtonComponent
htmlDocument={this}
translator={this.translator}
/>
)
);
}

/**
Expand Down Expand Up @@ -234,6 +209,73 @@ export class HTMLViewerFactory extends ABCWidgetFactory<HTMLViewer> {
protected createNewWidget(context: DocumentRegistry.Context): HTMLViewer {
return new HTMLViewer({ context });
}

/**
* Default factory for toolbar items to be added after the widget is created.
*/
protected defaultToolbarFactory(
widget: HTMLViewer
): DocumentRegistry.IToolbarItem[] {
return [
// Make a refresh button for the toolbar.
{
name: 'refresh',
widget: ToolbarItems.createRefreshButton(widget, this.translator)
},
// Make a trust button for the toolbar.
{
name: 'trust',
widget: ToolbarItems.createTrustButton(widget, this.translator)
}
];
}
}

/**
* A namespace for toolbar items generator
*/
export namespace ToolbarItems {
/**
* Create the refresh button
*
* @param widget HTML viewer widget
* @param translator Application translator object
* @returns Toolbar item button
*/
export function createRefreshButton(
widget: HTMLViewer,
translator?: ITranslator
): Widget {
const trans = (translator ?? nullTranslator).load('jupyterlab');
return new ToolbarButton({
icon: refreshIcon,
onClick: async () => {
if (!widget.context.model.dirty) {
await widget.context.revert();
widget.update();
}
},
tooltip: trans.__('Rerender HTML Document')
});
}
/**
* Create the trust button
*
* @param document HTML viewer widget
* @param translator Application translator object
* @returns Toolbar item button
*/
export function createTrustButton(
document: HTMLViewer,
translator: ITranslator
): Widget {
return ReactWidget.create(
<Private.TrustButtonComponent
htmlDocument={document}
translator={translator}
/>
);
}
}

/**
Expand Down Expand Up @@ -270,17 +312,19 @@ namespace Private {
/**
* React component for a trusted button.
*
* This wraps the ToolbarButtonComponent and watches for trust chagnes.
* This wraps the ToolbarButtonComponent and watches for trust changes.
*/
export function TrustButtonComponent(props: TrustButtonComponent.IProps) {
export function TrustButtonComponent(
props: TrustButtonComponent.IProps
): JSX.Element {
const translator = props.translator || nullTranslator;
const trans = translator.load('jupyterlab');
return (
<UseSignal
signal={props.htmlDocument.trustedChanged}
initialSender={props.htmlDocument}
>
{session => (
{() => (
<ToolbarButtonComponent
className=""
onClick={() =>
Expand Down

0 comments on commit 420d0f7

Please sign in to comment.