Add trusted indicator (#6736)

* Add trusted indicator

* Lint

* Add docstring

* Fix retro names

* Update Playwright Snapshots

* Update Playwright Snapshots

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Jeremy Tuloup 2023-02-17 09:17:25 +01:00 committed by GitHub
parent b8e9cae07b
commit dc3c26f93f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 153 additions and 1 deletions

View File

@ -34,6 +34,8 @@ import { Poll } from '@lumino/polling';
import { Widget } from '@lumino/widgets'; import { Widget } from '@lumino/widgets';
import { TrustedComponent } from './trusted';
/** /**
* The class for kernel status errors. * The class for kernel status errors.
*/ */
@ -364,6 +366,35 @@ const notebookToolsWidget: JupyterFrontEndPlugin<void> = {
} }
}; };
/**
* A plugin that adds a Trusted indicator to the menu area
*/
const trusted: JupyterFrontEndPlugin<void> = {
id: '@jupyter-notebook/notebook-extension:trusted',
autoStart: true,
requires: [INotebookShell, ITranslator],
activate: (
app: JupyterFrontEnd,
notebookShell: INotebookShell,
translator: ITranslator
): void => {
const onChange = async () => {
const current = notebookShell.currentWidget;
if (!(current instanceof NotebookPanel)) {
return;
}
const notebook = current.content;
await current.context.ready;
const widget = TrustedComponent.create({ notebook, translator });
notebookShell.add(widget, 'menu', { rank: 11_000 });
};
notebookShell.currentChanged.connect(onChange);
}
};
/** /**
* Export the plugins as default. * Export the plugins as default.
*/ */
@ -372,7 +403,8 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
kernelLogo, kernelLogo,
kernelStatus, kernelStatus,
scrollOutput, scrollOutput,
notebookToolsWidget notebookToolsWidget,
trusted
]; ];
export default plugins; export default plugins;

View File

@ -0,0 +1,107 @@
import { ReactWidget } from '@jupyterlab/apputils';
import { Notebook, NotebookActions } from '@jupyterlab/notebook';
import { ITranslator } from '@jupyterlab/translation';
import { toArray } from '@lumino/algorithm';
import React, { useEffect, useState } from 'react';
/**
* Check if a notebook is trusted
* @param notebook The notebook to check
* @returns true if the notebook is trusted, false otherwise
*/
const isTrusted = (notebook: Notebook): boolean => {
const model = notebook.model;
if (!model) {
return false;
}
const cells = toArray(model.cells);
const trusted = cells.reduce((accum, current) => {
if (current.trusted) {
return accum + 1;
} else {
return accum;
}
}, 0);
const total = cells.length;
return trusted === total;
};
/**
* A React component to display the Trusted badge in the menu bar.
* @param notebook The Notebook
* @param translator The Translation service
*/
const TrustedButton = ({
notebook,
translator
}: {
notebook: Notebook;
translator: ITranslator;
}): JSX.Element => {
const trans = translator.load('notebook');
const [trusted, setTrusted] = useState(isTrusted(notebook));
const checkTrust = () => {
const v = isTrusted(notebook);
setTrusted(v);
};
const trust = async () => {
await NotebookActions.trust(notebook, translator);
checkTrust();
};
useEffect(() => {
notebook.modelContentChanged.connect(checkTrust);
notebook.activeCellChanged.connect(checkTrust);
checkTrust();
return () => {
notebook.modelContentChanged.disconnect(checkTrust);
notebook.activeCellChanged.disconnect(checkTrust);
};
});
return (
<button
className={'jp-NotebookTrustedStatus'}
style={!trusted ? { cursor: 'pointer' } : { cursor: 'help' }}
onClick={() => !trusted && trust()}
title={
trusted
? trans.__('JavaScript enabled for notebook display')
: trans.__('JavaScript disabled for notebook display')
}
>
{trusted ? trans.__('Trusted') : trans.__('Not Trusted')}
</button>
);
};
/**
* A namespace for TrustedComponent statics.
*/
export namespace TrustedComponent {
/**
* Create a new TrustedComponent
*
* @param notebook The notebook
* @param translator The translator
*/
export const create = ({
notebook,
translator
}: {
notebook: Notebook;
translator: ITranslator;
}): ReactWidget => {
return ReactWidget.create(
<TrustedButton notebook={notebook} translator={translator} />
);
};
}

View File

@ -220,6 +220,19 @@ body[data-notebook='notebooks']
animation: 0.5s fade-out forwards; animation: 0.5s fade-out forwards;
} }
.jp-NotebookTrustedStatus {
background: var(--jp-layout-color1);
color: var(--jp-ui-font-color1);
margin-top: 4px;
margin-bottom: 4px;
border: solid 1px var(--jp-border-color2);
cursor: help;
}
.jp-NotebookTrustedStatus-not-trusted {
cursor: pointer;
}
@keyframes fade-out { @keyframes fade-out {
0% { 0% {
opacity: 1; opacity: 1;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB