Switch notebook editors to monaco (#239)

* Upgrade nteract packages and related dependencies to make new stateful-component work

* Switch to new monacoEditor

* Configure store using nteract mythic configuration

* Replace CodeMirror with Monaco editor in NotebookReadOnlyRenderer

* Reformat

* Upgrade d3 to latest to resolve d3-selection conflicts with nteract/data-explorer that broke D3ForceGraph

* Upgrade jupyterlab terminal widget to work with latest version of react. Upgrade jupyterlab services to include latest fix for websocket auth

* Update jest test snapshots

* Upgrade packages to fix build issues

* Remove comment

* Fix unit tests

* Fix unit test snapshot

* Remove useless @types/node-fetch
This commit is contained in:
Laurent Nguyen 2020-10-01 14:00:46 +02:00 committed by GitHub
parent 4fe2098730
commit 0ad5fb465b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 5135 additions and 11636 deletions

16215
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,26 +6,26 @@
"dependencies": { "dependencies": {
"@azure/cosmos": "3.9.0", "@azure/cosmos": "3.9.0",
"@azure/cosmos-language-service": "0.0.4", "@azure/cosmos-language-service": "0.0.4",
"@jupyterlab/services": "4.2.0", "@jupyterlab/services": "6.0.0-rc.2",
"@jupyterlab/terminal": "1.2.1", "@jupyterlab/terminal": "3.0.0-rc.2",
"@microsoft/applicationinsights-web": "2.5.8", "@microsoft/applicationinsights-web": "2.5.8",
"@nteract/commutable": "7.1.4", "@nteract/commutable": "7.3.2",
"@nteract/connected-components": "6.7.8", "@nteract/connected-components": "6.8.2",
"@nteract/core": "13.0.0", "@nteract/core": "15.1.0",
"@nteract/data-explorer": "8.0.3", "@nteract/data-explorer": "8.0.3",
"@nteract/directory-listing": "2.0.6", "@nteract/directory-listing": "2.0.6",
"@nteract/dropdown-menu": "1.0.1", "@nteract/dropdown-menu": "1.0.1",
"@nteract/editor": "9.6.6", "@nteract/editor": "10.1.2",
"@nteract/fixtures": "2.3.0", "@nteract/fixtures": "2.3.0",
"@nteract/iron-icons": "1.0.0", "@nteract/iron-icons": "1.0.0",
"@nteract/jupyter-widgets": "2.0.0", "@nteract/jupyter-widgets": "2.0.0",
"@nteract/logos": "1.0.0", "@nteract/logos": "1.0.0",
"@nteract/markdown": "4.4.0", "@nteract/markdown": "4.4.0",
"@nteract/monaco-editor": "3.0.3", "@nteract/monaco-editor": "3.2.0",
"@nteract/octicons": "2.0.0", "@nteract/octicons": "2.0.0",
"@nteract/outputs": "3.0.9", "@nteract/outputs": "3.0.9",
"@nteract/presentational-components": "3.0.7", "@nteract/presentational-components": "3.0.7",
"@nteract/stateful-components": "1.4.0", "@nteract/stateful-components": "1.7.0",
"@nteract/styles": "2.0.2", "@nteract/styles": "2.0.2",
"@nteract/transform-geojson": "5.1.8", "@nteract/transform-geojson": "5.1.8",
"@nteract/transform-model-debug": "5.0.1", "@nteract/transform-model-debug": "5.0.1",
@ -47,6 +47,7 @@
"copy-webpack-plugin": "6.0.2", "copy-webpack-plugin": "6.0.2",
"crossroads": "0.12.2", "crossroads": "0.12.2",
"css-element-queries": "1.1.1", "css-element-queries": "1.1.1",
"d3": "6.1.1",
"datatables.net-colreorder-dt": "1.5.1", "datatables.net-colreorder-dt": "1.5.1",
"datatables.net-dt": "1.10.19", "datatables.net-dt": "1.10.19",
"date-fns": "1.29.0", "date-fns": "1.29.0",
@ -73,7 +74,7 @@
"promise-polyfill": "8.1.0", "promise-polyfill": "8.1.0",
"promise.prototype.finally": "3.1.0", "promise.prototype.finally": "3.1.0",
"q": "1.5.1", "q": "1.5.1",
"react": "16.9.0", "react": "16.13.1",
"react-animate-height": "2.0.8", "react-animate-height": "2.0.8",
"react-dnd": "9.4.0", "react-dnd": "9.4.0",
"react-dnd-html5-backend": "9.4.0", "react-dnd-html5-backend": "9.4.0",
@ -82,11 +83,12 @@
"react-notification-system": "0.2.17", "react-notification-system": "0.2.17",
"react-redux": "7.1.3", "react-redux": "7.1.3",
"redux": "4.0.4", "redux": "4.0.4",
"rx-jupyter": "5.5.2", "rx-jupyter": "5.5.12",
"rxjs": "6.5.3", "rxjs": "6.6.3",
"styled-components": "4.3.2", "styled-components": "4.3.2",
"text-encoding": "0.7.0", "text-encoding": "0.7.0",
"underscore": "1.9.1", "underscore": "1.9.1",
"utility-types": "3.10.0",
"url-polyfill": "1.1.7", "url-polyfill": "1.1.7",
"webcrypto-liner": "1.1.4", "webcrypto-liner": "1.1.4",
"webfontloader": "1.6.28", "webfontloader": "1.6.28",
@ -100,9 +102,9 @@
"@types/applicationinsights-js": "1.0.7", "@types/applicationinsights-js": "1.0.7",
"@types/codemirror": "0.0.56", "@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30", "@types/crossroads": "0.0.30",
"@types/d3": "4.13.2", "@types/d3": "5.9.2",
"@types/enzyme": "3.10.3", "@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.5", "@types/enzyme-adapter-react-16": "1.0.6",
"@types/expect-puppeteer": "4.4.3", "@types/expect-puppeteer": "4.4.3",
"@types/hasher": "0.0.31", "@types/hasher": "0.0.31",
"@types/jest": "23.3.10", "@types/jest": "23.3.10",
@ -113,7 +115,7 @@
"@types/prop-types": "15.5.8", "@types/prop-types": "15.5.8",
"@types/puppeteer": "3.0.1", "@types/puppeteer": "3.0.1",
"@types/q": "1.5.1", "@types/q": "1.5.1",
"@types/react": "16.8.25", "@types/react": "16.9.49",
"@types/react-dom": "16.0.7", "@types/react-dom": "16.0.7",
"@types/react-notification-system": "0.2.39", "@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7", "@types/react-redux": "7.1.7",
@ -132,10 +134,9 @@
"case-sensitive-paths-webpack-plugin": "2.3.0", "case-sensitive-paths-webpack-plugin": "2.3.0",
"create-file-webpack": "1.0.2", "create-file-webpack": "1.0.2",
"css-loader": "1.0.0", "css-loader": "1.0.0",
"d3": "4.13.0", "enzyme": "3.11.0",
"enzyme": "3.10.0", "enzyme-adapter-react-16": "1.15.5",
"enzyme-adapter-react-16": "1.15.1", "enzyme-to-json": "3.6.1",
"enzyme-to-json": "3.4.3",
"eslint": "7.8.1", "eslint": "7.8.1",
"eslint-cli": "1.1.1", "eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2", "eslint-plugin-no-null": "1.0.2",
@ -156,7 +157,7 @@
"less-vars-loader": "1.1.0", "less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "0.4.3", "mini-css-extract-plugin": "0.4.3",
"monaco-editor-webpack-plugin": "1.7.0", "monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.0", "node-fetch": "2.6.1",
"prettier": "1.19.1", "prettier": "1.19.1",
"puppeteer": "4.0.0", "puppeteer": "4.0.0",
"raw-loader": "0.5.1", "raw-loader": "0.5.1",

View File

@ -26,7 +26,7 @@ exports[`GalleryCardComponent renders 1`] = `
/> />
</CardItem> </CardItem>
<CardItem> <CardItem>
<Memo(StyledImageBase) <StyledImageBase
alt="name cover image" alt="name cover image"
height={144} height={144}
imageFit={2} imageFit={2}
@ -95,7 +95,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
variant="tiny" variant="tiny"
> >
<Memo(StyledIconBase) <StyledIconBase
iconName="RedEye" iconName="RedEye"
styles={ styles={
Object { Object {
@ -119,7 +119,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
variant="tiny" variant="tiny"
> >
<Memo(StyledIconBase) <StyledIconBase
iconName="Download" iconName="Download"
styles={ styles={
Object { Object {
@ -143,7 +143,7 @@ exports[`GalleryCardComponent renders 1`] = `
} }
variant="tiny" variant="tiny"
> >
<Memo(StyledIconBase) <StyledIconBase
iconName="Heart" iconName="Heart"
styles={ styles={
Object { Object {

View File

@ -13,7 +13,7 @@ exports[`InfoComponent renders 1`] = `
<div <div
className="infoPanelMain" className="infoPanelMain"
> >
<Memo(StyledIconBase) <StyledIconBase
className="infoIconMain" className="infoIconMain"
iconName="Help" iconName="Help"
styles={ styles={

View File

@ -58,7 +58,7 @@ export class NotebookViewerComponent extends React.Component<
databaseAccountName: undefined, databaseAccountName: undefined,
defaultExperience: "NotebookViewer", defaultExperience: "NotebookViewer",
isReadOnly: true, isReadOnly: true,
cellEditorType: "codemirror", cellEditorType: "monaco",
autoSaveInterval: 365 * 24 * 3600 * 1000, // There is no way to turn off auto-save, set to 1 year autoSaveInterval: 365 * 24 * 3600 * 1000, // There is no way to turn off auto-save, set to 1 year
contentProvider: contents.JupyterContentProvider // NotebookViewer only knows how to talk to Jupyter contents API contentProvider: contents.JupyterContentProvider // NotebookViewer only knows how to talk to Jupyter contents API
}); });

View File

@ -56,14 +56,14 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
Invalid Date Invalid Date
</Text> </Text>
<Text> <Text>
<Memo(StyledIconBase) <StyledIconBase
iconName="RedEye" iconName="RedEye"
/> />
0 0
</Text> </Text>
<Text> <Text>
<Memo(StyledIconBase) <StyledIconBase
iconName="Download" iconName="Download"
/> />
0 0
@ -156,14 +156,14 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
Invalid Date Invalid Date
</Text> </Text>
<Text> <Text>
<Memo(StyledIconBase) <StyledIconBase
iconName="RedEye" iconName="RedEye"
/> />
0 0
</Text> </Text>
<Text> <Text>
<Memo(StyledIconBase) <StyledIconBase
iconName="Download" iconName="Download"
/> />
0 0

View File

@ -41,7 +41,7 @@ exports[`ToolTipLabelComponent renders 1`] = `
} }
} }
> >
<Memo(StyledIconBase) <StyledIconBase
ariaLabel="Info" ariaLabel="Info"
iconName="Info" iconName="Info"
styles={ styles={

View File

@ -1,13 +1,13 @@
import * as ko from "knockout"; import * as ko from "knockout";
import Q from "q"; import Q from "q";
import { schemeCategory20 } from "d3"; import { schemeCategory10 } from "d3-scale-chromatic";
import { event as d3Event, selectAll, select } from "d3-selection"; import { selectAll, select } from "d3-selection";
import { zoom, zoomIdentity } from "d3-zoom"; import { zoom, zoomIdentity } from "d3-zoom";
import { scaleOrdinal } from "d3-scale"; import { scaleOrdinal } from "d3-scale";
import { forceSimulation, forceLink, forceCollide, forceManyBody } from "d3-force"; import { forceSimulation, forceLink, forceCollide, forceManyBody } from "d3-force";
import { interpolateNumber, interpolate } from "d3-interpolate"; import { interpolateNumber, interpolate } from "d3-interpolate";
import { map as d3Map } from "d3-collection"; import { map as d3Map } from "d3-collection";
import { drag } from "d3-drag"; import { drag, D3DragEvent } from "d3-drag";
import _ from "underscore"; import _ from "underscore";
import { NeighborType } from "../../../Contracts/ViewModels"; import { NeighborType } from "../../../Contracts/ViewModels";
@ -89,7 +89,7 @@ export class D3ForceGraph implements GraphRenderer {
private static readonly PAGINATION_LINE2_Y_OFFSET_PX = 14; private static readonly PAGINATION_LINE2_Y_OFFSET_PX = 14;
// We limit the number of different colors to 20 // We limit the number of different colors to 20
private static readonly COLOR_SCHEME_20 = scaleOrdinal(schemeCategory20); private static readonly COLOR_SCHEME = scaleOrdinal(schemeCategory10);
private static readonly MAX_COLOR_NB = 20; private static readonly MAX_COLOR_NB = 20;
// Some state variables // Some state variables
@ -344,13 +344,13 @@ export class D3ForceGraph implements GraphRenderer {
} while ((el = el && el.parentNode)); } while ((el = el && el.parentNode));
} }
private zoomed() { private zoomed(event: any) {
this.zoomTransform = { this.zoomTransform = {
x: d3Event.transform.x, x: event.transform.x,
y: d3Event.transform.y, y: event.transform.y,
k: d3Event.transform.k k: event.transform.k
}; };
this.g.attr("transform", d3Event.transform); this.g.attr("transform", event.transform);
} }
private instantiateSimulation() { private instantiateSimulation() {
@ -719,17 +719,17 @@ export class D3ForceGraph implements GraphRenderer {
}) })
.call( .call(
drag() drag()
.on("start", (d: D3Node) => { .on("start", ((e: D3DragEvent<SVGGElement, D3Node, unknown>, d: D3Node) => {
return this.dragstarted(d); return this.dragstarted(d, e);
}) }) as any)
.on("drag", (d: D3Node) => { .on("drag", ((e: D3DragEvent<SVGGElement, D3Node, unknown>, d: D3Node) => {
return this.dragged(d); return this.dragged(d, e);
}) }) as any)
.on("end", (d: D3Node) => { .on("end", ((e: D3DragEvent<SVGGElement, D3Node, unknown>, d: D3Node) => {
return this.dragended(d); return this.dragended(d, e);
}) }) as any)
) )
.on("mouseover", (d: D3Node) => { .on("mouseover", (_: MouseEvent, d: D3Node) => {
if (this.isHighlightDisabled || this.selectedNode || this.isDragging) { if (this.isHighlightDisabled || this.selectedNode || this.isDragging) {
return; return;
} }
@ -737,7 +737,7 @@ export class D3ForceGraph implements GraphRenderer {
this.highlightNode(this, d); this.highlightNode(this, d);
this.simulation.stop(); this.simulation.stop();
}) })
.on("mouseout", (d: D3Node) => { .on("mouseout", (_: MouseEvent, d: D3Node) => {
if (this.isHighlightDisabled || this.selectedNode || this.isDragging) { if (this.isHighlightDisabled || this.selectedNode || this.isDragging) {
return; return;
} }
@ -765,17 +765,17 @@ export class D3ForceGraph implements GraphRenderer {
.attr("aria-label", (d: D3Node) => { .attr("aria-label", (d: D3Node) => {
return this.retrieveNodeCaption(d); return this.retrieveNodeCaption(d);
}) })
.on("dblclick", function(d: D3Node) { .on("dblclick", function(_: MouseEvent, d: D3Node) {
// this is the <g> element // this is the <g> element
self.onNodeClicked(this.parentNode, d); self.onNodeClicked(this.parentNode, d);
}) })
.on("click", function(d: D3Node) { .on("click", function(_: MouseEvent, d: D3Node) {
// this is the <g> element // this is the <g> element
self.onNodeClicked(this.parentNode, d); self.onNodeClicked(this.parentNode, d);
}) })
.on("keypress", function(d: D3Node) { .on("keypress", function(event: KeyboardEvent, d: D3Node) {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) { if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation(); event.stopPropagation();
// this is the <g> element // this is the <g> element
self.onNodeClicked(this.parentNode, d); self.onNodeClicked(this.parentNode, d);
} }
@ -850,24 +850,24 @@ export class D3ForceGraph implements GraphRenderer {
return `Next page of nodes for ${this.retrieveNodeCaption(d)}`; return `Next page of nodes for ${this.retrieveNodeCaption(d)}`;
}) })
.attr("tabindex", 0) .attr("tabindex", 0)
.on("click", function(d: D3Node) { .on("click", ((_: MouseEvent, d: D3Node) => {
self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE); self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE);
}) }) as any)
.on("dblclick", function(d: D3Node) { .on("dblclick", ((_: MouseEvent, d: D3Node) => {
self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE); self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE);
}) }) as any)
.on("keypress", function(d: D3Node) { .on("keypress", ((event: KeyboardEvent, d: D3Node) => {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) { if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation(); event.stopPropagation();
self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE); self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE);
} }
}) }) as any)
.on("mouseover", function(d: D3Node) { .on("mouseover", ((e: MouseEvent, d: D3Node) => {
select(this).classed("active", true); select(e.target as any).classed("active", true);
}) }) as any)
.on("mouseout", function(d: D3Node) { .on("mouseout", ((e: MouseEvent, d: D3Node) => {
select(this).classed("active", false); select(e.target as any).classed("active", false);
}) }) as any)
.attr("visibility", (d: D3Node) => (!d._outEAllLoaded || !d._inEAllLoaded ? "visible" : "hidden")); .attr("visibility", (d: D3Node) => (!d._outEAllLoaded || !d._inEAllLoaded ? "visible" : "hidden"));
parent parent
.append("use") .append("use")
@ -879,24 +879,24 @@ export class D3ForceGraph implements GraphRenderer {
return `Previous page of nodes for ${this.retrieveNodeCaption(d)}`; return `Previous page of nodes for ${this.retrieveNodeCaption(d)}`;
}) })
.attr("tabindex", 0) .attr("tabindex", 0)
.on("click", function(d: D3Node) { .on("click", ((_: MouseEvent, d: D3Node) => {
self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE); self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE);
}) }) as any)
.on("dblclick", function(d: D3Node) { .on("dblclick", ((_: MouseEvent, d: D3Node) => {
self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE); self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE);
}) }) as any)
.on("keypress", function(d: D3Node) { .on("keypress", ((event: KeyboardEvent, d: D3Node) => {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) { if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation(); event.stopPropagation();
self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE); self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE);
} }
}) }) as any)
.on("mouseover", function(d: D3Node) { .on("mouseover", ((e: MouseEvent, d: D3Node) => {
select(this).classed("active", true); select(e.target as any).classed("active", true);
}) }) as any)
.on("mouseout", function(d: D3Node) { .on("mouseout", ((e: MouseEvent, d: D3Node) => {
select(this).classed("active", false); select(e.target as any).classed("active", false);
}) }) as any)
.attr("visibility", (d: D3Node) => .attr("visibility", (d: D3Node) =>
!d._pagination || d._pagination.currentPage.start !== 0 ? "visible" : "hidden" !d._pagination || d._pagination.currentPage.start !== 0 ? "visible" : "hidden"
); );
@ -975,24 +975,24 @@ export class D3ForceGraph implements GraphRenderer {
return `Load adjacent nodes for ${this.retrieveNodeCaption(d)}`; return `Load adjacent nodes for ${this.retrieveNodeCaption(d)}`;
}) })
.attr("tabindex", 0) .attr("tabindex", 0)
.on("click", function(d: D3Node) { .on("click", ((_: MouseEvent, d: D3Node) => {
self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE); self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE);
}) }) as any)
.on("dblclick", function(d: D3Node) { .on("dblclick", ((_: MouseEvent, d: D3Node) => {
self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE); self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE);
}) }) as any)
.on("keypress", function(d: D3Node) { .on("keypress", ((event: KeyboardEvent, d: D3Node) => {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) { if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation(); event.stopPropagation();
self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE); self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE);
} }
}) }) as any)
.on("mouseover", function(d: D3Node) { .on("mouseover", ((e: MouseEvent, d: D3Node) => {
select(this).classed("active", true); select(e.target as any).classed("active", true);
}) }) as any)
.on("mouseout", function(d: D3Node) { .on("mouseout", ((e: MouseEvent, d: D3Node) => {
select(this).classed("active", false); select(e.target as any).classed("active", false);
}); }) as any);
} }
/** /**
@ -1053,7 +1053,7 @@ export class D3ForceGraph implements GraphRenderer {
if (index < 0 || index >= D3ForceGraph.MAX_COLOR_NB) { if (index < 0 || index >= D3ForceGraph.MAX_COLOR_NB) {
index = D3ForceGraph.MAX_COLOR_NB - 1; index = D3ForceGraph.MAX_COLOR_NB - 1;
} }
return D3ForceGraph.COLOR_SCHEME_20(index.toString()); return D3ForceGraph.COLOR_SCHEME(index.toString());
} }
/** /**
@ -1071,23 +1071,23 @@ export class D3ForceGraph implements GraphRenderer {
} }
} }
private dragstarted(d: D3Node) { private dragstarted(d: D3Node, event: D3DragEvent<SVGGElement, D3Node, unknown>) {
this.isDragging = true; this.isDragging = true;
if (!d3Event.active) { if (!event.active) {
this.simulation.alphaTarget(0.3).restart(); this.simulation.alphaTarget(0.3).restart();
} }
d.fx = d.x; d.fx = d.x;
d.fy = d.y; d.fy = d.y;
} }
private dragged(d: D3Node) { private dragged(d: D3Node, event: D3DragEvent<SVGGElement, D3Node, unknown>) {
d.fx = d3Event.x; d.fx = event.x;
d.fy = d3Event.y; d.fy = event.y;
} }
private dragended(d: D3Node) { private dragended(d: D3Node, event: D3DragEvent<SVGGElement, D3Node, unknown>) {
this.isDragging = false; this.isDragging = false;
if (!d3Event.active) { if (!event.active) {
this.simulation.alphaTarget(0); this.simulation.alphaTarget(0);
} }

View File

@ -13,6 +13,7 @@ import {
makeAppRecord, makeAppRecord,
makeCommsRecord, makeCommsRecord,
makeContentsRecord, makeContentsRecord,
makeEditorsRecord,
makeEntitiesRecord, makeEntitiesRecord,
makeHostsRecord, makeHostsRecord,
makeJupyterHostRecord, makeJupyterHostRecord,
@ -34,6 +35,7 @@ import configureStore from "./NotebookComponent/store";
import { Notification } from "react-notification-system"; import { Notification } from "react-notification-system";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { configOption, createConfigCollection, defineConfigOption } from "@nteract/mythic-configuration";
export type KernelSpecsDisplay = { name: string; displayName: string }; export type KernelSpecsDisplay = { name: string; displayName: string };
@ -112,20 +114,15 @@ export class NotebookClientV2 {
host: jupyterHostRecord host: jupyterHostRecord
// TODO: tamitta: notificationSystem.addNotification was removed, do we need a substitute? // TODO: tamitta: notificationSystem.addNotification was removed, do we need a substitute?
}), }),
comms: makeCommsRecord(),
config: Immutable.Map({
theme: "light",
editorType: params.cellEditorType || "codemirror",
autoSaveInterval: params.autoSaveInterval || Constants.Notebook.autoSaveIntervalMs
}),
core: makeStateRecord({ core: makeStateRecord({
currentKernelspecsRef: kernelspecsRef, currentKernelspecsRef: kernelspecsRef,
entities: makeEntitiesRecord({ entities: makeEntitiesRecord({
editors: makeEditorsRecord({}),
hosts: makeHostsRecord({ hosts: makeHostsRecord({
byRef: Immutable.Map<string, HostRecord>().set(this.contentHostRef, jupyterHostRecord) byRef: Immutable.Map<string, HostRecord>().set(this.contentHostRef, jupyterHostRecord)
}), }),
comms: makeCommsRecord(),
contents: makeContentsRecord({ contents: makeContentsRecord({
// byRef: Immutable.Map<string, ContentRecord>().set(this.contentRef, record)
byRef: Immutable.Map<string, ContentRecord>() byRef: Immutable.Map<string, ContentRecord>()
}), }),
transforms: makeTransformsRecord({ transforms: makeTransformsRecord({
@ -238,6 +235,24 @@ export class NotebookClientV2 {
}; };
this.store = configureStore(initialState, params.contentProvider, traceErrorFct, [cacheKernelSpecsMiddleware]); this.store = configureStore(initialState, params.contentProvider, traceErrorFct, [cacheKernelSpecsMiddleware]);
// Additional configuration
this.store.dispatch(configOption("editorType").action(params.cellEditorType ?? "monaco"));
this.store.dispatch(
configOption("autoSaveInterval").action(params.autoSaveInterval ?? Constants.Notebook.autoSaveIntervalMs)
);
createConfigCollection({
key: "monaco"
});
defineConfigOption({
label: "Show Line numbers",
key: "monaco.lineNumbers",
values: [
{ label: "Yes", value: true },
{ label: "No", value: false }
],
defaultValue: true
});
} }
/** /**

View File

@ -196,7 +196,6 @@ export class NotebookComponentBootstrapper {
this.getStore().dispatch( this.getStore().dispatch(
actions.createCellBelow({ actions.createCellBelow({
cellType: "code", cellType: "code",
source: "",
contentRef: this.contentRef contentRef: this.contentRef
}) })
); );

View File

@ -1,6 +1,6 @@
import { StringUtils } from "../../../../../Utils/StringUtils"; import { StringUtils } from "../../../../../Utils/StringUtils";
import { actions, AppState, ContentRef, selectors } from "@nteract/core"; import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import { MonacoEditorProps } from "@nteract/monaco-editor"; import { IMonacoProps as MonacoEditorProps } from "@nteract/monaco-editor";
import * as React from "react"; import * as React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
@ -21,7 +21,7 @@ interface MappedStateProps {
mimetype: string; mimetype: string;
text: string; text: string;
contentRef: ContentRef; contentRef: ContentRef;
theme: string; // "light" | "dark"; theme?: "light" | "dark";
} }
interface MappedDispatchProps { interface MappedDispatchProps {
@ -65,8 +65,10 @@ export class TextFile extends React.PureComponent<TextFileProps, TextFileState>
return ( return (
<EditorContainer className="nteract-editor" style={{ position: "static" }}> <EditorContainer className="nteract-editor" style={{ position: "static" }}>
<Editor <Editor
id={"no-cell-id-for-single-editor"}
contentRef={this.props.contentRef}
theme={this.props.theme === "dark" ? "vs-dark" : "vs"} theme={this.props.theme === "dark" ? "vs-dark" : "vs"}
mode={this.props.mimetype} language={"plaintext"}
editorFocused editorFocused
value={this.props.text} value={this.props.text}
onChange={this.handleChange.bind(this)} onChange={this.handleChange.bind(this)}
@ -97,8 +99,7 @@ function makeMapStateToTextFileProps(
return { return {
contentRef, contentRef,
mimetype: content.mimetype != null ? content.mimetype : "text/plain", mimetype: content.mimetype != null ? content.mimetype : "text/plain",
text, text
theme: selectors.currentTheme(state)
}; };
}; };
return mapStateToTextFileProps; return mapStateToTextFileProps;

View File

@ -153,7 +153,7 @@ const mapDispatchToProps = (dispatch: Dispatch, ownProps: ContentsProps): object
}, },
COPY_CELL: () => dispatch(actions.copyCell({ contentRef })), COPY_CELL: () => dispatch(actions.copyCell({ contentRef })),
CREATE_CELL_ABOVE: () => dispatch(actions.createCellAbove({ cellType: "code", contentRef })), CREATE_CELL_ABOVE: () => dispatch(actions.createCellAbove({ cellType: "code", contentRef })),
CREATE_CELL_BELOW: () => dispatch(actions.createCellBelow({ cellType: "code", source: "", contentRef })), CREATE_CELL_BELOW: () => dispatch(actions.createCellBelow({ cellType: "code", contentRef })),
CUT_CELL: () => dispatch(actions.cutCell({ contentRef })), CUT_CELL: () => dispatch(actions.cutCell({ contentRef })),
DELETE_CELL: () => dispatch(actions.deleteCell({ contentRef })), DELETE_CELL: () => dispatch(actions.deleteCell({ contentRef })),
EXECUTE_ALL_CELLS: () => dispatch(actions.executeAllCells({ contentRef })), EXECUTE_ALL_CELLS: () => dispatch(actions.executeAllCells({ contentRef })),

View File

@ -1,6 +1,6 @@
import * as Immutable from "immutable"; import * as Immutable from "immutable";
import { ActionsObservable, StateObservable } from "redux-observable"; import { StateObservable } from "redux-observable";
import { Subject, empty } from "rxjs"; import { Subject, of } from "rxjs";
import { toArray } from "rxjs/operators"; import { toArray } from "rxjs/operators";
import { makeNotebookRecord } from "@nteract/commutable"; import { makeNotebookRecord } from "@nteract/commutable";
import { actions, state } from "@nteract/core"; import { actions, state } from "@nteract/core";
@ -124,7 +124,7 @@ describe("launchWebSocketKernelEpic", () => {
const kernelSpecName = "kernelspecname"; const kernelSpecName = "kernelspecname";
const sessionId = "sessionId"; const sessionId = "sessionId";
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -192,7 +192,7 @@ describe("launchWebSocketKernelEpic", () => {
const kernelSpecName = "kernelspecname"; const kernelSpecName = "kernelspecname";
const sessionId = "sessionId"; const sessionId = "sessionId";
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -247,7 +247,7 @@ describe("launchWebSocketKernelEpic", () => {
const kernelSpecName = "kernelspecname"; const kernelSpecName = "kernelspecname";
const sessionId = "sessionId"; const sessionId = "sessionId";
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -298,7 +298,7 @@ describe("launchWebSocketKernelEpic", () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const cwd = "/"; const cwd = "/";
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -398,7 +398,7 @@ describe("launchWebSocketKernelEpic", () => {
it("launches supported kernel in kernelspecs", async () => { it("launches supported kernel in kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -421,7 +421,7 @@ describe("launchWebSocketKernelEpic", () => {
it("launches undefined kernel uses default kernel from kernelspecs", async () => { it("launches undefined kernel uses default kernel from kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -445,7 +445,7 @@ describe("launchWebSocketKernelEpic", () => {
it("launches unsupported kernel uses default kernel from kernelspecs", async () => { it("launches unsupported kernel uses default kernel from kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -469,7 +469,7 @@ describe("launchWebSocketKernelEpic", () => {
it("launches unsupported kernel uses kernelspecs with similar name", async () => { it("launches unsupported kernel uses kernelspecs with similar name", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = ActionsObservable.of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
contentRef, contentRef,
kernelRef, kernelRef,
@ -499,7 +499,7 @@ describe("autoStartKernelEpic", () => {
it("automatically starts kernel when content fetch is successful if kernelRef is defined", async () => { it("automatically starts kernel when content fetch is successful if kernelRef is defined", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = ActionsObservable.of( const action$ = of(
actions.fetchContentFulfilled({ actions.fetchContentFulfilled({
contentRef, contentRef,
kernelRef, kernelRef,
@ -527,7 +527,7 @@ describe("autoStartKernelEpic", () => {
it("Don't start kernel when content fetch is successful if kernelRef is not defined", async () => { it("Don't start kernel when content fetch is successful if kernelRef is not defined", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>(), initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = ActionsObservable.of( const action$ = of(
actions.fetchContentFulfilled({ actions.fetchContentFulfilled({
contentRef, contentRef,
kernelRef: undefined, kernelRef: undefined,

View File

@ -1,6 +1,6 @@
import { EMPTY, merge, of, timer, concat, Subject, Subscriber, Observable, Observer } from "rxjs"; import { EMPTY, merge, of, timer, concat, Subject, Subscriber, Observable, Observer } from "rxjs";
import { webSocket } from "rxjs/webSocket"; import { webSocket } from "rxjs/webSocket";
import { ActionsObservable, StateObservable } from "redux-observable"; import { StateObservable } from "redux-observable";
import { ofType } from "redux-observable"; import { ofType } from "redux-observable";
import { import {
mergeMap, mergeMap,
@ -65,7 +65,7 @@ const logToTelemetry = (state: CdbAppState, title: string, error?: string) => {
* @param state$ * @param state$
*/ */
const addInitialCodeCellEpic = ( const addInitialCodeCellEpic = (
action$: ActionsObservable<actions.FetchContentFulfilled>, action$: Observable<actions.FetchContentFulfilled>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<{} | actions.CreateCellBelow> => { ): Observable<{} | actions.CreateCellBelow> => {
return action$.pipe( return action$.pipe(
@ -104,7 +104,7 @@ const addInitialCodeCellEpic = (
* @param state$ * @param state$
*/ */
export const autoStartKernelEpic = ( export const autoStartKernelEpic = (
action$: ActionsObservable<actions.FetchContentFulfilled>, action$: Observable<actions.FetchContentFulfilled>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<{} | actions.CreateCellBelow> => { ): Observable<{} | actions.CreateCellBelow> => {
return action$.pipe( return action$.pipe(
@ -157,7 +157,7 @@ const formWebSocketURL = (serverConfig: NotebookServiceConfig, kernelId: string,
* Override from kernel-lifecycle to improve code mirror language intellisense * Override from kernel-lifecycle to improve code mirror language intellisense
* @param action$ * @param action$
*/ */
export const acquireKernelInfoEpic = (action$: ActionsObservable<actions.NewKernelAction>) => { export const acquireKernelInfoEpic = (action$: Observable<actions.NewKernelAction>) => {
return action$.pipe( return action$.pipe(
ofType(actions.LAUNCH_KERNEL_SUCCESSFUL), ofType(actions.LAUNCH_KERNEL_SUCCESSFUL),
switchMap((action: actions.NewKernelAction) => { switchMap((action: actions.NewKernelAction) => {
@ -310,7 +310,7 @@ const connect = (serverConfig: NotebookServiceConfig, kernelID: string, sessionI
* @param state$ * @param state$
*/ */
export const launchWebSocketKernelEpic = ( export const launchWebSocketKernelEpic = (
action$: ActionsObservable<actions.LaunchKernelByNameAction>, action$: Observable<actions.LaunchKernelByNameAction>,
state$: StateObservable<CdbAppState> state$: StateObservable<CdbAppState>
) => { ) => {
return action$.pipe( return action$.pipe(
@ -422,7 +422,7 @@ export const launchWebSocketKernelEpic = (
* TODO: Remove this epic once the /restart endpoint is implemented. * TODO: Remove this epic once the /restart endpoint is implemented.
*/ */
export const restartWebSocketKernelEpic = ( export const restartWebSocketKernelEpic = (
action$: ActionsObservable<actions.RestartKernel | actions.NewKernelAction>, action$: Observable<actions.RestartKernel | actions.NewKernelAction>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
) => ) =>
action$.pipe( action$.pipe(
@ -532,7 +532,7 @@ export const restartWebSocketKernelEpic = (
* @param state$ * @param state$
*/ */
const changeWebSocketKernelEpic = ( const changeWebSocketKernelEpic = (
action$: ActionsObservable<actions.ChangeKernelByName>, action$: Observable<actions.ChangeKernelByName>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
) => { ) => {
return action$.pipe( return action$.pipe(
@ -614,7 +614,7 @@ const changeWebSocketKernelEpic = (
* @param state$ * @param state$
*/ */
const focusInitialCodeCellEpic = ( const focusInitialCodeCellEpic = (
action$: ActionsObservable<actions.CreateCellAppend>, action$: Observable<actions.CreateCellAppend>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<{} | actions.FocusCell> => { ): Observable<{} | actions.FocusCell> => {
return action$.pipe( return action$.pipe(
@ -652,10 +652,7 @@ const focusInitialCodeCellEpic = (
* @param action$ * @param action$
* @param state$ * @param state$
*/ */
const notificationsToUserEpic = ( const notificationsToUserEpic = (action$: Observable<any>, state$: StateObservable<CdbAppState>): Observable<{}> => {
action$: ActionsObservable<any>,
state$: StateObservable<CdbAppState>
): Observable<{}> => {
return action$.pipe( return action$.pipe(
ofType( ofType(
actions.RESTART_KERNEL_SUCCESSFUL, actions.RESTART_KERNEL_SUCCESSFUL,
@ -705,7 +702,7 @@ const notificationsToUserEpic = (
* @param state$ * @param state$
*/ */
const handleKernelConnectionLostEpic = ( const handleKernelConnectionLostEpic = (
action$: ActionsObservable<actions.UpdateDisplayFailed>, action$: Observable<actions.UpdateDisplayFailed>,
state$: StateObservable<CdbAppState> state$: StateObservable<CdbAppState>
): Observable<CdbActions.UpdateKernelRestartDelayAction | actions.RestartKernel | {}> => { ): Observable<CdbActions.UpdateKernelRestartDelayAction | actions.RestartKernel | {}> => {
return action$.pipe( return action$.pipe(
@ -766,7 +763,7 @@ const handleKernelConnectionLostEpic = (
* @param state$ * @param state$
*/ */
export const cleanKernelOnConnectionLostEpic = ( export const cleanKernelOnConnectionLostEpic = (
action$: ActionsObservable<actions.UpdateDisplayFailed>, action$: Observable<actions.UpdateDisplayFailed>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<actions.KillKernelSuccessful> => { ): Observable<actions.KillKernelSuccessful> => {
return action$.pipe( return action$.pipe(
@ -789,7 +786,7 @@ export const cleanKernelOnConnectionLostEpic = (
* @param state$ * @param state$
*/ */
const executeFocusedCellAndFocusNextEpic = ( const executeFocusedCellAndFocusNextEpic = (
action$: ActionsObservable<CdbActions.ExecuteFocusedCellAndFocusNextAction>, action$: Observable<CdbActions.ExecuteFocusedCellAndFocusNextAction>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<{} | actions.FocusNextCellEditor> => { ): Observable<{} | actions.FocusNextCellEditor> => {
return action$.pipe( return action$.pipe(
@ -829,7 +826,7 @@ function getUserPuid(): string {
* @param state$ * @param state$
*/ */
const closeUnsupportedMimetypesEpic = ( const closeUnsupportedMimetypesEpic = (
action$: ActionsObservable<actions.FetchContentFulfilled>, action$: Observable<actions.FetchContentFulfilled>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<{}> => { ): Observable<{}> => {
return action$.pipe( return action$.pipe(
@ -858,7 +855,7 @@ const closeUnsupportedMimetypesEpic = (
* @param state$ * @param state$
*/ */
const closeContentFailedToFetchEpic = ( const closeContentFailedToFetchEpic = (
action$: ActionsObservable<actions.FetchContentFailed>, action$: Observable<actions.FetchContentFailed>,
state$: StateObservable<AppState> state$: StateObservable<AppState>
): Observable<{}> => { ): Observable<{}> => {
return action$.pipe( return action$.pipe(

View File

@ -1,36 +1,22 @@
import { AppState, epics as coreEpics, reducers, IContentProvider } from "@nteract/core"; import { AppState, epics as coreEpics, reducers, IContentProvider } from "@nteract/core";
import { import { compose, Store, AnyAction, Middleware, Dispatch, MiddlewareAPI } from "redux";
applyMiddleware, import { createEpicMiddleware, Epic } from "redux-observable";
combineReducers,
compose,
createStore,
Store,
AnyAction,
Middleware,
Dispatch,
MiddlewareAPI
} from "redux";
import { combineEpics, createEpicMiddleware, Epic, ActionsObservable } from "redux-observable";
import { allEpics } from "./epics"; import { allEpics } from "./epics";
import { coreReducer, cdbReducer } from "./reducers"; import { coreReducer, cdbReducer } from "./reducers";
import { catchError } from "rxjs/operators"; import { catchError } from "rxjs/operators";
import { Observable } from "rxjs";
import { configuration } from "@nteract/mythic-configuration";
import { makeConfigureStore } from "@nteract/myths";
import { CdbAppState } from "./types";
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore( export default function configureStore(
initialState: Partial<AppState>, initialState: Partial<CdbAppState>,
contentProvider: IContentProvider, contentProvider: IContentProvider,
onTraceFailure: (title: string, message: string) => void, onTraceFailure: (title: string, message: string) => void,
customMiddlewares?: Middleware<{}, any, Dispatch<AnyAction>>[] customMiddlewares?: Middleware<{}, any, Dispatch<AnyAction>>[]
): Store<AppState, AnyAction> { ): Store<CdbAppState, AnyAction> {
const rootReducer = combineReducers({
app: reducers.app,
comms: reducers.comms,
config: reducers.config,
core: coreReducer,
cdb: cdbReducer
});
/** /**
* Catches errors in reducers * Catches errors in reducers
*/ */
@ -46,7 +32,7 @@ export default function configureStore(
}; };
const protect = (epic: Epic) => { const protect = (epic: Epic) => {
return (action$: ActionsObservable<any>, state$: any, dependencies: any) => return (action$: Observable<any>, state$: any, dependencies: any) =>
epic(action$, state$, dependencies).pipe( epic(action$, state$, dependencies).pipe(
catchError((error, caught) => { catchError((error, caught) => {
traceFailure("Epic failure", error); traceFailure("Epic failure", error);
@ -64,9 +50,8 @@ export default function configureStore(
} }
}; };
const combineAndProtectEpics = (epics: Epic[]): Epic => { const protectEpics = (epics: Epic[]): Epic[] => {
const protectedEpics = epics.map(epic => protect(epic)); return epics.map(epic => protect(epic));
return combineEpics<Epic>(...protectedEpics);
}; };
// This list needs to be consistent and in sync with core.allEpics until we figure // This list needs to be consistent and in sync with core.allEpics until we figure
@ -93,20 +78,23 @@ export default function configureStore(
coreEpics.publishToBookstoreAfterSave, coreEpics.publishToBookstoreAfterSave,
coreEpics.sendInputReplyEpic coreEpics.sendInputReplyEpic
]; ];
const rootEpic = combineAndProtectEpics([...filteredCoreEpics, ...allEpics]);
const epicMiddleware = createEpicMiddleware({ dependencies: { contentProvider } });
let middlewares: Middleware[] = [epicMiddleware];
// TODO: tamitta: errorMiddleware was removed, do we need a substitute?
if (customMiddlewares) { const mythConfigureStore = makeConfigureStore<CdbAppState>()({
middlewares = middlewares.concat(customMiddlewares); packages: [configuration],
} reducers: {
middlewares.push(catchErrorMiddleware); app: reducers.app,
core: coreReducer as any,
cdb: cdbReducer
},
epics: protectEpics([...filteredCoreEpics, ...allEpics]),
epicDependencies: { contentProvider },
epicMiddleware: [catchErrorMiddleware],
enhancer: composeEnhancers
});
const store = createStore(rootReducer, initialState, composeEnhancers(applyMiddleware(...middlewares))); const store = mythConfigureStore(initialState as any);
epicMiddleware.run(rootEpic);
// TODO Fix typing issue here: createStore() output type doesn't quite match AppState // TODO Fix typing issue here: createStore() output type doesn't quite match AppState
return store as Store<AppState, AnyAction>; // return store as Store<AppState, AnyAction>;
return store as any;
} }

View File

@ -10,7 +10,8 @@ import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import { actions, ContentRef } from "@nteract/core"; import { actions, ContentRef } from "@nteract/core";
import loadTransform from "../NotebookComponent/loadTransform"; import loadTransform from "../NotebookComponent/loadTransform";
import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-editors/codemirror"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor";
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import "./NotebookReadOnlyRenderer.less"; import "./NotebookReadOnlyRenderer.less";
export interface NotebookRendererProps { export interface NotebookRendererProps {
@ -19,19 +20,6 @@ export interface NotebookRendererProps {
hidePrompts?: boolean; hidePrompts?: boolean;
} }
interface PassedEditorProps {
id: string;
contentRef: ContentRef;
editorFocused: boolean;
value: string;
channels: any;
kernelStatus: string;
theme: string;
onChange: (text: string) => void;
onFocusChange: (focused: boolean) => void;
className: string;
}
/** /**
* This is the class that uses nteract to render a read-only notebook. * This is the class that uses nteract to render a read-only notebook.
*/ */
@ -73,8 +61,8 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
{{ {{
prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef), prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef),
editor: { editor: {
codemirror: (props: PassedEditorProps) => monaco: (props: PassedEditorProps) =>
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} /> this.props.hideInputs ? <></> : <MonacoEditor readOnly={true} {...props} editorType={"monaco"} />
} }
}} }}
</CodeCell> </CodeCell>
@ -90,8 +78,8 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
<RawCell id={id} contentRef={contentRef} cell_type="raw"> <RawCell id={id} contentRef={contentRef} cell_type="raw">
{{ {{
editor: { editor: {
codemirror: (props: PassedEditorProps) => monaco: (props: PassedEditorProps) =>
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} /> this.props.hideInputs ? <></> : <MonacoEditor {...props} readOnly={true} editorType={"monaco"} />
} }
}} }}
</RawCell> </RawCell>

View File

@ -3,7 +3,8 @@ import "./base.css";
import "./default.css"; import "./default.css";
import { RawCell, Cells, CodeCell, MarkdownCell } from "@nteract/stateful-components"; import { RawCell, Cells, CodeCell, MarkdownCell } from "@nteract/stateful-components";
import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-editors/codemirror"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor";
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import Prompt from "./Prompt"; import Prompt from "./Prompt";
import { promptContent } from "./PromptContent"; import { promptContent } from "./PromptContent";
@ -42,19 +43,6 @@ interface NotebookRendererDispatchProps {
type NotebookRendererProps = NotebookRendererBaseProps & NotebookRendererDispatchProps; type NotebookRendererProps = NotebookRendererBaseProps & NotebookRendererDispatchProps;
interface PassedEditorProps {
id: string;
contentRef: ContentRef;
editorFocused: boolean;
value: string;
channels: any;
kernelStatus: string;
theme: string;
onChange: (text: string) => void;
onFocusChange: (focused: boolean) => void;
className: string;
}
const decorate = (id: string, contentRef: ContentRef, cell_type: CellType, children: React.ReactNode) => { const decorate = (id: string, contentRef: ContentRef, cell_type: CellType, children: React.ReactNode) => {
const Cell = () => ( const Cell = () => (
<DraggableCell id={id} contentRef={contentRef}> <DraggableCell id={id} contentRef={contentRef}>
@ -115,9 +103,7 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
<CodeCell id={id} contentRef={contentRef} cell_type="code"> <CodeCell id={id} contentRef={contentRef} cell_type="code">
{{ {{
editor: { editor: {
codemirror: (props: PassedEditorProps) => ( monaco: (props: PassedEditorProps) => <MonacoEditor {...props} editorType={"monaco"} />
<CodeMirrorEditor {...props} lineNumbers={true} />
)
}, },
prompt: ({ id, contentRef }: { id: CellId; contentRef: ContentRef }) => ( prompt: ({ id, contentRef }: { id: CellId; contentRef: ContentRef }) => (
<Prompt id={id} contentRef={contentRef} isHovered={false}> <Prompt id={id} contentRef={contentRef} isHovered={false}>
@ -135,6 +121,9 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
"markdown", "markdown",
<MarkdownCell id={id} contentRef={contentRef} cell_type="markdown"> <MarkdownCell id={id} contentRef={contentRef} cell_type="markdown">
{{ {{
editor: {
monaco: (props: PassedEditorProps) => <MonacoEditor {...props} editorType={"monaco"} />
},
toolbar: () => <CellToolbar id={id} contentRef={contentRef} /> toolbar: () => <CellToolbar id={id} contentRef={contentRef} />
}} }}
</MarkdownCell> </MarkdownCell>
@ -147,6 +136,9 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
"raw", "raw",
<RawCell id={id} contentRef={contentRef} cell_type="raw"> <RawCell id={id} contentRef={contentRef} cell_type="raw">
{{ {{
editor: {
monaco: (props: PassedEditorProps) => <MonacoEditor {...props} editorType={"monaco"} />
},
toolbar: () => <CellToolbar id={id} contentRef={contentRef} /> toolbar: () => <CellToolbar id={id} contentRef={contentRef} />
}} }}
</RawCell> </RawCell>

View File

@ -150,9 +150,9 @@ const mapDispatchToProps = (
): DispatchProps => ({ ): DispatchProps => ({
executeCell: () => dispatch(actions.executeCell({ id, contentRef })), executeCell: () => dispatch(actions.executeCell({ id, contentRef })),
insertCodeCellAbove: () => dispatch(actions.createCellAbove({ id, contentRef, cellType: "code" })), insertCodeCellAbove: () => dispatch(actions.createCellAbove({ id, contentRef, cellType: "code" })),
insertCodeCellBelow: () => dispatch(actions.createCellBelow({ id, contentRef, cellType: "code", source: "" })), insertCodeCellBelow: () => dispatch(actions.createCellBelow({ id, contentRef, cellType: "code" })),
insertTextCellAbove: () => dispatch(actions.createCellAbove({ id, contentRef, cellType: "markdown" })), insertTextCellAbove: () => dispatch(actions.createCellAbove({ id, contentRef, cellType: "markdown" })),
insertTextCellBelow: () => dispatch(actions.createCellBelow({ id, contentRef, cellType: "markdown", source: "" })), insertTextCellBelow: () => dispatch(actions.createCellBelow({ id, contentRef, cellType: "markdown" })),
moveCell: (destinationId: CellId, above: boolean) => moveCell: (destinationId: CellId, above: boolean) =>
dispatch(actions.moveCell({ id, contentRef, destinationId, above })), dispatch(actions.moveCell({ id, contentRef, destinationId, above })),
clearOutputs: () => dispatch(actions.clearOutputs({ id, contentRef })), clearOutputs: () => dispatch(actions.clearOutputs({ id, contentRef })),

View File

@ -1,15 +1,16 @@
/** /**
* JupyterLab applications based on jupyterLab components * JupyterLab applications based on jupyterLab components
*/ */
import { ServerConnection, TerminalSession } from "@jupyterlab/services"; import { ServerConnection, TerminalManager } from "@jupyterlab/services";
import { Terminal } from "@jupyterlab/terminal"; import { Terminal } from "@jupyterlab/terminal";
import { Panel, Widget } from "@phosphor/widgets"; import { Panel, Widget } from "@phosphor/widgets";
export class JupyterLabAppFactory { export class JupyterLabAppFactory {
public static async createTerminalApp(serverSettings: ServerConnection.ISettings) { public static async createTerminalApp(serverSettings: ServerConnection.ISettings) {
const session = await TerminalSession.startNew({ const manager = new TerminalManager({
serverSettings: serverSettings serverSettings: serverSettings
}); });
const session = await manager.startNew();
const term = new Terminal(session, { theme: "dark", shutdownOnClose: true }); const term = new Terminal(session, { theme: "dark", shutdownOnClose: true });
if (!term) { if (!term) {
@ -21,7 +22,7 @@ export class JupyterLabAppFactory {
term.addClass("terminalWidget"); term.addClass("terminalWidget");
let panel = new Panel(); let panel = new Panel();
panel.addWidget(term); panel.addWidget(term as any);
panel.id = "main"; panel.id = "main";
// Attach the widget to the dom. // Attach the widget to the dom.

View File

@ -36,6 +36,7 @@ const createServerSettings = (urlVars: { [key: string]: string }): ServerConnect
options = { options = {
baseUrl: server, baseUrl: server,
token: urlVars[TerminalQueryParams.Token], token: urlVars[TerminalQueryParams.Token],
appendToken: true,
init: { body }, init: { body },
fetch: window.parent.fetch fetch: window.parent.fetch
}; };

View File

@ -1,4 +1,11 @@
import { armRequest } from "./request"; import { armRequest } from "./request";
import fetch from "node-fetch";
interface Global {
Headers: unknown;
}
((global as unknown) as Global).Headers = ((fetch as unknown) as Global).Headers;
describe("ARM request", () => { describe("ARM request", () => {
it("should call window.fetch", async () => { it("should call window.fetch", async () => {