From 448566146fdcdecb0dde1f03f41e7b8ffa9dd89a Mon Sep 17 00:00:00 2001 From: Tanuj Mittal Date: Thu, 22 Apr 2021 13:37:12 -0400 Subject: [PATCH] Add CellOutputViewer for SandboxOutputs (#686) * Initial commit * Optimizations * Optimize notebookOutputViewer bundle size by lazy loading transforms * Update package-lock.json * More optimizations * Updates * Fix unit test and other updates * Address feedback * Update package-lock.json * Update test snapshots * Fix build * Reduce cellOutputViewer bundle size * Renaming --- externals/iframeResizer.contentWindow.min.js | 10 + package-lock.json | 592 ++++++++++++++++-- package.json | 7 +- src/CellOutputViewer/CellOutputViewer.tsx | 68 ++ src/CellOutputViewer/TransformMedia.tsx | 138 ++++ src/CellOutputViewer/cellOutputViewer.html | 12 + src/CellOutputViewer/transforms/Vega2.ts | 1 + src/CellOutputViewer/transforms/Vega3.ts | 1 + src/CellOutputViewer/transforms/Vega4.ts | 1 + src/CellOutputViewer/transforms/Vega5.ts | 1 + src/CellOutputViewer/transforms/VegaLite1.ts | 1 + src/CellOutputViewer/transforms/VegaLite2.ts | 1 + src/CellOutputViewer/transforms/VegaLite3.ts | 1 + src/CellOutputViewer/transforms/VegaLite4.ts | 1 + .../transforms/WidgetDisplay.ts | 1 + src/Explorer/Notebook/NotebookClientV2.ts | 114 ++-- .../NotebookReadOnlyRenderer.tsx | 17 +- .../NotebookRenderer/NotebookRenderer.tsx | 17 +- .../outputs/IFrameOutputs.tsx | 70 --- .../NotebookRenderer/outputs/SandboxFrame.tsx | 69 -- .../outputs/SandboxJavaScript.tsx | 26 - .../outputs/SandboxOutputs.tsx | 132 ++++ .../outputs/SanitizedHTML.tsx | 38 -- webpack.config.js | 13 + 24 files changed, 998 insertions(+), 334 deletions(-) create mode 100644 externals/iframeResizer.contentWindow.min.js create mode 100644 src/CellOutputViewer/CellOutputViewer.tsx create mode 100644 src/CellOutputViewer/TransformMedia.tsx create mode 100644 src/CellOutputViewer/cellOutputViewer.html create mode 100644 src/CellOutputViewer/transforms/Vega2.ts create mode 100644 src/CellOutputViewer/transforms/Vega3.ts create mode 100644 src/CellOutputViewer/transforms/Vega4.ts create mode 100644 src/CellOutputViewer/transforms/Vega5.ts create mode 100644 src/CellOutputViewer/transforms/VegaLite1.ts create mode 100644 src/CellOutputViewer/transforms/VegaLite2.ts create mode 100644 src/CellOutputViewer/transforms/VegaLite3.ts create mode 100644 src/CellOutputViewer/transforms/VegaLite4.ts create mode 100644 src/CellOutputViewer/transforms/WidgetDisplay.ts delete mode 100644 src/Explorer/Notebook/NotebookRenderer/outputs/IFrameOutputs.tsx delete mode 100644 src/Explorer/Notebook/NotebookRenderer/outputs/SandboxFrame.tsx delete mode 100644 src/Explorer/Notebook/NotebookRenderer/outputs/SandboxJavaScript.tsx create mode 100644 src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx delete mode 100644 src/Explorer/Notebook/NotebookRenderer/outputs/SanitizedHTML.tsx diff --git a/externals/iframeResizer.contentWindow.min.js b/externals/iframeResizer.contentWindow.min.js new file mode 100644 index 000000000..f711ae4fe --- /dev/null +++ b/externals/iframeResizer.contentWindow.min.js @@ -0,0 +1,10 @@ +/*! iFrame Resizer (iframeSizer.contentWindow.min.js) - v4.3.1 - 2021-01-11 + * Desc: Include this file in any page being loaded into an iframe + * to force the iframe to resize to the content size. + * Requires: iframeResizer.min.js on host page. + * Copyright: (c) 2021 David J. Bradshaw - dave@bradshaw.net + * License: MIT + */ + +!function(u){if("undefined"!=typeof window){var n=!0,o=10,i="",r=0,a="",t=null,c="",s=!1,d={resize:1,click:1},l=128,f=!0,m=1,h="bodyOffset",g=h,p=!0,v="",y={},w=32,b=null,T=!1,E=!1,O="[iFrameSizer]",S=O.length,M="",I={max:1,min:1,bodyScroll:1,documentElementScroll:1},N="child",A=!0,C=window.parent,z="*",k=0,R=!1,e=null,x=16,L=1,F="scroll",P=F,D=window,j=function(){ae("onMessage function not defined")},q=function(){},H=function(){},W={height:function(){return ae("Custom height calculation function not defined"),document.documentElement.offsetHeight},width:function(){return ae("Custom width calculation function not defined"),document.body.scrollWidth}},B={},J=!1;try{var U=Object.create({},{passive:{get:function(){J=!0}}});window.addEventListener("test",te,U),window.removeEventListener("test",te,U)}catch(e){}var V,X,Y,K,Q,G,Z=Date.now||function(){return(new Date).getTime()},$={bodyOffset:function(){return document.body.offsetHeight+ve("marginTop")+ve("marginBottom")},offset:function(){return $.bodyOffset()},bodyScroll:function(){return document.body.scrollHeight},custom:function(){return W.height()},documentElementOffset:function(){return document.documentElement.offsetHeight},documentElementScroll:function(){return document.documentElement.scrollHeight},max:function(){return Math.max.apply(null,we($))},min:function(){return Math.min.apply(null,we($))},grow:function(){return $.max()},lowestElement:function(){return Math.max($.bodyOffset()||$.documentElementOffset(),ye("bottom",Te()))},taggedElement:function(){return be("bottom","data-iframe-height")}},_={bodyScroll:function(){return document.body.scrollWidth},bodyOffset:function(){return document.body.offsetWidth},custom:function(){return W.width()},documentElementScroll:function(){return document.documentElement.scrollWidth},documentElementOffset:function(){return document.documentElement.offsetWidth},scroll:function(){return Math.max(_.bodyScroll(),_.documentElementScroll())},max:function(){return Math.max.apply(null,we(_))},min:function(){return Math.min.apply(null,we(_))},rightMostElement:function(){return ye("right",Te())},taggedElement:function(){return be("right","data-iframe-width")}},ee=(V=Ee,Q=null,G=0,function(){var e=Z(),t=x-(e-(G=G||e));return X=this,Y=arguments,t<=0||x void; +} + +const onInit = async () => { + postRobot.on( + "props", + { + window: window.parent, + domain: window.location.origin, + }, + (event) => { + // Typescript definition for event is wrong. So read props by casting to + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const props = (event as any).data as CellOutputViewerProps; + const outputs = ( +
+ {props.outputs?.map((output, index) => ( + + props.onMetadataChange(metadata, mediaType, index)} + /> + props.onMetadataChange(metadata, mediaType, index)} + /> + + + + ))} +
+ ); + + ReactDOM.render(outputs, document.getElementById("cellOutput")); + } + ); +}; + +// Entry point +window.addEventListener("load", onInit); diff --git a/src/CellOutputViewer/TransformMedia.tsx b/src/CellOutputViewer/TransformMedia.tsx new file mode 100644 index 000000000..b38922042 --- /dev/null +++ b/src/CellOutputViewer/TransformMedia.tsx @@ -0,0 +1,138 @@ +import { ImmutableDisplayData, ImmutableExecuteResult, JSONObject } from "@nteract/commutable"; +// import outputs individually to avoid increasing the bundle size +import { HTML } from "@nteract/outputs/lib/components/media/html"; +import { Image } from "@nteract/outputs/lib/components/media/image"; +import { JavaScript } from "@nteract/outputs/lib/components/media/javascript"; +import { Json } from "@nteract/outputs/lib/components/media/json"; +import { LaTeX } from "@nteract/outputs/lib/components/media/latex"; +import { Plain } from "@nteract/outputs/lib/components/media/plain"; +import { SVG } from "@nteract/outputs/lib/components/media/svg"; +import { ContentRef } from "@nteract/types"; +import React, { Suspense } from "react"; + +const EmptyTransform = (): JSX.Element => <>; + +const displayOrder = [ + "application/vnd.jupyter.widget-view+json", + "application/vnd.vega.v5+json", + "application/vnd.vega.v4+json", + "application/vnd.vega.v3+json", + "application/vnd.vega.v2+json", + "application/vnd.vegalite.v4+json", + "application/vnd.vegalite.v3+json", + "application/vnd.vegalite.v2+json", + "application/vnd.vegalite.v1+json", + "application/geo+json", + "application/vnd.plotly.v1+json", + "text/vnd.plotly.v1+html", + "application/x-nteract-model-debug+json", + "application/vnd.dataresource+json", + "application/vdom.v1+json", + "application/json", + "application/javascript", + "text/html", + "text/markdown", + "text/latex", + "image/svg+xml", + "image/gif", + "image/png", + "image/jpeg", + "text/plain", +]; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformsById = new Map>([ + ["text/vnd.plotly.v1+html", React.lazy(() => import("@nteract/transform-plotly"))], + ["application/vnd.plotly.v1+json", React.lazy(() => import("@nteract/transform-plotly"))], + ["application/geo+json", EmptyTransform], // TODO: The geojson transform will likely need some work because of the basemap URL(s) + ["application/x-nteract-model-debug+json", React.lazy(() => import("@nteract/transform-model-debug"))], + ["application/vnd.dataresource+json", React.lazy(() => import("@nteract/data-explorer"))], + ["application/vnd.jupyter.widget-view+json", React.lazy(() => import("./transforms/WidgetDisplay"))], + ["application/vnd.vegalite.v1+json", React.lazy(() => import("./transforms/VegaLite1"))], + ["application/vnd.vegalite.v2+json", React.lazy(() => import("./transforms/VegaLite2"))], + ["application/vnd.vegalite.v3+json", React.lazy(() => import("./transforms/VegaLite3"))], + ["application/vnd.vegalite.v4+json", React.lazy(() => import("./transforms/VegaLite4"))], + ["application/vnd.vega.v2+json", React.lazy(() => import("./transforms/Vega2"))], + ["application/vnd.vega.v3+json", React.lazy(() => import("./transforms/Vega3"))], + ["application/vnd.vega.v4+json", React.lazy(() => import("./transforms/Vega4"))], + ["application/vnd.vega.v5+json", React.lazy(() => import("./transforms/Vega5"))], + ["application/vdom.v1+json", React.lazy(() => import("@nteract/transform-vdom"))], + ["application/json", Json], + ["application/javascript", JavaScript], + ["text/html", HTML], + ["text/markdown", React.lazy(() => import("@nteract/outputs/lib/components/media/markdown"))], // Markdown increases the bundle size so lazy load it + ["text/latex", LaTeX], + ["image/svg+xml", SVG], + ["image/gif", Image], + ["image/png", Image], + ["image/jpeg", Image], + ["text/plain", Plain], +]); + +interface TransformMediaProps { + output_type: string; + id: string; + contentRef: ContentRef; + output?: ImmutableDisplayData | ImmutableExecuteResult; + onMetadataChange: (metadata: JSONObject, mediaType: string) => void; +} + +export const TransformMedia = (props: TransformMediaProps): JSX.Element => { + const { Media, mediaType, data, metadata } = getMediaInfo(props); + + // If we had no valid result, return an empty output + if (!mediaType || !data) { + return <>; + } + + return ( + Loading...}> + + + ); +}; + +const getMediaInfo = (props: TransformMediaProps) => { + const { output, output_type } = props; + // This component should only be used with display data and execute result + if (!output || !(output_type === "display_data" || output_type === "execute_result")) { + console.warn("connected transform media managed to get a non media bundle output"); + return { + Media: EmptyTransform, + }; + } + + // Find the first mediaType in the output data that we support with a handler + const mediaType = displayOrder.find( + (key) => + Object.prototype.hasOwnProperty.call(output.data, key) && + (Object.prototype.hasOwnProperty.call(transformsById, key) || transformsById.get(key)) + ); + + if (mediaType) { + const metadata = output.metadata.get(mediaType); + const data = output.data[mediaType]; + + const Media = transformsById.get(mediaType); + return { + Media, + mediaType, + data, + metadata, + }; + } + + return { + Media: EmptyTransform, + mediaType, + output, + }; +}; + +export default TransformMedia; diff --git a/src/CellOutputViewer/cellOutputViewer.html b/src/CellOutputViewer/cellOutputViewer.html new file mode 100644 index 000000000..7db8c5958 --- /dev/null +++ b/src/CellOutputViewer/cellOutputViewer.html @@ -0,0 +1,12 @@ + + + + + + Cell Output Viewer + + + +
+ + diff --git a/src/CellOutputViewer/transforms/Vega2.ts b/src/CellOutputViewer/transforms/Vega2.ts new file mode 100644 index 000000000..08a358f54 --- /dev/null +++ b/src/CellOutputViewer/transforms/Vega2.ts @@ -0,0 +1 @@ +export { Vega2 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/Vega3.ts b/src/CellOutputViewer/transforms/Vega3.ts new file mode 100644 index 000000000..87289b52f --- /dev/null +++ b/src/CellOutputViewer/transforms/Vega3.ts @@ -0,0 +1 @@ +export { Vega3 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/Vega4.ts b/src/CellOutputViewer/transforms/Vega4.ts new file mode 100644 index 000000000..a95100d63 --- /dev/null +++ b/src/CellOutputViewer/transforms/Vega4.ts @@ -0,0 +1 @@ +export { Vega4 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/Vega5.ts b/src/CellOutputViewer/transforms/Vega5.ts new file mode 100644 index 000000000..6fa20b452 --- /dev/null +++ b/src/CellOutputViewer/transforms/Vega5.ts @@ -0,0 +1 @@ +export { Vega5 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite1.ts b/src/CellOutputViewer/transforms/VegaLite1.ts new file mode 100644 index 000000000..fb615d92c --- /dev/null +++ b/src/CellOutputViewer/transforms/VegaLite1.ts @@ -0,0 +1 @@ +export { VegaLite1 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite2.ts b/src/CellOutputViewer/transforms/VegaLite2.ts new file mode 100644 index 000000000..f664e0bda --- /dev/null +++ b/src/CellOutputViewer/transforms/VegaLite2.ts @@ -0,0 +1 @@ +export { VegaLite2 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite3.ts b/src/CellOutputViewer/transforms/VegaLite3.ts new file mode 100644 index 000000000..8a57abbc4 --- /dev/null +++ b/src/CellOutputViewer/transforms/VegaLite3.ts @@ -0,0 +1 @@ +export { VegaLite3 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite4.ts b/src/CellOutputViewer/transforms/VegaLite4.ts new file mode 100644 index 000000000..fca92c80f --- /dev/null +++ b/src/CellOutputViewer/transforms/VegaLite4.ts @@ -0,0 +1 @@ +export { VegaLite4 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/WidgetDisplay.ts b/src/CellOutputViewer/transforms/WidgetDisplay.ts new file mode 100644 index 000000000..4387a4f5d --- /dev/null +++ b/src/CellOutputViewer/transforms/WidgetDisplay.ts @@ -0,0 +1 @@ +export { WidgetDisplay as default } from "@nteract/jupyter-widgets"; diff --git a/src/Explorer/Notebook/NotebookClientV2.ts b/src/Explorer/Notebook/NotebookClientV2.ts index 940be1e3a..7c5f4179a 100644 --- a/src/Explorer/Notebook/NotebookClientV2.ts +++ b/src/Explorer/Notebook/NotebookClientV2.ts @@ -34,8 +34,6 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { userContext } from "../../UserContext"; import configureStore from "./NotebookComponent/store"; import { CdbAppState, makeCdbRecord } from "./NotebookComponent/types"; -import SandboxJavaScript from "./NotebookRenderer/outputs/SandboxJavaScript"; -import SanitizedHTML from "./NotebookRenderer/outputs/SanitizedHTML"; export type KernelSpecsDisplay = { name: string; displayName: string }; @@ -125,62 +123,62 @@ export class NotebookClientV2 { contents: makeContentsRecord({ byRef: Immutable.Map(), }), - transforms: makeTransformsRecord({ - displayOrder: Immutable.List([ - "application/vnd.jupyter.widget-view+json", - "application/vnd.vega.v5+json", - "application/vnd.vega.v4+json", - "application/vnd.vega.v3+json", - "application/vnd.vega.v2+json", - "application/vnd.vegalite.v3+json", - "application/vnd.vegalite.v2+json", - "application/vnd.vegalite.v1+json", - "application/geo+json", - "application/vnd.plotly.v1+json", - "text/vnd.plotly.v1+html", - "application/x-nteract-model-debug+json", - "application/vnd.dataresource+json", - "application/vdom.v1+json", - "application/json", - "application/javascript", - "text/html", - "text/markdown", - "text/latex", - "image/svg+xml", - "image/gif", - "image/png", - "image/jpeg", - "text/plain", - ]), - byId: Immutable.Map({ - "text/vnd.plotly.v1+html": NullTransform, - "application/vnd.plotly.v1+json": NullTransform, - "application/geo+json": NullTransform, - "application/x-nteract-model-debug+json": NullTransform, - "application/vnd.dataresource+json": NullTransform, - "application/vnd.jupyter.widget-view+json": NullTransform, - "application/vnd.vegalite.v1+json": NullTransform, - "application/vnd.vegalite.v2+json": NullTransform, - "application/vnd.vegalite.v3+json": NullTransform, - "application/vnd.vega.v2+json": NullTransform, - "application/vnd.vega.v3+json": NullTransform, - "application/vnd.vega.v4+json": NullTransform, - "application/vnd.vega.v5+json": NullTransform, - "application/vdom.v1+json": TransformVDOM, - "application/json": Media.Json, - "application/javascript": userContext.features.sandboxNotebookOutputs - ? SandboxJavaScript - : Media.JavaScript, - "text/html": userContext.features.sandboxNotebookOutputs ? SanitizedHTML : Media.HTML, - "text/markdown": Media.Markdown, - "text/latex": Media.LaTeX, - "image/svg+xml": Media.SVG, - "image/gif": Media.Image, - "image/png": Media.Image, - "image/jpeg": Media.Image, - "text/plain": Media.Plain, - }), - }), + transforms: userContext.features.sandboxNotebookOutputs + ? undefined + : makeTransformsRecord({ + displayOrder: Immutable.List([ + "application/vnd.jupyter.widget-view+json", + "application/vnd.vega.v5+json", + "application/vnd.vega.v4+json", + "application/vnd.vega.v3+json", + "application/vnd.vega.v2+json", + "application/vnd.vegalite.v3+json", + "application/vnd.vegalite.v2+json", + "application/vnd.vegalite.v1+json", + "application/geo+json", + "application/vnd.plotly.v1+json", + "text/vnd.plotly.v1+html", + "application/x-nteract-model-debug+json", + "application/vnd.dataresource+json", + "application/vdom.v1+json", + "application/json", + "application/javascript", + "text/html", + "text/markdown", + "text/latex", + "image/svg+xml", + "image/gif", + "image/png", + "image/jpeg", + "text/plain", + ]), + byId: Immutable.Map({ + "text/vnd.plotly.v1+html": NullTransform, + "application/vnd.plotly.v1+json": NullTransform, + "application/geo+json": NullTransform, + "application/x-nteract-model-debug+json": NullTransform, + "application/vnd.dataresource+json": NullTransform, + "application/vnd.jupyter.widget-view+json": NullTransform, + "application/vnd.vegalite.v1+json": NullTransform, + "application/vnd.vegalite.v2+json": NullTransform, + "application/vnd.vegalite.v3+json": NullTransform, + "application/vnd.vega.v2+json": NullTransform, + "application/vnd.vega.v3+json": NullTransform, + "application/vnd.vega.v4+json": NullTransform, + "application/vnd.vega.v5+json": NullTransform, + "application/vdom.v1+json": TransformVDOM, + "application/json": Media.Json, + "application/javascript": Media.JavaScript, + "text/html": Media.HTML, + "text/markdown": Media.Markdown, + "text/latex": Media.LaTeX, + "image/svg+xml": Media.SVG, + "image/gif": Media.Image, + "image/png": Media.Image, + "image/jpeg": Media.Image, + "text/plain": Media.Plain, + }), + }), }), }), cdb: makeCdbRecord({ diff --git a/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx b/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx index 75133d7f3..ceef02cbb 100644 --- a/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx +++ b/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx @@ -1,10 +1,8 @@ import { actions, ContentRef } from "@nteract/core"; -import { KernelOutputError, StreamText } from "@nteract/outputs"; import { Cells, CodeCell, MarkdownCell, RawCell } from "@nteract/stateful-components"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt"; -import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media"; import * as React from "react"; import { connect } from "react-redux"; import { Dispatch } from "redux"; @@ -14,7 +12,7 @@ import { AzureTheme } from "./AzureTheme"; import "./base.css"; import "./default.css"; import "./NotebookReadOnlyRenderer.less"; -import IFrameOutputs from "./outputs/IFrameOutputs"; +import SandboxOutputs from "./outputs/SandboxOutputs"; export interface NotebookRendererProps { contentRef: any; @@ -27,7 +25,9 @@ export interface NotebookRendererProps { */ class NotebookReadOnlyRenderer extends React.Component { componentDidMount() { - loadTransform(this.props as any); + if (!userContext.features.sandboxNotebookOutputs) { + loadTransform(this.props as any); + } } private renderPrompt(id: string, contentRef: string): JSX.Element { @@ -63,14 +63,7 @@ class NotebookReadOnlyRenderer extends React.Component { {{ prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef), outputs: userContext.features.sandboxNotebookOutputs - ? (props: any) => ( - - - - - - - ) + ? () => : undefined, editor: { monaco: (props: PassedEditorProps) => diff --git a/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx b/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx index b78fdab97..876cbf337 100644 --- a/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx +++ b/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx @@ -1,11 +1,9 @@ import { CellId } from "@nteract/commutable"; import { CellType } from "@nteract/commutable/src"; import { actions, ContentRef } from "@nteract/core"; -import { KernelOutputError, StreamText } from "@nteract/outputs"; import { Cells, CodeCell, RawCell } from "@nteract/stateful-components"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; -import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media"; import * as React from "react"; import { DndProvider } from "react-dnd"; import HTML5Backend from "react-dnd-html5-backend"; @@ -23,7 +21,7 @@ import KeyboardShortcuts from "./decorators/kbd-shortcuts"; import "./default.css"; import MarkdownCell from "./markdown-cell"; import "./NotebookRenderer.less"; -import IFrameOutputs from "./outputs/IFrameOutputs"; +import SandboxOutputs from "./outputs/SandboxOutputs"; import Prompt from "./Prompt"; import { promptContent } from "./PromptContent"; import StatusBar from "./StatusBar"; @@ -71,7 +69,9 @@ class BaseNotebookRenderer extends React.Component { } componentDidMount() { - loadTransform(this.props as any); + if (!userContext.features.sandboxNotebookOutputs) { + loadTransform(this.props as any); + } this.props.updateNotebookParentDomElt(this.props.contentRef, this.notebookRendererRef.current); } @@ -109,14 +109,7 @@ class BaseNotebookRenderer extends React.Component { ), toolbar: () => , outputs: userContext.features.sandboxNotebookOutputs - ? (props: any) => ( - - - - - - - ) + ? () => : undefined, }} diff --git a/src/Explorer/Notebook/NotebookRenderer/outputs/IFrameOutputs.tsx b/src/Explorer/Notebook/NotebookRenderer/outputs/IFrameOutputs.tsx deleted file mode 100644 index 8ca9764f5..000000000 --- a/src/Explorer/Notebook/NotebookRenderer/outputs/IFrameOutputs.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { AppState, ContentRef, selectors } from "@nteract/core"; -import { Output } from "@nteract/outputs"; -import Immutable from "immutable"; -import React from "react"; -import { connect } from "react-redux"; -import { SandboxFrame } from "./SandboxFrame"; - -// Adapted from https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/outputs/index.tsx -// to add support for sandboxing using - ); - } - - componentWillUnmount(): void { - this.resizeObserver?.disconnect(); - this.mutationObserver?.disconnect(); - } - - onFrameLoad(event: React.SyntheticEvent): void { - const doc = (event.target as HTMLIFrameElement).contentDocument; - copyStyles(document, doc); - - this.setState({ frameBody: doc.body }); - - this.mutationObserver = new MutationObserver(() => { - const bodyFirstElementChild = this.state.frameBody?.firstElementChild; - if (!this.resizeObserver && bodyFirstElementChild) { - this.resizeObserver = new ResizeObserver(() => - this.setState({ - frameHeight: this.state.frameBody?.firstElementChild.scrollHeight, - }) - ); - this.resizeObserver.observe(bodyFirstElementChild); - } - }); - this.mutationObserver.observe(doc.body, { childList: true }); - } -} diff --git a/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxJavaScript.tsx b/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxJavaScript.tsx deleted file mode 100644 index c6420a30e..000000000 --- a/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxJavaScript.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Media } from "@nteract/outputs"; -import React from "react"; - -interface Props { - /** - * The JavaScript code that we would like to execute. - */ - data: string; - /** - * The media type associated with our component. - */ - mediaType: "text/javascript"; -} - -export class SandboxJavaScript extends React.PureComponent { - static defaultProps = { - data: "", - mediaType: "application/javascript", - }; - - render(): JSX.Element { - return ${this.props.data}`} />; - } -} - -export default SandboxJavaScript; diff --git a/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx b/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx new file mode 100644 index 000000000..40f42fd35 --- /dev/null +++ b/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx @@ -0,0 +1,132 @@ +import { JSONObject } from "@nteract/commutable"; +import { outputToJS } from "@nteract/commutable/lib/v4"; +import { actions, AppState, ContentRef, selectors } from "@nteract/core"; +import IframeResizer from "iframe-resizer-react"; +import Immutable from "immutable"; +import postRobot from "post-robot"; +import React from "react"; +import { connect } from "react-redux"; +import { Dispatch } from "redux"; +import { CellOutputViewerProps } from "../../../../CellOutputViewer/CellOutputViewer"; + +// Adapted from https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/outputs/index.tsx +// to add support for sandboxing using