From 87b03e762290bbd9d5c0b2658c27ea0fc552e469 Mon Sep 17 00:00:00 2001 From: Mariusz Jurowicz Date: Mon, 27 Nov 2017 16:11:26 +0100 Subject: [PATCH] #5268 beakerx jupyterlab extension (#6219) * 6152 upgrade dependencies to use the v7 named modules * #6103 python API for output containers (#6150) * #6103 python API for output containers * #6103 python API for output containers * #6103 fix NaN in plots * polish doc * #6154 python TableDisplay constructor should handle array of dicts (#6165) * #6154 python TableDisplay constructor should handle array of dicts * add examples to doc * #6155 table in python output container displayed as text (#6167) * #6155 table in python output container displayed as text * add link to python output containers, polish that tutorial * #5980 #5982 #5995 #5996 #5997 Added support for INDEX. (#6161) * #5980 Added support for INDEX. * #5980 Fixed test. * Simplify if statemant. * #5980 Added backward compatibility for customizing styles. * #5980 Setting up default value. * #5980 Rollback backward compatibility. Fixed tests. * update doc to match code * #runAll: prevent receiving new messages before code execution ends (#6169) * #5268 beakerx jupyterlab extension * #5268 move lab extension out of beakerx npm module * #5268 fix TableDisplay widget for Lab * #5268 reimplement contextMenu for plots * add test for jupyter console (#6337) * #5268 tableDisplay context menu * #5268 add table cell context menu * #5268 add unique id for table header menu commands * #5268 update jslab README file --- beakerx/js/package.json | 1 - beakerx/js/src/Plot.js | 5 +- beakerx/js/src/TableDisplay.js | 1 - beakerx/js/src/contextMenu/BkoContextMenu.ts | 186 ++++++++++++++++++ beakerx/js/src/embed.js | 4 +- beakerx/js/src/extension.js | 1 - beakerx/js/src/plot/bko-plot.css | 2 + beakerx/js/src/plot/combinedPlotScope.js | 14 +- .../plot/contextMenu/createSaveAsMenuItems.ts | 46 +++++ .../src/plot/contextMenu/plotContextMenu.ts | 32 +++ beakerx/js/src/plot/plotScope.js | 16 +- .../interfaces/contextMenuItemInterface.ts | 22 +++ .../js/src/shared/interfaces/menuInterface.ts | 20 ++ .../interfaces/menuItemInterface.ts} | 4 + .../contextMenu/createTableCellMenuItems.ts | 104 ++++++++++ .../contextMenu/createTableMenuItems.ts | 43 ++++ .../contextMenu/tableContextMenu.ts | 34 ++++ .../js/src/tableDisplay/css/datatables.scss | 118 +++++------ .../tableHeaderMenu/ColumnMenu.ts | 3 +- .../tableHeaderMenu/HeaderMenu.ts | 24 +-- .../tableDisplay/tableHeaderMenu/IndexMenu.ts | 4 +- .../tableHeaderMenu/createFormatMenuItems.ts | 2 +- .../tableHeaderMenu/createIndexMenuItems.ts | 2 +- beakerx/js/src/tableDisplay/tableScope.js | 79 +------- beakerx/js/src/tableDisplay/tableUtils.js | 1 + beakerx/js/webpack.config.js | 22 +++ beakerx/js/yarn.lock | 4 - beakerx/jslab/.gitignore | 2 + beakerx/jslab/.npmignore | 1 + beakerx/jslab/README.md | 20 ++ beakerx/jslab/index.js | 31 +++ beakerx/jslab/package.json | 26 +++ doc/StartHere.ipynb | 0 .../twosigma/beakerx/kernel/comm/Comm.java | 6 +- .../beakerx/widgets/DisplayWidgetTest.java | 2 +- 35 files changed, 706 insertions(+), 176 deletions(-) create mode 100644 beakerx/js/src/contextMenu/BkoContextMenu.ts create mode 100644 beakerx/js/src/plot/contextMenu/createSaveAsMenuItems.ts create mode 100644 beakerx/js/src/plot/contextMenu/plotContextMenu.ts create mode 100644 beakerx/js/src/shared/interfaces/contextMenuItemInterface.ts create mode 100644 beakerx/js/src/shared/interfaces/menuInterface.ts rename beakerx/js/src/{tableDisplay/tableHeaderMenu/MenuItemInterface.ts => shared/interfaces/menuItemInterface.ts} (89%) create mode 100644 beakerx/js/src/tableDisplay/contextMenu/createTableCellMenuItems.ts create mode 100644 beakerx/js/src/tableDisplay/contextMenu/createTableMenuItems.ts create mode 100644 beakerx/js/src/tableDisplay/contextMenu/tableContextMenu.ts create mode 100644 beakerx/jslab/.gitignore create mode 100644 beakerx/jslab/.npmignore create mode 100644 beakerx/jslab/README.md create mode 100644 beakerx/jslab/index.js create mode 100644 beakerx/jslab/package.json create mode 100644 doc/StartHere.ipynb diff --git a/beakerx/js/package.json b/beakerx/js/package.json index ec8b229deb..beeb068007 100644 --- a/beakerx/js/package.json +++ b/beakerx/js/package.json @@ -56,7 +56,6 @@ "datatables.net-keytable-dt": "^2.3.2", "datatables.net-select": "^1.2.2", "flatpickr": "^2.6.3", - "jquery-contextmenu": "^2.4.5", "jquery-ui": "^1.12.1", "moment": "^2.17.1", "moment-timezone": "^0.5.13", diff --git a/beakerx/js/src/Plot.js b/beakerx/js/src/Plot.js index 1ff70efca9..7b272ba151 100644 --- a/beakerx/js/src/Plot.js +++ b/beakerx/js/src/Plot.js @@ -73,10 +73,9 @@ var PlotView = widgets.DOMWidgetView.extend({ if (that._currentScope instanceof CombinedPlotScope) { that._currentScope.scopes.forEach(function(scope) { scope.destroy(); - }) - } else { - that._currentScope.destroy(); + }); } + that._currentScope.destroy(); setTimeout(function() { that._currentScope = null; }); }); diff --git a/beakerx/js/src/TableDisplay.js b/beakerx/js/src/TableDisplay.js index 0a175e1e4d..11decbb6ac 100644 --- a/beakerx/js/src/TableDisplay.js +++ b/beakerx/js/src/TableDisplay.js @@ -24,7 +24,6 @@ require('datatables.net-dt/css/jquery.dataTables.css'); require('datatables.net-colreorder-dt/css/colReorder.dataTables.css'); require('datatables.net-fixedcolumns-dt/css/fixedColumns.dataTables.css'); require('datatables.net-keytable-dt/css/keyTable.dataTables.css'); -require('jquery-contextmenu/dist/jquery.contextMenu.css'); require('./tableDisplay/css/datatables.scss'); var TableDisplayModel = widgets.DOMWidgetModel.extend({ diff --git a/beakerx/js/src/contextMenu/BkoContextMenu.ts b/beakerx/js/src/contextMenu/BkoContextMenu.ts new file mode 100644 index 0000000000..ebe95e9bdb --- /dev/null +++ b/beakerx/js/src/contextMenu/BkoContextMenu.ts @@ -0,0 +1,186 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare var lab: { contextMenu: ContextMenu }; + +import { ContextMenu, Menu } from '@phosphor/widgets'; +import { CommandRegistry } from '@phosphor/commands'; +import { IDisposable } from '@phosphor/disposable'; +import MenuItem from "shared/interfaces/contextMenuItemInterface"; +import _ from 'underscore'; +import MenuInterface from '../shared/interfaces/menuInterface' + +interface addItem { + addItem: Function +} + +export default abstract class BkoContextMenu implements MenuInterface { + protected scope: any; + protected commands: CommandRegistry; + protected menuItems: Menu.IItem[] = []; + protected inLab: boolean; + protected disposables: IDisposable[] = []; + protected event: MouseEvent; + + public contextMenu: ContextMenu; + + constructor(scope: any) { + this.inLab = this.isInLab(); + this.scope = scope; + + this.handleContextMenu = this.handleContextMenu.bind(this); + this.buildMenu(); + } + + protected abstract buildMenu(): void; + + protected isInLab(): boolean { + let inLab = false; + + try { + inLab = lab && lab.contextMenu instanceof ContextMenu; + } catch (e) {} + + return inLab; + } + + protected handleContextMenu(event: MouseEvent): void { + this.event = event; + + if (this.inLab) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + this.open(event); + } + + open(e: MouseEvent): void { + this.contextMenu.open(e); + } + + protected buildLabMenu(): void { + this.commands = lab.contextMenu.menu.commands; + this.contextMenu = lab.contextMenu; + } + + protected buildBkoMenu(): void { + this.commands = new CommandRegistry(); + this.contextMenu = new ContextMenu({ commands: this.commands }); + this.contextMenu.menu.addClass('bko-table-menu'); + } + + protected createItems(items: MenuItem[], menu: addItem): void { + for (let i = 0, ien = items.length; i < ien; i++) { + this.createMenuItem(items[i], menu); + } + } + + protected createMenuItem(menuItem: MenuItem, menu: addItem): void { + const subitems = (typeof menuItem.items == 'function') ? menuItem.items() : menuItem.items; + const hasSubitems = _.isArray(subitems) && subitems.length; + + menuItem.separator && this.addSeparatorItem(menuItem, menu); + !hasSubitems && this.menuItems.push(this.addMenuItem(menuItem, menu)); + hasSubitems && this.menuItems.push(this.addSubmenuItem(menuItem, menu, subitems)); + } + + protected addMenuItem(menuItem: MenuItem, menu: addItem): Menu.IItem { + this.addCommand(menuItem); + this.addKeyBinding(menuItem); + + return menu.addItem({ command: menuItem.id, selector: menuItem.selector }); + } + + protected addSeparatorItem(menuItem: MenuItem, menu: addItem): Menu.IItem { + return menu.addItem({ type: 'separator', selector: menuItem.selector }); + } + + protected addSubmenuItem(menuItem: MenuItem, menu: addItem, subitems: MenuItem[]): Menu.IItem { + return menu.addItem({ + type: 'submenu', + submenu: this.createSubmenu(menuItem, subitems), + selector: menuItem.selector + }); + } + + protected addCommand(menuItem: MenuItem): void { + if (this.commands.hasCommand(menuItem.id)) { + return; + } + + const self = this; + this.disposables.push(this.commands.addCommand(menuItem.id, { + label: menuItem.title, + usage: menuItem.tooltip || '', + iconClass: () => menuItem.icon ? menuItem.icon : '', + isVisible: menuItem.isVisible, + execute: (): void => { + if (menuItem.action && typeof menuItem.action == 'function') { + menuItem.action(self.event); + } + } + })); + } + + protected addKeyBinding(menuItem: MenuItem): void { + if (!menuItem.shortcut) { + return; + } + + this.disposables.push(this.commands.addKeyBinding({ + keys: [menuItem.shortcut], + selector: menuItem.selector, + command: menuItem.id + })); + } + + protected createSubmenu(menuItem: MenuItem, subitems: MenuItem[]): Menu { + const submenu = new Menu({ commands: this.commands }); + + !this.inLab && submenu.addClass('bko-table-menu'); + submenu.title.label = menuItem.title; + submenu.setHidden(false); + + this.createItems(subitems, submenu); + + return submenu; + } + + protected bindEvents(): void { + this.scope.element[0].addEventListener('contextmenu', this.handleContextMenu); + } + + destroy(): void { + this.unbind(); + this.removeMenuItems(); + this.dispose(); + } + + removeMenuItems(): void { + this.menuItems.forEach(item => this.contextMenu.menu.removeItem(item)); + } + + dispose(): void { + this.disposables.forEach(disposable => disposable.dispose()); + } + + unbind(): void { + this.scope.element[0].removeEventListener('contextmenu', this.handleContextMenu); + } +} diff --git a/beakerx/js/src/embed.js b/beakerx/js/src/embed.js index ba8ebf793e..830cae92de 100644 --- a/beakerx/js/src/embed.js +++ b/beakerx/js/src/embed.js @@ -10,7 +10,6 @@ module.exports = {}; require('./../src/shared/style/beakerx.scss'); require('./../src/plot/bko-combinedplot.css'); require('./../src/plot/bko-plot.css'); -require('jquery-contextmenu/dist/jquery.contextMenu.min.css'); var loadedModules = [ require("./Plot"), @@ -18,7 +17,8 @@ var loadedModules = [ require("./EasyForm"), require("./TabView"), require("./GridView"), - require("./CyclingDisplayBox") + require("./CyclingDisplayBox"), + require("./HTMLPre").default ]; for (var i in loadedModules) { diff --git a/beakerx/js/src/extension.js b/beakerx/js/src/extension.js index a3c48d3968..4078778eab 100644 --- a/beakerx/js/src/extension.js +++ b/beakerx/js/src/extension.js @@ -36,7 +36,6 @@ __webpack_public_path__ = document.querySelector('body').getAttribute('data-base require('./../src/shared/style/beakerx.scss'); require('./../src/plot/bko-combinedplot.css'); require('./../src/plot/bko-plot.css'); -require('jquery-contextmenu/dist/jquery.contextMenu.min.css'); define([ 'services/config', diff --git a/beakerx/js/src/plot/bko-plot.css b/beakerx/js/src/plot/bko-plot.css index 56212e2b3c..9b0bcdb386 100644 --- a/beakerx/js/src/plot/bko-plot.css +++ b/beakerx/js/src/plot/bko-plot.css @@ -14,6 +14,8 @@ * limitations under the License. */ +@import "~@phosphor/widgets/style/menu.css"; + .plot-plotcontainer { background-color: #FEFEFE; position: relative; diff --git a/beakerx/js/src/plot/combinedPlotScope.js b/beakerx/js/src/plot/combinedPlotScope.js index d39d61889f..94c5975b89 100644 --- a/beakerx/js/src/plot/combinedPlotScope.js +++ b/beakerx/js/src/plot/combinedPlotScope.js @@ -23,7 +23,6 @@ define([ './combinedPlotFormatter', './../shared/bkUtils', './chartExtender', - 'jquery-contextmenu', './plotScope' ], function( _, @@ -34,7 +33,6 @@ define([ combinedPlotFormatter, bkUtils, bkoChartExtender, - contextMenu, PlotScope ) { @@ -256,6 +254,10 @@ define([ ); }; + CombinedPlotScope.prototype.doDestroy = function() { + this.contextMenu && this.contextMenu.destroy(); + }; + CombinedPlotScope.prototype.init = function() { var self = this; self.canvas = self.element.find("canvas")[0]; @@ -264,12 +266,8 @@ define([ self.id = 'bko-plot-' + bkUtils.generateId(6); self.element.find('.combplot-plotcontainer').attr('id', self.id); self.saveAsMenuContainer = $('#' + self.id); - $.contextMenu({ - selector: '#' + self.id, - zIndex: 3, - items: plotUtils.getSavePlotAsContextMenuItems(self), - trigger: 'none' - }); + var ContextMenu = require('./contextMenu/plotContextMenu').default; + self.contextMenu = new ContextMenu(self); self.standardizeData(); self.preparePlotModels(); diff --git a/beakerx/js/src/plot/contextMenu/createSaveAsMenuItems.ts b/beakerx/js/src/plot/contextMenu/createSaveAsMenuItems.ts new file mode 100644 index 0000000000..8cfd567d39 --- /dev/null +++ b/beakerx/js/src/plot/contextMenu/createSaveAsMenuItems.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import MenuItem from '../../shared/interfaces/contextMenuItemInterface'; + +export default function createSaveAsMenuItems(scope: any): MenuItem[] { + const selector = `#${scope.id}`; + + return [ + { + id: `beakerx:saveAsSvg:${scope.id}`, + title: 'Save as SVG', + action: () => scope.saveAsSvg(), + selector + }, + { + id: `beakerx:saveAsPng:${scope.id}`, + title: 'Save as PNG', + action: () => scope.saveAsPng(), + selector + }, + { + id: `beakerx:saveAsHighDpiPng:${scope.id}`, + title: 'Save as PNG at high DPI...', + items: [2,3,4,5].map((scale) => ({ + id: `beakerx:saveAsHighDpiPng:${scope.id}:${scale}`, + title: scale + 'x', + action: () => scope.saveAsPng(scale) + })), + selector + } + ]; +} diff --git a/beakerx/js/src/plot/contextMenu/plotContextMenu.ts b/beakerx/js/src/plot/contextMenu/plotContextMenu.ts new file mode 100644 index 0000000000..c9653a0058 --- /dev/null +++ b/beakerx/js/src/plot/contextMenu/plotContextMenu.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import createSaveAsMenuItems from './createSaveAsMenuItems'; +import BkoContextMenu from '../../contextMenu/BkoContextMenu'; + +export default class PlotContextMenu extends BkoContextMenu { + constructor(scope: any) { + super(scope); + } + + protected buildMenu(): void { + this.inLab ? this.buildLabMenu() : this.buildBkoMenu(); + + const menuItems = createSaveAsMenuItems(this.scope); + this.createItems(menuItems, this.contextMenu); + this.bindEvents(); + } +} diff --git a/beakerx/js/src/plot/plotScope.js b/beakerx/js/src/plot/plotScope.js index 36416b67ff..18fc4fe787 100644 --- a/beakerx/js/src/plot/plotScope.js +++ b/beakerx/js/src/plot/plotScope.js @@ -28,8 +28,7 @@ define([ './../shared/bkUtils', './../shared/bkHelper', './gradientlegend', - './chartExtender', - 'jquery-contextmenu' + './chartExtender' ], function( _, $, @@ -44,8 +43,7 @@ define([ bkUtils, bkHelper, GradientLegend, - bkoChartExtender, - contextMenu + bkoChartExtender ) { function PlotScope(wrapperId) { @@ -2201,12 +2199,8 @@ define([ if (!self.model.disableContextMenu) { self.saveAsMenuContainer = $('div#'+self.wrapperId+' #' + self.id); // init context menu for 'save as...' - $.contextMenu({ - selector: 'div#'+self.wrapperId+' #' + self.id, - zIndex: 3, - items: plotUtils.getSavePlotAsContextMenuItems(self), - trigger: 'none' - }); + var ContextMenu = require('./contextMenu/plotContextMenu.ts').default; + self.contextMenu = new ContextMenu(self); } else if (self.model && self.model.getSaveAsMenuContainer) { self.saveAsMenuContainer = self.model.getSaveAsMenuContainer(); } @@ -2436,7 +2430,7 @@ define([ setTimeout(this.initProperties.bind(this)); - $.contextMenu('destroy', { selector: '#' + this.id}); + this.contextMenu && this.contextMenu.destroy(); }; PlotScope.prototype.getSvgToSave = function() { diff --git a/beakerx/js/src/shared/interfaces/contextMenuItemInterface.ts b/beakerx/js/src/shared/interfaces/contextMenuItemInterface.ts new file mode 100644 index 0000000000..766a8757c9 --- /dev/null +++ b/beakerx/js/src/shared/interfaces/contextMenuItemInterface.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import MenuItem from './menuItemInterface'; + +export default interface ContextMenuItem extends MenuItem { + selector: string + id: string, +} diff --git a/beakerx/js/src/shared/interfaces/menuInterface.ts b/beakerx/js/src/shared/interfaces/menuInterface.ts new file mode 100644 index 0000000000..2165d266a9 --- /dev/null +++ b/beakerx/js/src/shared/interfaces/menuInterface.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default interface Menu { + open: Function, + destroy: Function +} diff --git a/beakerx/js/src/tableDisplay/tableHeaderMenu/MenuItemInterface.ts b/beakerx/js/src/shared/interfaces/menuItemInterface.ts similarity index 89% rename from beakerx/js/src/tableDisplay/tableHeaderMenu/MenuItemInterface.ts rename to beakerx/js/src/shared/interfaces/menuItemInterface.ts index 14c578efdf..c8d1054d4a 100644 --- a/beakerx/js/src/tableDisplay/tableHeaderMenu/MenuItemInterface.ts +++ b/beakerx/js/src/shared/interfaces/menuItemInterface.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { CommandRegistry } from '@phosphor/commands'; + export default interface MenuItem { title: string, action?: Function, @@ -30,4 +32,6 @@ export default interface MenuItem { type?: string, tooltip?: string, updateLayout?: boolean, + isVisible?: CommandRegistry.CommandFunc, + args?: object } diff --git a/beakerx/js/src/tableDisplay/contextMenu/createTableCellMenuItems.ts b/beakerx/js/src/tableDisplay/contextMenu/createTableCellMenuItems.ts new file mode 100644 index 0000000000..a9f058784e --- /dev/null +++ b/beakerx/js/src/tableDisplay/contextMenu/createTableCellMenuItems.ts @@ -0,0 +1,104 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import MenuItem from '../../shared/interfaces/contextMenuItemInterface'; +import _ from 'underscore'; + +interface cellIndex { row: number, column: number } + +export default function createTableCellMenuItems(scope: any, model: any): MenuItem[] { + const selector = `#${scope.id} tbody td`; + + function getTableCellIndex(node: HTMLElement|null): cellIndex|null { + if (!node) { + return null; + } + + if (node instanceof HTMLTableCellElement) { + return scope.table.cell(node).index(); + } + + return getTableCellIndex(node.parentElement); + } + + function createFromModelContextMenuItems(): MenuItem[] { + if (_.isEmpty(model.contextMenuItems)) { + return []; + } + + return model.contextMenuItems.map(item => ({ + id: `${item}_${scope.id}`, + title: item, + selector: selector, + action: (event) => { + const index = getTableCellIndex(event.target); + + if (!index) { + return; + } + + scope.tableDisplayModel.send({ + event: 'CONTEXT_MENU_CLICK', + itemKey : item, + row : index.row, + column : index.column - 1 + }, scope.tableDisplayView.callbacks()); + } + })); + } + + function createFromModelContextMenuTags(): MenuItem[] { + const items: MenuItem[] = []; + + _.forEach(model.contextMenuTags, function(tag, name) { + if (!model.contextMenuTags.hasOwnProperty(name)) { + return; + } + + items.push({ + id: `${tag}_${scope.id}`, + title: name, + selector: selector, + action: function(event) { + const index = getTableCellIndex(event.target); + + if (!index) { + return; + } + + const params = { + actionType: 'CONTEXT_MENU_CLICK', + contextMenuItem: name, + row: index.row, + col: index.column - 1 + }; + + scope.tableDisplayModel.send({ + event: 'actiondetails', + params + }, scope.tableDisplayView.callbacks()); + } + }); + }); + + return items; + } + + return [ + ...createFromModelContextMenuItems(), + ...createFromModelContextMenuTags() + ] +} diff --git a/beakerx/js/src/tableDisplay/contextMenu/createTableMenuItems.ts b/beakerx/js/src/tableDisplay/contextMenu/createTableMenuItems.ts new file mode 100644 index 0000000000..8db603a028 --- /dev/null +++ b/beakerx/js/src/tableDisplay/contextMenu/createTableMenuItems.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import MenuItem from '../../shared/interfaces/contextMenuItemInterface'; + +export default function createTableMenuItems(scope: any): MenuItem[] { + const selector = `#${scope.id}_wrapper thead`; + + const rotateMenuItemCallback = () => { + scope.headersVertical = !scope.headersVertical; + scope.rotateHeader(); + scope.table.draw(); + }; + + return [ + { + id: `${scope.id}_verticalHeaders`, + title: 'vertical headers', + action: rotateMenuItemCallback, + isVisible: () => !scope.headersVertical, + selector + }, { + id: `${scope.id}_horizontalHeaders`, + title: 'horizontal headers', + action: rotateMenuItemCallback, + isVisible: () => !!scope.headersVertical, + selector + } + ] +} diff --git a/beakerx/js/src/tableDisplay/contextMenu/tableContextMenu.ts b/beakerx/js/src/tableDisplay/contextMenu/tableContextMenu.ts new file mode 100644 index 0000000000..0d54715dbb --- /dev/null +++ b/beakerx/js/src/tableDisplay/contextMenu/tableContextMenu.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import createTableContextMenuItems from './createTableMenuItems'; +import createTableCellMenuItems from './createTableCellMenuItems'; +import BkoContextMenu from '../../contextMenu/BkoContextMenu'; + +export default class TableContextMenu extends BkoContextMenu { + constructor(scope: any) { + super(scope); + } + + protected buildMenu(): void { + this.inLab ? this.buildLabMenu() : this.buildBkoMenu(); + + const menuItems = createTableContextMenuItems(this.scope); + const cellMenuItems = createTableCellMenuItems(this.scope, this.scope.model.getCellModel()); + this.createItems([ ...menuItems, ...cellMenuItems ], this.contextMenu); + this.bindEvents(); + } +} diff --git a/beakerx/js/src/tableDisplay/css/datatables.scss b/beakerx/js/src/tableDisplay/css/datatables.scss index cd6e030bbe..affd75e35c 100644 --- a/beakerx/js/src/tableDisplay/css/datatables.scss +++ b/beakerx/js/src/tableDisplay/css/datatables.scss @@ -14,7 +14,7 @@ * limitations under the License. */ -@import '~@phosphor/widgets/style/index.css'; +@import '~@phosphor/widgets/style/menu.css'; $border-color: #D4D0D0; $highlight-blue: #39a9ed; @@ -570,77 +570,81 @@ $fix-col-separator-width: 3px; overflow: visible; } -.p-Menu { +.bko-table-menu { background-color: #fff; border-radius: 2px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; -} - -.p-Menu-content { - border-collapse: collapse; - display: block; - position: relative; - top: 0; - left: 0; - overflow: visible; - width: 100%; - .p-Menu-item { + .p-Menu-content { + border-collapse: collapse; display: block; - font-weight: normal; - line-height: 1.42857143; - color: #333333; - cursor: pointer; + position: relative; + top: 0; + left: 0; + overflow: visible; + width: 100%; - &:hover { - color: #262626; - background-color: #f5f5f5; - } - } + .p-Menu-item { + display: block; + font-weight: normal; + line-height: 1.42857143; + color: #333333; + cursor: pointer; - .p-Menu-itemLabel { - padding: 3px; - white-space: nowrap; - display: inline-block; - } + &:hover { + color: #262626; + background-color: #f5f5f5; + } + } - .p-Menu-itemIcon, - .p-Menu-itemShortcut { - min-width: 22px; - position: static; - padding: 4px; - display: inline-block; - } + .p-Menu-itemLabel { + padding: 3px; + white-space: nowrap; + display: inline-block; + } - .p-Menu-itemSubmenuIcon { - display: inline-block; - position: absolute; - right: 0px; - padding-right: 10px; - margin-top: 3px; - } + .p-Menu-itemIcon, + .p-Menu-itemShortcut { + min-width: 22px; + position: static; + padding: 4px; + display: inline-block; + } - [data-type="submenu"] .p-Menu-itemSubmenuIcon { - &:after { + .p-Menu-itemSubmenuIcon { display: inline-block; - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - display: block; - content: "\f0da"; - float: right; - color: #333333; - margin-top: 2px; + position: absolute; + right: 0px; + padding-right: 10px; + margin-top: 3px; } - } - [data-type="separator"] { - border-bottom: 1px solid #ccc; - height: 5px; + [data-type="submenu"] > .p-Menu-itemSubmenuIcon { + background: none; + + &:after { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: block; + content: "\f0da"; + float: right; + color: #333333; + margin-top: 2px; + } + } + + [data-type="separator"] { + border-bottom: 1px solid #ccc; + height: 5px; + min-height: 0; + overflow: hidden; + } } } diff --git a/beakerx/js/src/tableDisplay/tableHeaderMenu/ColumnMenu.ts b/beakerx/js/src/tableDisplay/tableHeaderMenu/ColumnMenu.ts index 9ccf1b616b..9a5ea4e502 100644 --- a/beakerx/js/src/tableDisplay/tableHeaderMenu/ColumnMenu.ts +++ b/beakerx/js/src/tableDisplay/tableHeaderMenu/ColumnMenu.ts @@ -40,9 +40,10 @@ export default class ColumnMenu extends HeaderMenu { $(this.cell).append($trigger); this.columnIndex = self.getColumnIndex($trigger); - this.menu.addClass('bko-header-menu'); + this.menu.addClass('bko-table-menu'); this.menu.addClass('dropdown'); $(this.menu.contentNode).addClass('dropdown-menu'); + $(this.menu.contentNode).addClass('bko-table-menu-content'); this.createItems(menu.items, this.menu); } diff --git a/beakerx/js/src/tableDisplay/tableHeaderMenu/HeaderMenu.ts b/beakerx/js/src/tableDisplay/tableHeaderMenu/HeaderMenu.ts index 041be2e55b..9d0076bede 100644 --- a/beakerx/js/src/tableDisplay/tableHeaderMenu/HeaderMenu.ts +++ b/beakerx/js/src/tableDisplay/tableHeaderMenu/HeaderMenu.ts @@ -18,9 +18,10 @@ import $ from 'jquery'; import _ from 'underscore'; import { CommandRegistry } from '@phosphor/commands'; import Menu from './BkoMenu'; -import MenuItem from './MenuItemInterface'; +import MenuItem from '../../shared/interfaces/menuItemInterface'; +import MenuInterface from '../../shared/interfaces/menuInterface'; -export default abstract class HeaderMenu { +export default abstract class HeaderMenu implements MenuInterface { columnIndex: number; protected commands: CommandRegistry; @@ -44,15 +45,6 @@ export default abstract class HeaderMenu { protected abstract buildMenu($trigger?: any): void - protected destroy(): void { - this.scopeElement.off('mousedown.headermenu, keydown.HeaderMenu'); - $(this.menu.node).off('keyup.keyTable, change'); - $(document.body).off('click.table-headermenu'); - $(document).off('keydown.keyTable', this.handleKeydownEvent); - $(this.menu.node).off('keydown.HeaderMenu', '.dropdown-menu-search input', this.handleKeydownEvent) - this.menu.dispose(); - } - protected getMenuPosition($trigger: any) { const triggerHeight = $trigger.height() || 20; const triggerOffset = $trigger.offset(); @@ -91,6 +83,15 @@ export default abstract class HeaderMenu { } } + destroy(): void { + this.scopeElement.off('mousedown.headermenu, keydown.HeaderMenu'); + $(this.menu.node).off('keyup.keyTable, change'); + $(document.body).off('click.table-headermenu'); + $(document).off('keydown.keyTable', this.handleKeydownEvent); + $(this.menu.node).off('keydown.HeaderMenu', '.dropdown-menu-search input', this.handleKeydownEvent) + this.menu.dispose(); + } + toggleMenu($trigger: any, submenuIndex?: number): void { $trigger.hasClass(this.TRIGGER_CLASS_OPENED) ? $trigger.removeClass(this.TRIGGER_CLASS_OPENED) : @@ -153,6 +154,7 @@ export default abstract class HeaderMenu { const submenu = new Menu({ commands: this.commands }); submenu.addClass('dropdown-submenu'); + submenu.addClass('bko-table-menu'); submenu.title.label = menuItem.title; menuItem.enableItemsFiltering && this.addItemsFiltering(submenu); diff --git a/beakerx/js/src/tableDisplay/tableHeaderMenu/IndexMenu.ts b/beakerx/js/src/tableDisplay/tableHeaderMenu/IndexMenu.ts index 9cc6f88142..6a07e062fc 100644 --- a/beakerx/js/src/tableDisplay/tableHeaderMenu/IndexMenu.ts +++ b/beakerx/js/src/tableDisplay/tableHeaderMenu/IndexMenu.ts @@ -15,7 +15,7 @@ */ import createIndexMenuItems from './createIndexMenuItems'; -import MenuItem from './MenuItemInterface'; +import MenuItem from '../../shared/interfaces/menuItemInterface'; import HeaderMenu from './HeaderMenu'; export default class IndexMenu extends HeaderMenu { @@ -31,11 +31,13 @@ export default class IndexMenu extends HeaderMenu { protected buildMenu($trigger: any): void { this.menu.addClass('bko-header-menu'); + this.menu.addClass('bko-table-menu'); this.menu.addClass('dropdown'); const self = this; this.menu.contentNode.classList.add('dropdown-menu'); + this.menu.contentNode.classList.add('bko-table-menu-content'); this.scopeElement.off('mousedown.headermenu', `#${$trigger.attr('id')}`); this.scopeElement.on('mousedown.headermenu', `#${$trigger.attr('id')}`, function(event) { event.preventDefault(); diff --git a/beakerx/js/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts b/beakerx/js/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts index 48ec261efa..690037f595 100644 --- a/beakerx/js/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts +++ b/beakerx/js/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import MenuItem from "./MenuItemInterface"; +import MenuItem from "../../shared/interfaces/menuItemInterface"; import _ from 'underscore'; declare function require(moduleName: string): any; diff --git a/beakerx/js/src/tableDisplay/tableHeaderMenu/createIndexMenuItems.ts b/beakerx/js/src/tableDisplay/tableHeaderMenu/createIndexMenuItems.ts index 8238d881cb..08086edb6e 100644 --- a/beakerx/js/src/tableDisplay/tableHeaderMenu/createIndexMenuItems.ts +++ b/beakerx/js/src/tableDisplay/tableHeaderMenu/createIndexMenuItems.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import MenuItem from './MenuItemInterface'; +import MenuItem from '../../shared/interfaces/menuItemInterface'; import createFormatSubitems from './createFormatMenuItems'; export default function createIndexMenuItems(scope: any): MenuItem[] { diff --git a/beakerx/js/src/tableDisplay/tableScope.js b/beakerx/js/src/tableDisplay/tableScope.js index 65ef5e204f..ce90d4bf18 100644 --- a/beakerx/js/src/tableDisplay/tableScope.js +++ b/beakerx/js/src/tableDisplay/tableScope.js @@ -29,7 +29,6 @@ define([ './cellHighlighters', './../shared/bkHelper', './consts', - 'jquery-contextmenu', 'jquery-ui/ui/widgets/tooltip', './tableUtils' ], function( @@ -47,7 +46,6 @@ define([ cellHighlighters, bkHelper, tableConsts, - contextMenu, tooltip, tableUtils ) { @@ -334,8 +332,7 @@ define([ self.element.find(".bko-table-use-pagination").remove(); $body.tooltip('instance') && $body.tooltip('destroy'); - $.contextMenu('destroy', '#' + self.id + ' tbody td'); - $.contextMenu('destroy', '#' + self.id +'_wrapper thead'); + self.contextMenu && self.contextMenu.destroy(); $body.off('click.bko-dt-container'); $document.off('contextmenu.bko-dt-header'); $tableContainer.find('.dataTables_scrollHead').off('scroll'); @@ -363,10 +360,10 @@ define([ } if (all) { + self.actualtype = []; self.timeStrings = undefined; self.tz = undefined; self.types = undefined; - self.actualtype = undefined; self.actualalign = undefined; self.data = undefined; self.update = undefined; @@ -614,40 +611,6 @@ define([ } self.setCellHighlighters(); - - self.contextMenuItems = {}; - if (!_.isEmpty(model.contextMenuItems)) { - _.forEach(model.contextMenuItems, function(item) { - self.contextMenuItems[item] = { - name: item, - callback: function(itemKey, options) { - var index = self.table.cell(options.$trigger.get(0)).index(); - self.tableDisplayModel.send({event: 'CONTEXT_MENU_CLICK', itemKey : itemKey, row : index.row, column : index.column - 1}, self.tableDisplayView.callbacks()); - } - } - }); - } - - if (!_.isEmpty(model.contextMenuTags)) { - _.forEach(model.contextMenuTags, function(tag, name) { - if (model.contextMenuTags.hasOwnProperty(name)) { - self.contextMenuItems[name] = { - name: name, - callback: function(itemKey, options) { - var index = self.table.cell(options.$trigger.get(0)).index(); - var params = { - actionType: 'CONTEXT_MENU_CLICK', - contextMenuItem: itemKey, - row: index.row, - col: index.column - 1 - }; - self.tableDisplayModel.send({event: 'actiondetails', params: params}, self.tableDisplayView.callbacks()); - } - } - } - }); - } - self.doCreateData(model); self.doCreateTable(model); var $body = $(document.body); @@ -1461,39 +1424,11 @@ define([ init.scrollCollapse = true; } } + self.fixcreated = false; - if (!_.isEmpty(self.contextMenuItems)) { - $.contextMenu({ - selector: id +' tbody td', - items: self.contextMenuItems - }); - } - var rotateMenuItem = { - callback: function(itemKey, options) { - self.headersVertical = !!!self.headersVertical; - self.rotateHeader(); - self.table.draw(); - } - }; - $.contextMenu({ - selector: id +'_wrapper thead', - zIndex: 3, //to be over fixed headers - items: { - verticalHeaders: _.extend({}, rotateMenuItem, { - name: 'vertical headers', - visible: function(key, opt){ - return !!!self.headersVertical; - } - }), - horizontalHeaders: _.extend({}, rotateMenuItem, { - name: 'horizontal headers', - visible: function(key, opt){ - return !!self.headersVertical; - } - }) - } - }); + var ContextMenu = require('./contextMenu/tableContextMenu').default; + self.contextMenu = new ContextMenu(self); $(document).on('contextmenu.bko-dt-header', id +'_wrapper thead th', function(){ $(this).blur(); @@ -1561,14 +1496,16 @@ define([ var createColumnMenus = require('./tableHeaderMenu/createColumnMenus').default; self.columnMenus = createColumnMenus(self); - self.createTableMenuElements(); // $rootScope.$emit('beaker.resize'); //TODO check - handle resize? self.fixcols.fnRedrawLayout(); self.updateFixedColumnsSeparator(); setTimeout(function(){ if (!self.table) { return; } + + self.createTableMenuElements(); self.applyFilters(); + if (self.columnFilter) { self.table.columns().every(function(i) { var column = this; diff --git a/beakerx/js/src/tableDisplay/tableUtils.js b/beakerx/js/src/tableDisplay/tableUtils.js index 95a7e1aa16..15b2996eca 100644 --- a/beakerx/js/src/tableDisplay/tableUtils.js +++ b/beakerx/js/src/tableDisplay/tableUtils.js @@ -1,6 +1,7 @@ var $ = require('jquery'); var jQuery = require('jquery'); var _ = require('underscore'); +var moment = require('moment-timezone/builds/moment-timezone-with-data.min'); module.exports = { setJqExtentions: setJqExtentions, diff --git a/beakerx/js/webpack.config.js b/beakerx/js/webpack.config.js index cc02146808..2ac2997c99 100644 --- a/beakerx/js/webpack.config.js +++ b/beakerx/js/webpack.config.js @@ -154,5 +154,27 @@ module.exports = [ resolve: resolve, externals: externals, plugins: plugins + }, + {// Beakerx JupyterLab bundle + // + // This bundle is generally almost identical to the embeddable bundle + // + entry: './src/embed.js', + output: { + filename: 'index.js', + path: path.resolve(__dirname, '../jslab/lib/'), + libraryTarget: 'amd' + }, + module: { + rules: rules + }, + resolve: resolve, + externals: externals.concat([ + '@jupyter-widgets/jupyterlab-manager', + '@phosphor/widgets', + '@phosphor/commands', + '@phosphor/messaging' + ]), + plugins: plugins } ]; diff --git a/beakerx/js/yarn.lock b/beakerx/js/yarn.lock index 5b6b368709..327b86522b 100644 --- a/beakerx/js/yarn.lock +++ b/beakerx/js/yarn.lock @@ -1969,10 +1969,6 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -jquery-contextmenu@^2.4.5: - version "2.5.0" - resolved "https://registry.yarnpkg.com/jquery-contextmenu/-/jquery-contextmenu-2.5.0.tgz#940dbadbd0f2d7e0a0a8f1bbc5e71667d8257405" - jquery-ui@^1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51" diff --git a/beakerx/jslab/.gitignore b/beakerx/jslab/.gitignore new file mode 100644 index 0000000000..97dba69de2 --- /dev/null +++ b/beakerx/jslab/.gitignore @@ -0,0 +1,2 @@ +/lib +node_modules/ diff --git a/beakerx/jslab/.npmignore b/beakerx/jslab/.npmignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/beakerx/jslab/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/beakerx/jslab/README.md b/beakerx/jslab/README.md new file mode 100644 index 0000000000..f216a6924d --- /dev/null +++ b/beakerx/jslab/README.md @@ -0,0 +1,20 @@ +# BeakerX JupyterLab extension NPM module + +BeakerX: Beaker Extensions for JupyterLab. +This NPM module has the BeakerX widgets. +See http://BeakerX.com + +Install +------- +To install the experimental beakerx JupyterLab extension, install the Python package, make sure the Jupyter widgets extension is installed, and install the beakerx extension: + +``` +$ git clone https://github.com/twosigma/beakerx.git +$ conda create -y -n beakerx 'python>=3' nodejs pandas openjdk +$ source activate beakerx +$ (cd beakerx; pip install -e . --verbose) +$ beakerx-install +$ cd beakerx/beakerx/jslab +$ jupyter labextension install @jupyter-widgets/jupyterlab-manager # install the Jupyter widgets extension +$ jupyter labextension install . +``` diff --git a/beakerx/jslab/index.js b/beakerx/jslab/index.js new file mode 100644 index 0000000000..dba5ce0676 --- /dev/null +++ b/beakerx/jslab/index.js @@ -0,0 +1,31 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var beakerx = require('./lib/index.js'); +var jupyterlab_widgets = require('@jupyter-widgets/jupyterlab-manager'); + +module.exports = { + id: 'beakerx', + requires: [jupyterlab_widgets.INBWidgetExtension], + activate: function(app, widgets) { + widgets.registerWidget({ + name: 'beakerx', + version: beakerx.version, + exports: beakerx + }); + }, + autoStart: true +}; diff --git a/beakerx/jslab/package.json b/beakerx/jslab/package.json new file mode 100644 index 0000000000..92c9f5ff11 --- /dev/null +++ b/beakerx/jslab/package.json @@ -0,0 +1,26 @@ +{ + "name": "beakerx-jupyterlab", + "version": "0.1.0", + "description": "BeakerX: Beaker Extensions for JupyterLab", + "author": "Two Sigma Open Source, LLC", + "main": "index.js", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "" + }, + "keywords": [ + "ipywidgets", + "jupyterlab", + "jupyterlab extension" + ], + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "@jupyter-widgets/jupyterlab-manager": "^0.28.0" + }, + "jupyterlab": { + "extension": true + } +} diff --git a/doc/StartHere.ipynb b/doc/StartHere.ipynb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/kernel/base/src/main/java/com/twosigma/beakerx/kernel/comm/Comm.java b/kernel/base/src/main/java/com/twosigma/beakerx/kernel/comm/Comm.java index 07858f1977..3df7c0f339 100644 --- a/kernel/base/src/main/java/com/twosigma/beakerx/kernel/comm/Comm.java +++ b/kernel/base/src/main/java/com/twosigma/beakerx/kernel/comm/Comm.java @@ -202,7 +202,11 @@ public void send(JupyterMessages type) { message.setParentHeader(getParentMessage().getHeader()); } HashMap map = new HashMap<>(6); - map.put(COMM_ID, getCommId()); + + if (type != JupyterMessages.DISPLAY_DATA) { + map.put(COMM_ID, getCommId()); + } + map.put(DATA, data); map.put(METADATA, metadata); message.setContent(map); diff --git a/kernel/base/src/test/java/com/twosigma/beakerx/widgets/DisplayWidgetTest.java b/kernel/base/src/test/java/com/twosigma/beakerx/widgets/DisplayWidgetTest.java index a065bd85c1..824b7c5a3b 100644 --- a/kernel/base/src/test/java/com/twosigma/beakerx/widgets/DisplayWidgetTest.java +++ b/kernel/base/src/test/java/com/twosigma/beakerx/widgets/DisplayWidgetTest.java @@ -63,6 +63,6 @@ private void verifyCommDisplayMsg(IntSlider widget) { Message message = groovyKernel.getPublishedMessages().get(0); assertThat(getData(message).get(METHOD)).isEqualTo(DISPLAY_DATA.getName()); verifyDisplayMsg(message); - assertThat(getContent(message).get(COMM_ID)).isEqualTo(widget.getComm().getCommId()); + assertThat(getContent(message).get(COMM_ID)).isNull(); } } \ No newline at end of file