diff --git a/js/lab/src/plugin/gistPublish.ts b/js/lab/src/plugin/gistPublish.ts new file mode 100644 index 0000000000..66eb93f8be --- /dev/null +++ b/js/lab/src/plugin/gistPublish.ts @@ -0,0 +1,103 @@ +/* + * 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 * as $ from 'jquery'; +import { NotebookPanel } from "@jupyterlab/notebook"; +import { showDialog, Dialog, ToolbarButton } from '@jupyterlab/apputils'; + +const CONFIG = { + gistsUrl: 'https://api.github.com/gists', + nbviewerBaseUrl: 'https://nbviewer.jupyter.org/' +}; + +export function registerFeature(panel: NotebookPanel) { + addActionButton(panel); +} + +export function addActionButton(panel: NotebookPanel): void { + const action = { + className: 'fa fa-share-alt', + tooltip: 'Publish...', + onClick: () => openPublishDialog(panel) + }; + + panel.toolbar.insertItem(9,'publish', new ToolbarButton(action)); +} + +function openPublishDialog(panel: NotebookPanel) { + showDialog({ + title : 'Publish', + body : 'Publish to an anonymous Github Gist, and open in nbviewer?', + buttons: [ + Dialog.okButton({ label: 'OK' }), + Dialog.cancelButton({ label: 'Cancel' }) + ] + }) + .then(() => savePanelState(panel)) + .then(() => doPublish(panel)); +} + +function savePanelState(panel: NotebookPanel): Promise { + return new Promise((resolve, reject) => { + panel.context.save().then(() => { + console.log("widgets state has been saved"); + + if (!panel.isDisposed) { + resolve(); + } else { + reject(); + } + }, reject); + }); +} + +function doPublish(panel: NotebookPanel): void { + const nbjson = panel.notebook.model.toJSON(); + const filedata = {}; + + filedata[panel.context.contentsModel.name] = { + content : JSON.stringify(nbjson, undefined, 1) + }; + + const settings = { + type : 'POST', + headers : {}, + data : JSON.stringify({ + public : true, + files : filedata + }), + success : (data, status) => { + console.log("gist successfully published: " + data.id); + window.open(CONFIG.nbviewerBaseUrl + data.id); + }, + error : (jqXHR, status, err) => { + const errorMsg = jqXHR.readyState === 0 && !err ? 'NETWORK ERROR!' : err; + + console.log(errorMsg); + showErrorDialog(errorMsg); + } + }; + + $.ajax(CONFIG.gistsUrl, settings); +} + +function showErrorDialog(errorMsg) { + showDialog({ + title : 'Gist publication error', + body : `Uploading gist failed: ${errorMsg}`, + buttons: [ Dialog.okButton({ label: 'OK' }) ] + }); +} diff --git a/js/lab/src/plugin/gistPublish/index.ts b/js/lab/src/plugin/gistPublish/index.ts index ee1d39cc7b..ca51e83fb4 100644 --- a/js/lab/src/plugin/gistPublish/index.ts +++ b/js/lab/src/plugin/gistPublish/index.ts @@ -88,7 +88,7 @@ function doPublish(panel: NotebookPanel, personalAccessToken: string|null): void } }; - $.ajax(gistsUrl, settings).catch((jqXHR, status, err) => { + $.ajax(gistsUrl, settings).fail((jqXHR, status, err) => { let errorMsg = jqXHR.readyState === 0 && !err ? 'NETWORK ERROR!' : err; if (jqXHR.responseJSON && jqXHR.responseJSON.message) { diff --git a/js/notebook/.gitignore b/js/notebook/.gitignore index 785bc5e102..e862f2af9e 100644 --- a/js/notebook/.gitignore +++ b/js/notebook/.gitignore @@ -4,3 +4,4 @@ /dist node_modules/ /build/ +/.nyc_output diff --git a/js/notebook/package.json b/js/notebook/package.json index b6c64c2b93..9426429ae6 100644 --- a/js/notebook/package.json +++ b/js/notebook/package.json @@ -15,28 +15,46 @@ "ipython", "ipywidgets" ], + "nyc": { + "extension": [ + ".ts" + ], + "include": [ + "**/src/**/*.ts" + ] + }, "scripts": { "build": "yarn run build:dev", "build:prod": "webpack --config webpack.prod.js && yarn run build-lab-extension", "build:dev": "webpack --config webpack.dev.js && yarn run build-lab-extension", "build-lab-extension": "cd ../lab && npm install", "prepublish": "yarn run build:prod", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "TS_NODE_PROJECT=test mocha", + "coverage": "TS_NODE_PROJECT=test nyc mocha", "stats": "webpack --env production --profile --json > stats.json" }, "devDependencies": { + "@types/chai": "^4.1.1", "@types/jquery": "^3.3.0", + "@types/mocha": "^2.2.46", "base64-loader": "^1.0.0", + "chai": "^4.1.2", "css-loader": "^0.28.4", "file-loader": "^0.10.0", "fork-ts-checker-webpack-plugin": "^0.2.8", "html-loader": "^0.4.5", + "ignore-styles": "^5.0.1", + "jsdom": "^11.5.1", "json-loader": "^0.5.4", + "mocha": "^5.0.0", "node-sass": "^4.5.2", + "nyc": "^11.4.1", "sass-loader": "^6.0.5", + "sinon": "^4.2.0", "source-map-loader": "^0.2.1", "style-loader": "^0.18.1", "ts-loader": "^3.0.3", + "ts-node": "^4.1.0", "tsconfig-paths": "^3.1.1", "tsconfig-paths-webpack-plugin": "^2.0.0", "typescript": "^2.6.2", @@ -46,6 +64,8 @@ "webpack-merge": "^4.1.0" }, "dependencies": { + "@phosphor/datagrid": "^0.1.5", + "@phosphor/datastore": "^0.7.0", "@phosphor/widgets": "^1.5.0", "big.js": "^3.1.3", "bootstrap-sass": "^3.3.7", @@ -65,6 +85,7 @@ "jquery-ui": "^1.12.1", "moment": "^2.17.1", "moment-timezone": "^0.5.13", + "reselect": "^3.0.1", "underscore": "^1.8.3" } } diff --git a/js/notebook/src/TableDisplay.js b/js/notebook/src/TableDisplay.js index 11decbb6ac..2bfcb6a88b 100644 --- a/js/notebook/src/TableDisplay.js +++ b/js/notebook/src/TableDisplay.js @@ -15,9 +15,11 @@ */ var widgets = require('./widgets'); +var BeakerXApi = require('./tree/Utils/BeakerXApi').default; var _ = require('underscore'); var $ = require('jquery'); +var DataGridScope = require('./tableDisplay/dataGrid').DataGridScope; var TableScope = require('./tableDisplay/tableScope'); require('datatables.net-dt/css/jquery.dataTables.css'); @@ -87,6 +89,42 @@ var TableDisplayView = widgets.DOMWidgetView.extend({ }, initTableDisplay: function(data) { + var baseUrl = (Jupyter.notebook_list || Jupyter.notebook).base_url; + var api = new BeakerXApi(baseUrl); + var self = this; + + api.loadSettings().then(function(settings) { + if (!settings || !settings.ui_options || !settings.ui_options.use_data_grid) { + self.initDatatablesTable(data); + } else { + self.initDataGridTable(data); + } + }); + }, + + showWarning: function(data) { + var rowLength = data.rowLength; + var rowLimit = data.rowLimit; + var tmpl = '
' + + '

Note: table is too big to display. ' + + 'The limit is ' + rowLimit + ' rows, but this table has ' + rowLength + ' rows. ' + + 'The first 10000 rows are displayed as a preview.

'; + var tmplElement = $(tmpl); + tmplElement.appendTo(this.$el); + }, + + initDataGridTable: function(data) { + this._currentScope = new DataGridScope({ + element: this.el, + data: data, + widgetModel: this.model, + widgetView: this + }); + + this._currentScope.render(); + }, + + initDatatablesTable: function(data) { this._currentScope = new TableScope('wrap_'+this.model.model_id); var tmpl = this._currentScope.buildTemplate(); var tmplElement = $(tmpl); @@ -100,18 +138,6 @@ var TableDisplayView = widgets.DOMWidgetView.extend({ this._currentScope.run(); this._currentScope.initColumLimitModal(); this._currentScope.setWidgetView(this); - }, - - showWarning: function(data) { - var rowLength = data.rowLength; - var columnLength = data.columnNames.length; - var rowLimit = data.rowLimit; - var tmpl = '
' + - '

Note: table is too big to display. ' + - 'The limit is ' + rowLimit + ' rows, but this table has ' + rowLength + ' rows. ' + - 'The first 1000 rows are displayed as a preview.

'; - var tmplElement = $(tmpl); - tmplElement.appendTo(this.$el); } }); diff --git a/js/notebook/src/contextMenu/BkoContextMenu.ts b/js/notebook/src/contextMenu/BkoContextMenu.ts index ebe95e9bdb..33629a61f2 100644 --- a/js/notebook/src/contextMenu/BkoContextMenu.ts +++ b/js/notebook/src/contextMenu/BkoContextMenu.ts @@ -20,7 +20,6 @@ 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 { @@ -28,12 +27,13 @@ interface addItem { } export default abstract class BkoContextMenu implements MenuInterface { + event: MouseEvent; + protected scope: any; protected commands: CommandRegistry; protected menuItems: Menu.IItem[] = []; protected inLab: boolean; protected disposables: IDisposable[] = []; - protected event: MouseEvent; public contextMenu: ContextMenu; @@ -93,7 +93,7 @@ export default abstract class BkoContextMenu implements MenuInterface { protected createMenuItem(menuItem: MenuItem, menu: addItem): void { const subitems = (typeof menuItem.items == 'function') ? menuItem.items() : menuItem.items; - const hasSubitems = _.isArray(subitems) && subitems.length; + const hasSubitems = Array.isArray(subitems) && subitems.length; menuItem.separator && this.addSeparatorItem(menuItem, menu); !hasSubitems && this.menuItems.push(this.addMenuItem(menuItem, menu)); diff --git a/js/notebook/src/global.env.ts b/js/notebook/src/global.env.ts index deb8c7e1e9..3ed118252e 100644 --- a/js/notebook/src/global.env.ts +++ b/js/notebook/src/global.env.ts @@ -1,6 +1,7 @@ declare var BEAKERX_MODULE_VERSION: string; declare var require: Function; declare var Jupyter: any; +declare var beakerx: any; interface GlobalEnvironment { BEAKERX_MODULE_VERSION; diff --git a/js/notebook/src/shared/bkCoreManager.js b/js/notebook/src/shared/bkCoreManager.js index 280bdc44d9..1d5bfa6d6e 100644 --- a/js/notebook/src/shared/bkCoreManager.js +++ b/js/notebook/src/shared/bkCoreManager.js @@ -23,8 +23,8 @@ define([ var beakerObj = { //TODO MOCK - replace beakerObj: { prefs: { - outputColumnLimit: 50, - outputLineLimit: 1000, + outputColumnLimit: 500, + outputLineLimit: 100000, theme: { name: 'default', plotColors: [ diff --git a/js/notebook/src/shared/bkGlobals.js b/js/notebook/src/shared/bkGlobals.js index f856c29fed..cac4e4ae12 100644 --- a/js/notebook/src/shared/bkGlobals.js +++ b/js/notebook/src/shared/bkGlobals.js @@ -14,42 +14,40 @@ * limitations under the License. */ -define(function() { - return { - DEFAULT_EVALUATOR: 'JavaScript', - REQUIREJS_TIMEOUT: 30, - RECONNECT_TIMEOUT: 30 * 1000, // 30 seconds - CELL_INSTANTIATION_DISTANCE: 500, // in pixels - if the cell is closer than from the viewport it gets instantiated - EVENTS: { - RECONNECT_FAILED: 'reconnect-failed', - LANGUAGE_MANAGER_SHOW_SPINNER: 'language-manager-show-spinner', - LANGUAGE_MANAGER_HIDE_SPINNER: 'language-manager-hide-spinner', - DISCARD_LANGUAGE_SETTINGS: 'discard-language-settings', - HIGHLIGHT_EDITED_LANGUAGE_SETTINGS: 'highlight-edited-language-settings', - SET_LANGUAGE_SETTINGS_EDITED: 'set-language-settings-edited', - LANGUAGE_ADDED: 'languageAdded', - CELL_OUTPUT_EXPANDED: 'cell-output-expanded', - CELL_OUTPUT_LM_SHOWED: 'cell-output-lm-showed', - ADVANCED_MODE_TOGGLED: 'advanced-mode-toggled', - FILE_DROPPED: 'file-dropped' +module.exports = { + DEFAULT_EVALUATOR: 'JavaScript', + REQUIREJS_TIMEOUT: 30, + RECONNECT_TIMEOUT: 30 * 1000, // 30 seconds + CELL_INSTANTIATION_DISTANCE: 500, // in pixels - if the cell is closer than from the viewport it gets instantiated + EVENTS: { + RECONNECT_FAILED: 'reconnect-failed', + LANGUAGE_MANAGER_SHOW_SPINNER: 'language-manager-show-spinner', + LANGUAGE_MANAGER_HIDE_SPINNER: 'language-manager-hide-spinner', + DISCARD_LANGUAGE_SETTINGS: 'discard-language-settings', + HIGHLIGHT_EDITED_LANGUAGE_SETTINGS: 'highlight-edited-language-settings', + SET_LANGUAGE_SETTINGS_EDITED: 'set-language-settings-edited', + LANGUAGE_ADDED: 'languageAdded', + CELL_OUTPUT_EXPANDED: 'cell-output-expanded', + CELL_OUTPUT_LM_SHOWED: 'cell-output-lm-showed', + ADVANCED_MODE_TOGGLED: 'advanced-mode-toggled', + FILE_DROPPED: 'file-dropped' + }, + FILE_LOCATION: { + FILESYS: "file", + HTTP: "http", + AJAX: "ajax" + }, + EVALUATOR_SPEC: { + PROPERTIES: { + STRING: "settableString", + BOOLEAN: "settableBoolean", + ENUM: "settableEnum", + SELECT: "settableSelect" }, - FILE_LOCATION: { - FILESYS: "file", - HTTP: "http", - AJAX: "ajax" - }, - EVALUATOR_SPEC: { - PROPERTIES: { - STRING: "settableString", - BOOLEAN: "settableBoolean", - ENUM: "settableEnum", - SELECT: "settableSelect" - }, - ACTION: "action" - }, - THEMES: { - DEFAULT: 'default', - AMBIANCE: 'ambiance' - } - }; -}); \ No newline at end of file + ACTION: "action" + }, + THEMES: { + DEFAULT: 'default', + AMBIANCE: 'ambiance' + } +}; diff --git a/js/notebook/src/shared/bkUtils.js b/js/notebook/src/shared/bkUtils.js index f88330c4a7..72dcdebfd9 100644 --- a/js/notebook/src/shared/bkUtils.js +++ b/js/notebook/src/shared/bkUtils.js @@ -14,52 +14,45 @@ * limitations under the License. */ -define([ - './../plot/commonUtils', - 'underscore' -], function( - commonUtils, - _ -) { - var bkUtils = { - generateId: function(length) { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +var commonUtils = require('./../plot/commonUtils'); +var _ = require('underscore'); - if (_.isUndefined(length)) { - length = 6; - } - for (var i = 0; i < length; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; - }, - applyTimezone: function(timestamp, tz) { - return commonUtils.applyTimezone(timestamp, tz); - }, - formatTimestamp: function(timestamp, tz, format) { - return commonUtils.formatTimestamp(timestamp, tz, format); - }, - rgbaToHex: function (r, g, b, a) { - if(a == undefined){ - a = 0xFF; - } - var num = ((a & 0xFF) << 24) | - ((r & 0xFF) << 16) | - ((g & 0xFF) << 8) | - ((b & 0xFF)); - if(num < 0) { - num = 0xFFFFFFFF + num + 1; - } - return "#" + num.toString(16); - }, - timeout: function(fn, ms) { - return setTimeout(fn, ms); - }, - newDeferred: function() { - return jQuery.Deferred(); - } - }; +module.exports = { + generateId: function(length) { + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - return bkUtils; -}); \ No newline at end of file + if (_.isUndefined(length)) { + length = 6; + } + for (var i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; + }, + applyTimezone: function(timestamp, tz) { + return commonUtils.applyTimezone(timestamp, tz); + }, + formatTimestamp: function(timestamp, tz, format) { + return commonUtils.formatTimestamp(timestamp, tz, format); + }, + rgbaToHex: function (r, g, b, a) { + if(a == undefined){ + a = 0xFF; + } + var num = ((a & 0xFF) << 24) | + ((r & 0xFF) << 16) | + ((g & 0xFF) << 8) | + ((b & 0xFF)); + if(num < 0) { + num = 0xFFFFFFFF + num + 1; + } + return "#" + num.toString(16); + }, + timeout: function(fn, ms) { + return setTimeout(fn, ms); + }, + newDeferred: function() { + return jQuery.Deferred(); + } +}; diff --git a/js/notebook/src/tableDisplay/consts.js b/js/notebook/src/tableDisplay/consts.js deleted file mode 100644 index 43ae9de8ea..0000000000 --- a/js/notebook/src/tableDisplay/consts.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -define(function() { - - var scopeData = { - allStringTypes: [ - {type: 0, name: 'string'}, - {type: 10, name: 'html'} - ], - allTimeTypes: [ - {type: 8, name: 'datetime'}, - {type: 0, name: 'string'} - ], - allIntTypes: [ - {type: 0, name: 'string'}, - {type: 1, name: 'integer'}, - {type: 2, name: 'formatted integer'}, - {type: 8, name: 'datetime'} - ], - allDoubleTypes: [ - {type: 0, name: 'string'}, - {type: 3, name: 'double'}, - {type: 4, name: 'double with precision'}, - {type: 6, name: 'exponential 5'}, - {type: 7, name: 'exponential 15'} - ], - allBoolTypes: [ - {type: 0, name: 'string'}, - {type: 9, name: 'boolean'} - ], - allTypes: [ - {type: 0, name: 'string'}, - {type: 1, name: 'integer'}, - {type: 2, name: 'formatted integer'}, - {type: 3, name: 'double'}, - {type: 4, name: 'double with precision'}, - {type: 6, name: 'exponential 5'}, - {type: 7, name: 'exponential 15'}, - {type: 8, name: 'datetime'}, - {type: 9, name: 'boolean'}, - {type: 10, name: 'html'} - ], - rowsToDisplayMenu: [ - [10, 25, 50, 100, -1], - [10, 25, 50, 100, 'All'] - ] - }; - - return { - scopeData: scopeData, - CELL_TYPE: 'bko-tabledisplay', - ROW_HEIGHT: 27, - ROW_HEIGHT_ADVANCED_MODE: 22, - DEFAULT_PAGE_LENGTH: 25, - MIN_ROWS_FOR_PAGING: 25, - FC_LEFT_SEPARATOR_CLASS: 'left-fix-col-separator', - FC_RIGHT_SEPARATOR_CLASS: 'right-fix-col-separator', - FC_COL_FIXED_CLASS: 'fix-col-fixed', - TIME_UNIT_FORMATS: { - DATETIME: { title: 'datetime', format: 'YYYYMMDD HH:mm:ss.SSS ZZ' }, - DAYS: { title: 'date', format: 'YYYYMMDD' }, - HOURS: { title: 'hours', format: 'YYYYMMDD HH:mm ZZ' }, - MINUTES: { title: 'minutes', format: 'HH:mm ZZ' }, - SECONDS: { title: 'seconds', format: 'HH:mm:ss ZZ' }, - MILLISECONDS: { title: 'milliseconds', format: 'HH:mm:ss.SSS ZZ' } - } - }; - -}); \ No newline at end of file diff --git a/js/notebook/src/tableDisplay/consts.ts b/js/notebook/src/tableDisplay/consts.ts new file mode 100644 index 0000000000..b3454708cc --- /dev/null +++ b/js/notebook/src/tableDisplay/consts.ts @@ -0,0 +1,90 @@ +/* + * 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 const scopeData = { + allStringTypes: [ + {type: 0, name: 'string'}, + {type: 10, name: 'html'} + ], + allTimeTypes: [ + {type: 8, name: 'datetime'}, + {type: 0, name: 'string'} + ], + allIntTypes: [ + {type: 0, name: 'string'}, + {type: 1, name: 'integer'}, + {type: 2, name: 'formatted integer'}, + {type: 8, name: 'datetime'} + ], + allDoubleTypes: [ + {type: 0, name: 'string'}, + {type: 3, name: 'double'}, + {type: 4, name: 'double with precision'}, + {type: 6, name: 'exponential 5'}, + {type: 7, name: 'exponential 15'} + ], + allBoolTypes: [ + {type: 0, name: 'string'}, + {type: 9, name: 'boolean'} + ], + allTypes: [ + {type: 0, name: 'string'}, + {type: 1, name: 'integer'}, + {type: 2, name: 'formatted integer'}, + {type: 3, name: 'double'}, + {type: 4, name: 'double with precision'}, + {type: 6, name: 'exponential 5'}, + {type: 7, name: 'exponential 15'}, + {type: 8, name: 'datetime'}, + {type: 9, name: 'boolean'}, + {type: 10, name: 'html'} + ], + rowsToDisplayMenu: [ + [10, 25, 50, 100, -1], + [10, 25, 50, 100, 'All'] + ], + allPrecissions: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] +}; + +export const CELL_TYPE = 'bko-tabledisplay'; +export const ROW_HEIGHT = 27; +export const ROW_HEIGHT_ADVANCED_MODE = 22; +export const DEFAULT_PAGE_LENGTH = 25; +export const MIN_ROWS_FOR_PAGING = 25; +export const FC_LEFT_SEPARATOR_CLASS = 'left-fix-col-separator'; +export const FC_RIGHT_SEPARATOR_CLASS = 'right-fix-col-separator'; +export const FC_COL_FIXED_CLASS = 'fix-col-fixed'; +export const TIME_UNIT_FORMATS = { + DATETIME: { title: 'datetime', format: 'YYYYMMDD HH:mm:ss.SSS ZZ' }, + DAYS: { title: 'date', format: 'YYYYMMDD' }, + HOURS: { title: 'hours', format: 'YYYYMMDD HH:mm ZZ' }, + MINUTES: { title: 'minutes', format: 'HH:mm ZZ' }, + SECONDS: { title: 'seconds', format: 'HH:mm:ss ZZ' }, + MILLISECONDS: { title: 'milliseconds', format: 'HH:mm:ss.SSS ZZ' } +}; + +export default { + scopeData, + CELL_TYPE, + ROW_HEIGHT, + ROW_HEIGHT_ADVANCED_MODE, + DEFAULT_PAGE_LENGTH, + MIN_ROWS_FOR_PAGING, + FC_LEFT_SEPARATOR_CLASS, + FC_RIGHT_SEPARATOR_CLASS, + FC_COL_FIXED_CLASS, + TIME_UNIT_FORMATS +}; diff --git a/js/notebook/src/tableDisplay/css/datatables.scss b/js/notebook/src/tableDisplay/css/datatables.scss index a19d4da262..786db01fcb 100644 --- a/js/notebook/src/tableDisplay/css/datatables.scss +++ b/js/notebook/src/tableDisplay/css/datatables.scss @@ -396,7 +396,7 @@ $fix-col-separator-width: 3px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAWCAYAAADjGu3TAAAAJUlEQVQoFWOcOXPmfwYsgAmLGFiIHhK47MYtzjjqD/TAoWJEAQCA7QiAdBnZNQAAAABJRU5ErkJggg=='); background-size: 3px; background-repeat: no-repeat; - background-position: center; + background-position: 5px center; height: 20px; padding: 0 10px; } diff --git a/js/notebook/src/tableDisplay/dataGrid/BeakerxDataGrid.ts b/js/notebook/src/tableDisplay/dataGrid/BeakerxDataGrid.ts new file mode 100644 index 0000000000..c2c01b787a --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/BeakerxDataGrid.ts @@ -0,0 +1,337 @@ +/* + * Copyright 2018 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 {CellRenderer, DataGrid} from "@phosphor/datagrid"; +import { BeakerxDataGridModel } from "./model/BeakerxDataGridModel"; +import { Widget } from "@phosphor/widgets"; +import { Signal } from '@phosphor/signaling'; +import { ICellData } from "./interface/ICell"; +import { CellRendererFactory } from "./cell/CellRendererFactory"; +import DataGridColumn from "./column/DataGridColumn"; +import IDataModelState from "./interface/IDataGridModelState"; +import HighlighterManager from "./highlighter/HighlighterManager"; +import IHihglighterState from "./interface/IHighlighterState"; +import {DEFAULT_PAGE_LENGTH} from "../consts"; +import ColumnManager from "./column/ColumnManager"; +import RowManager from "./row/RowManager"; +import CellSelectionManager from "./cell/CellSelectionManager"; +import CellManager from "./cell/CellManager"; +import {DataGridHelpers} from "./dataGridHelpers"; +import EventManager from "./EventManager"; +import { IMessageHandler, Message, MessageLoop } from '@phosphor/messaging'; +import CellFocusManager from "./cell/CellFocusManager"; +import { + DEFAULT_GRID_BORDER_WIDTH, + DEFAULT_GRID_PADDING, DEFAULT_ROW_HEIGHT, + MIN_COLUMN_WIDTH +} from "./style/dataGridStyle"; +import CellTooltipManager from "./cell/CellTooltipManager"; +import * as bkUtils from '../../shared/bkUtils'; +import getStringSize = DataGridHelpers.getStringSize; +import {BeakerxDataStore} from "./store/dataStore"; +import { + selectCellHighlighters, selectDataFontSize, + selectHasIndex, selectHeaderFontSize, selectHeadersVertical, + selectTooltips, + selectValues +} from "./model/selectors"; +import {selectColumnWidth} from "./column/selectors"; +import throttle = DataGridHelpers.throttle; +import DataGridCell from "./cell/DataGridCell"; +import {COLUMN_TYPES} from "./column/enums"; +import ResizeMessage = Widget.ResizeMessage; + +export class BeakerxDataGrid extends DataGrid { + id: string; + store: BeakerxDataStore; + columnSections: any; + columnHeaderSections: any; + model: BeakerxDataGridModel; + rowHeaderSections: any; + rowSections: any; + viewport: Widget; + highlighterManager: HighlighterManager; + columnManager: ColumnManager; + rowManager: RowManager; + cellSelectionManager: CellSelectionManager; + cellManager: CellManager; + eventManager: EventManager; + cellFocusManager: CellFocusManager; + cellTooltipManager: CellTooltipManager; + focused: boolean; + wrapperId: string; + + headerCellHovered = new Signal(this); + cellHovered = new Signal(this); + commSignal = new Signal(this); + + constructor(options: DataGrid.IOptions, dataStore: BeakerxDataStore) { + super(options); + + //this is hack to use private DataGrid properties + this.viewport = this['_viewport']; + this.columnHeaderSections = this['_columnHeaderSections']; + this.rowHeaderSections = this['_rowHeaderSections']; + this.rowSections = this['_rowSections']; + this.columnSections = this['_columnSections']; + + this.baseRowSize = DEFAULT_ROW_HEIGHT; + this.baseColumnHeaderSize = DEFAULT_ROW_HEIGHT; + + this.setSectionWidth = this.setSectionWidth.bind(this); + this.setInitialSectionWidth = this.setInitialSectionWidth.bind(this); + this.resizeSectionWidth = this.resizeSectionWidth.bind(this); + this.resize = throttle(this.resize.bind(this), 150); + this.init(dataStore); + } + + handleEvent(event: Event): void { + this.eventManager.handleEvent(event, super.handleEvent); + } + + messageHook(handler: IMessageHandler, msg: Message): boolean { + super.messageHook(handler, msg); + + if (handler === this.viewport && msg.type === 'section-resize-request') { + this.columnSections['_sections'].forEach(({ index, size }) => { + let columnOnPosition = this.columnManager.getColumnByPosition(COLUMN_TYPES.body, index); + + columnOnPosition.setWidth(size); + }); + this.updateWidgetWidth(); + this.updateWidgetHeight(); + } + + return true; + } + + destroy() { + this.eventManager.destroy(); + this.columnManager.destroy(); + + Signal.disconnectAll(this); + } + + getColumn(config: CellRenderer.ICellConfig): DataGridColumn { + return this.columnManager.getColumn(config); + } + + getColumnByName(columnName: string): DataGridColumn|undefined { + return this.columnManager.getColumnByName(columnName); + } + + getCellData(clientX: number, clientY: number): ICellData|null { + return DataGridCell.getCellData(this, clientX, clientY); + } + + getColumnOffset(index: number, type: COLUMN_TYPES) { + if (type === COLUMN_TYPES.index) { + return 0; + } + + return this.rowHeaderSections.totalSize + this.columnSections.sectionOffset(index); + } + + getRowOffset(row: number) { + return this.rowSections.sectionOffset(row); + } + + isOverHeader(event: MouseEvent) { + let rect = this.viewport.node.getBoundingClientRect(); + let x = event.clientX - rect.left; + let y = event.clientY - rect.top; + + return x < (this.bodyWidth + this.rowHeaderSections.totalSize) && y < this.headerHeight; + } + + updateModelData(state: IDataModelState) { + this.model.updateData(state); + this.columnManager.recalculateMinMaxValues(); + this.setInitialSize(); + } + + setWrapperId(id: string) { + this.wrapperId = id; + } + + resize(args?: any): void { + this.resizeHeader(); + this.resizeSections(); + this.updateWidgetWidth(); + this.columnManager.updateColumnFilterNodes(); + this.columnManager.updateColumnMenuTriggers(); + } + + setInitialSize() { + this.resizeHeader(); + this.updateWidgetHeight(); + this.setInitialSectionWidths(); + this.updateWidgetWidth(); + } + + updateWidgetWidth() { + const spacing = 2 * (DEFAULT_GRID_PADDING + DEFAULT_GRID_BORDER_WIDTH) + 1; + const hasVScroll = !this['_vScrollBar'].isHidden; + const vScrollWidth = hasVScroll ? this['_vScrollBarMinWidth'] + 1 : 0; + const width = this.totalWidth + spacing + vScrollWidth; + + this.node.style.width = `${width}px`; + this.fit(); + } + + setInitialSectionWidth(column) { + this.setSectionWidth('column', column, this.getSectionWidth(column)); + } + + private init(store: BeakerxDataStore) { + this.id = 'grid_' + bkUtils.generateId(6); + this.store = store; + this.columnManager = new ColumnManager(this); + this.rowManager = new RowManager(selectValues(store.state), selectHasIndex(store.state), this.columnManager); + this.cellSelectionManager = new CellSelectionManager(this); + this.cellManager = new CellManager(this); + this.eventManager = new EventManager(this); + this.cellFocusManager = new CellFocusManager(this); + this.cellTooltipManager = new CellTooltipManager(this, selectTooltips(store.state)); + this.model = new BeakerxDataGridModel(store, this.columnManager, this.rowManager); + this.focused = false; + + this.columnManager.addColumns(); + this.rowManager.createFilterExpressionVars(); + this.store.changed.connect(throttle(this.handleStateChanged.bind(this), 150)); + + this.installMessageHook(); + this.addHighlighterManager(); + this.addCellRenderers(); + this.setInitialSize(); + } + + private installMessageHook() { + MessageLoop.installMessageHook(this.viewport, this.viewportResizeMessageHook.bind(this)); + } + + private handleStateChanged() { + this.model.reset(); + } + + private addHighlighterManager() { + let cellHighlighters: IHihglighterState[] = selectCellHighlighters(this.store.state); + + this.highlighterManager = new HighlighterManager(this, cellHighlighters); + } + + private addCellRenderers() { + let cellRendererFactory = new CellRendererFactory(this); + let defaultRenderer = cellRendererFactory.getRenderer(); + + this.cellRenderers.set('body', {}, defaultRenderer); + this.cellRenderers.set('column-header', {}, defaultRenderer); + this.cellRenderers.set('corner-header', {}, defaultRenderer); + this.cellRenderers.set('row-header', {}, defaultRenderer); + } + + private getWidgetHeight() { + const bodyRowCount = this.model.rowCount('body'); + const rowCount = DEFAULT_PAGE_LENGTH < bodyRowCount ? DEFAULT_PAGE_LENGTH : bodyRowCount; + const spacing = 2 * (DEFAULT_GRID_PADDING + DEFAULT_GRID_BORDER_WIDTH); + + return rowCount * this.baseRowSize + this.headerHeight + spacing; + } + + private updateWidgetHeight() { + this.node.style.minHeight = `${this.getWidgetHeight()}px`; + } + + private setInitialSectionWidths() { + this.columnManager.bodyColumns.forEach(this.setInitialSectionWidth); + this.resizeIndexColumn(); + } + + private resizeSections() { + this.columnManager.bodyColumns.forEach(this.resizeSectionWidth); + this.columnManager.indexColumns.forEach(this.resizeSectionWidth); + } + + private resizeSectionWidth(column) { + const columnOnPosition = this.columnManager.getColumnByPosition(column.type, column.index); + const value = selectColumnWidth(this.store.state, columnOnPosition); + const area = column.type === COLUMN_TYPES.body ? 'column' : 'row-header'; + + this.resizeSection(area, column.index, value); + } + + private resizeHeader() { + let bodyColumnNamesWidths: number[] = []; + let indexColumnNamesWidths: number[] = []; + + if (selectHeadersVertical(this.store.state)) { + const mapNameToWidth = name => getStringSize(name, selectHeaderFontSize(this.store.state)).width; + + bodyColumnNamesWidths = this.columnManager.bodyColumnNames.map(mapNameToWidth); + indexColumnNamesWidths = this.columnManager.indexColumnNames.map(mapNameToWidth); + } + + this.baseColumnHeaderSize = Math.max.apply( + null, + [...bodyColumnNamesWidths, ...indexColumnNamesWidths, DEFAULT_ROW_HEIGHT] + ); + } + + private getSectionWidth(column) { + const value = String(column.formatFn(this.cellManager.createCellConfig({ + region: 'body', + value: column.maxValue, + column: column.index, + row: 0, + }))); + const nameSize = getStringSize(column.name, selectHeaderFontSize(this.store.state)); + const valueSize = getStringSize(value, selectDataFontSize(this.store.state)); + const nameSizeProp = selectHeadersVertical(this.store.state) ? 'height' : 'width'; + + nameSize.width += 4; // Add space for the menu + const result = nameSize[nameSizeProp] > valueSize.width - 7 ? nameSize[nameSizeProp] : valueSize.width; + + return result > MIN_COLUMN_WIDTH ? result : MIN_COLUMN_WIDTH; + } + + private resizeIndexColumn() { + const valueCharLength = this.model.rowCount('body'); + const name = this.columnManager.getColumnByIndex(COLUMN_TYPES.index, 0).name; + const value = name.length > valueCharLength ? name : String(valueCharLength); + const nameSizeProp = selectHeadersVertical(this.store.state) ? 'height' : 'width'; + const nameSize = getStringSize(name, selectHeaderFontSize(this.store.state)); + const valueSize = getStringSize(value, selectDataFontSize(this.store.state)); + const result = (nameSize[nameSizeProp] > valueSize.width ? nameSize[nameSizeProp]: valueSize.width) + 10; + const column = this.columnManager.getColumnByIndex(COLUMN_TYPES.index, 0); + + this.setSectionWidth('row-header', column, result); + } + + private setSectionWidth(section, column: DataGridColumn, value: number) { + this.resizeSection(section, column.index, value); + column.setWidth(value); + } + + private viewportResizeMessageHook(handler, msg) { + if (handler === this.viewport && msg.type === 'resize') { + setTimeout(() => { + this['_syncViewport'](); + }); + } + + return true; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/DataFormatter.ts b/js/notebook/src/tableDisplay/dataGrid/DataFormatter.ts new file mode 100644 index 0000000000..9055fd52cd --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/DataFormatter.ts @@ -0,0 +1,262 @@ +/* + * 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 * as moment from 'moment-timezone/builds/moment-timezone-with-data'; +import * as _ from 'underscore'; +import { + isDoubleWithPrecision, + getDoublePrecisionByType +} from './dataTypes'; +import { DataGridHelpers } from './dataGridHelpers'; +import { TIME_UNIT_FORMATS } from '../consts'; +import {CellRenderer} from "@phosphor/datagrid"; +import {IColumnState} from "./interface/IColumn"; +import { + selectColumnNames, + selectStringFormatForColumn, selectFormatForTimes, + selectStringFormatForType, + selectTimeStrings, + selectTimeZone +} from "./model/selectors"; +import {BeakerxDataStore} from "./store/dataStore"; + +const bkUtils = require('../../shared/bkUtils'); + +export const DEFAULT_TIME_FORMAT = 'YYYYMMDD HH:mm:ss.SSS ZZ'; + +export class DataFormatter { + store: BeakerxDataStore; + + constructor(store: BeakerxDataStore) { + this.store = store; + + this.handleNull = this.handleNull.bind(this); + this.value = this.value.bind(this); + this.string = this.string.bind(this); + this.integer = this.integer.bind(this); + this.formattedInteger = this.formattedInteger.bind(this); + this.double = this.double.bind(this); + this.doubleWithPrecision = this.doubleWithPrecision.bind(this); + this.exponential_5 = this.exponential_5.bind(this); + this.exponential_15 = this.exponential_15.bind(this); + this.datetime = this.datetime.bind(this); + this.boolean = this.boolean.bind(this); + this.html = this.html.bind(this); + } + + get stringFormatForColumn() { + return selectStringFormatForColumn(this.store.state); + } + + get timeStrings() { + return selectTimeStrings(this.store.state); + } + + get timeZone() { + return selectTimeZone(this.store.state); + } + + get stringFormatForType() { + return selectStringFormatForType(this.store.state); + } + + get formatForTimes() { + return selectFormatForTimes(this.store.state); + } + + get columnNames() { + return selectColumnNames(this.store.state); + } + + getFormatFnByDisplayType(displayType, columnState?: IColumnState): CellRenderer.ConfigFunc { + if (isDoubleWithPrecision(displayType)) { + return this.doubleWithPrecision(getDoublePrecisionByType(displayType)); + } + + switch (displayType) { + case 1: + return this.integer; + case 2: + return this.formattedInteger; + case 3: + return this.double; + case 6: + return this.exponential_5; + case 7: + return this.exponential_15; + case 8: + return this.datetimeWithFormat(this.getTimeFormatForColumn(columnState)); + case 9: + return this.boolean; + case 10: + return this.html; + + default: + return this.string; + } + } + + private isNull(value: any) { + return value === undefined || value === '' || value === 'null' || value === null; + } + + private handleNull(formatFn: CellRenderer.ConfigFunc): CellRenderer.ConfigFunc { + return (config: CellRenderer.ICellConfig): string => { + if (this.isNull(config.value)) { + return config.value; + } + + return formatFn(config); + } + } + + private value(config: CellRenderer.ICellConfig): string { + let columnName = this.columnNames[config.column]; + + return this.stringFormatForColumn[columnName].values[columnName][config.row]; + }; + + private string(config: CellRenderer.ICellConfig) { + const objectValue = _.isObject(config.value); + const stringFormatForColumn = this.stringFormatForColumn[this.columnNames[config.column]]; + let formattedValue = config.value; + + if (!objectValue && stringFormatForColumn && stringFormatForColumn.type === 'value') { + return this.value(config); + } + + if (objectValue) { + formattedValue = config.value.type === 'Date' ? + moment(config.value.timestamp).format(DEFAULT_TIME_FORMAT) : + JSON.stringify(config.value); + } else if (_.isString(config.value)) { + const limitedText = DataGridHelpers.truncateString(config.value); + + formattedValue = limitedText; + } + + return formattedValue; + } + + private integer(config: CellRenderer.ICellConfig) { + if (this.isNull(config.value)) { + return config.value; + } + + return parseInt(config.value); + } + + private formattedInteger(config: CellRenderer.ICellConfig) { + if (this.isNull(config.value)) { + return config.value; + } + + let x = parseInt(config.value); + + if (!isNaN(x)) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + } + + return x; + } + + private double(config: CellRenderer.ICellConfig) { + if (this.isNull(config.value)) { + return config.value; + } + + let doubleValue = parseFloat(config.value); + let colFormat = this.stringFormatForColumn[this.columnNames[config.column]]; + let typeFormat = this.stringFormatForType['double']; + let format = colFormat && colFormat.type === 'decimal' ? colFormat : typeFormat; + + if (!format || format.type !== 'decimal') { + return doubleValue; + } + + let precision = doubleValue.toString().split('.')[1]; + + if (precision && precision.length >= format.maxDecimals) { + return doubleValue.toFixed(format.maxDecimals); + } + + return doubleValue.toFixed(format.minDecimals); + } + + private doubleWithPrecision(precision: any): CellRenderer.ConfigFunc { + return this.handleNull((config: CellRenderer.ICellConfig) => { + return parseFloat(config.value).toFixed(precision); + }); + } + + private exponential_5(config: CellRenderer.ICellConfig): string { + if (this.isNull(config.value)) { + return config.value; + } + + return parseFloat(config.value).toExponential(5); + } + + private exponential_15(config: CellRenderer.ICellConfig): string { + if (this.isNull(config.value)) { + return config.value; + } + + return parseFloat(config.value).toExponential(15); + } + + private datetime(config: CellRenderer.ICellConfig, formatForTimes: any): string { + if (this.timeStrings) { + return this.timeStrings[config.row]; + } + + let format = formatForTimes && formatForTimes.format + ? formatForTimes.format + : TIME_UNIT_FORMATS.DATETIME.format; + + if (_.isObject(config.value) && config.value.type === 'Date') { + return bkUtils.formatTimestamp(config.value.timestamp, this.timeZone, format); + } + + let milli = config.value * 1000; + + return bkUtils.formatTimestamp(milli, this.timeZone, format); + } + + private getTimeFormatForColumn(columnState?: IColumnState) { + return columnState && columnState.formatForTimes + ? columnState.formatForTimes + : this.formatForTimes; + } + + private datetimeWithFormat(formatForTimes?: any) { + return (config) => this.datetime(config, formatForTimes); + } + + private boolean(config: CellRenderer.ICellConfig): string { + return ( + this.isNull(config.value) || + config.value === false || + (typeof config.value === 'number' && isNaN(config.value)) + ) ? + 'false': + 'true'; + } + + private html(config: CellRenderer.ICellConfig): string { + return config.value; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/DataGridScope.ts b/js/notebook/src/tableDisplay/dataGrid/DataGridScope.ts new file mode 100644 index 0000000000..dfa1538990 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/DataGridScope.ts @@ -0,0 +1,89 @@ +/* + * 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 { Widget } from '@phosphor/widgets'; +import { BeakerxDataGrid } from './BeakerxDataGrid'; +import { silverStripeStyle } from './style/dataGridStyle'; +import IDataGridScopeOptions from "./interface/IDataGridScopeOptions"; +import DataGridContextMenu from "./contextMenu/DataGridContextMenu"; +import ColumnLimitModal from "./modal/ColumnLimitModal"; +import createStore, {BeakerxDataStore} from "./store/dataStore"; +import {selectModel} from "./model/selectors"; + +export class DataGridScope { + contextMenu: DataGridContextMenu; + + readonly dataGrid: BeakerxDataGrid; + readonly element: HTMLElement; + readonly store: BeakerxDataStore; + private tableDisplayModel: any; + private tableDisplayView: any; + + constructor(options: IDataGridScopeOptions) { + this.store = createStore(options.data); + this.element = options.element; + this.tableDisplayModel = options.widgetModel; + this.tableDisplayView = options.widgetView; + this.dataGrid = new BeakerxDataGrid({ style: silverStripeStyle }, this.store); + + this.element.id = `wrap_${this.tableDisplayModel.model_id}`; + this.dataGrid.setWrapperId(this.element.id); + this.connectToCommSignal(); + this.createContextMenu(); + this.initColumnLimitModal(); + } + + get state() { + return selectModel(this.store.state); + } + + render(): void { + Widget.attach(this.dataGrid, this.element); + } + + doDestroy() { + this.dataGrid.destroy(); + this.contextMenu.destroy(); + } + + updateModelData(newData) { + this.dataGrid.updateModelData(newData); + } + + doResetAll() { + this.dataGrid.highlighterManager.removeHighlighters(); + this.dataGrid.cellSelectionManager.clear(); + this.dataGrid.rowManager.resetSorting(); + this.dataGrid.columnManager.resetFilters(); + this.dataGrid.columnManager.showAllColumns(); + this.dataGrid.columnManager.resetColumnsAlignment(); + this.dataGrid.columnManager.resetColumnsOrder(); + } + + connectToCommSignal() { + this.dataGrid.commSignal.connect((handler, args) => { + this.tableDisplayModel.send(args, this.tableDisplayView.callbacks()); + }, this); + } + + createContextMenu() { + this.contextMenu = new DataGridContextMenu(this); + } + + initColumnLimitModal() { + return new ColumnLimitModal(this.dataGrid, this.element); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/EventManager.ts b/js/notebook/src/tableDisplay/dataGrid/EventManager.ts new file mode 100644 index 0000000000..22f2f7d552 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/EventManager.ts @@ -0,0 +1,235 @@ +/* + * Copyright 2018 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 {BeakerxDataGrid} from "./BeakerxDataGrid"; +import DataGridColumn from "./column/DataGridColumn"; +import {HIGHLIGHTER_TYPE} from "./interface/IHighlighterState"; +import {DataGridHelpers} from "./dataGridHelpers"; +import disableKeyboardManager = DataGridHelpers.disableKeyboardManager; +import enableKeyboardManager = DataGridHelpers.enableKeyboardManager; +import throttle = DataGridHelpers.throttle; +import {ICellData} from "./interface/ICell"; +import {BeakerxDataStore} from "./store/dataStore"; +import {selectDoubleClickTag, selectHasDoubleClickAction} from "./model/selectors"; +import {COLUMN_TYPES} from "./column/enums"; + +export default class EventManager { + dataGrid: BeakerxDataGrid; + store: BeakerxDataStore; + + constructor(dataGrid: BeakerxDataGrid) { + this.store = dataGrid.store; + this.dataGrid = dataGrid; + this.handleKeyDown = this.handleKeyDown.bind(this); + this.handleMouseOut = this.handleMouseOut.bind(this); + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleDoubleClick = this.handleDoubleClick.bind(this); + this.handleHeaderClick = this.handleHeaderClick.bind(this); + this.handleCellHover = throttle(this.handleCellHover.bind(this), 250); + + this.dataGrid.node.removeEventListener('mouseout', this.handleMouseOut); + this.dataGrid.node.addEventListener('mouseout', this.handleMouseOut); + this.dataGrid.node.removeEventListener('dblclick', this.handleDoubleClick); + this.dataGrid.node.addEventListener('dblclick', this.handleDoubleClick); + this.dataGrid.node.removeEventListener('mouseup', this.handleHeaderClick); + this.dataGrid.node.addEventListener('mouseup', this.handleHeaderClick); + document.removeEventListener('keydown', this.handleKeyDown); + document.addEventListener('keydown', this.handleKeyDown); + } + + handleEvent(event: Event, parentHandler: Function): void { + switch (event.type) { + case 'mousemove': + this.handleCellHover(event as MouseEvent); + break; + case 'mousedown': + this.handleMouseDown(event as MouseEvent); + break; + case 'wheel': + this.handleMouseWheel(event as MouseEvent, parentHandler); + return; + } + + parentHandler.call(this.dataGrid, event); + } + + destroy() { + document.removeEventListener('keydown', this.handleKeyDown); + } + + private handleCellHover(event: MouseEvent): void { + const data = this.dataGrid.getCellData(event.clientX, event.clientY); + + this.handleBodyCellHover(data, event); + this.handleHeaderCellHover(data, event); + } + + private handleHeaderCellHover(data: ICellData|null, event: MouseEvent) { + if (!this.dataGrid.isOverHeader(event)) { + return this.dataGrid.headerCellHovered.emit(null); + } + + this.dataGrid.headerCellHovered.emit(data); + } + + private handleBodyCellHover(data: ICellData|null, event: MouseEvent) { + if (this.dataGrid.isOverHeader(event)) { + return this.dataGrid.cellHovered.emit(null); + } + + this.dataGrid.cellHovered.emit(data); + } + + private handleMouseDown(event: MouseEvent): void { + if (event.buttons !== 1) { + return; + } + + this.dataGrid.focused = true; + this.dataGrid.node.classList.add('bko-focused'); + disableKeyboardManager(); + } + + private handleMouseOut(event: MouseEvent): void { + if (this.dataGrid.node.contains(event.toElement)) { + return; + } + + this.dataGrid.headerCellHovered.emit(null); + this.dataGrid.node.classList.remove('bko-focused'); + this.dataGrid.focused = false; + this.dataGrid.cellTooltipManager.hideTooltip(); + enableKeyboardManager(); + } + + private handleMouseWheel(event: MouseEvent, parentHandler: Function): void { + if(!this.dataGrid.focused) { + return; + } + + parentHandler.call(this.dataGrid, event); + } + + private handleHeaderClick(event: MouseEvent): void { + if ( + !this.dataGrid.isOverHeader(event) + || event.buttons !== 0 + || event.target !== this.dataGrid['_canvas'] + ) { + return; + } + + const data = this.dataGrid.getCellData(event.clientX, event.clientY); + + if (!data) { + return; + } + + const destColumn = this.dataGrid.columnManager.getColumnByPosition(data.type, data.column); + + destColumn.toggleSort(); + } + + private handleKeyDown(event: KeyboardEvent): void { + if (!this.dataGrid.focused) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const focusedCell = this.dataGrid.cellFocusManager.focusedCellData; + const column: DataGridColumn|null = focusedCell && this.dataGrid.columnManager.takeColumnByCell(focusedCell); + const key = event.keyCode; + const charCode = String.fromCharCode(key); + + if (!charCode) { + return; + } + + this.handleHighlighterKeyDown(charCode, column); + this.handleNumKeyDown(event, charCode, column); + this.handleArrowKeyDown(event); + } + + private handleHighlighterKeyDown(charCode: string, column: DataGridColumn|null) { + switch(charCode.toUpperCase()){ + case 'H': + column && column.toggleHighlighter(HIGHLIGHTER_TYPE.heatmap); + break; + case 'U': + column && column.toggleHighlighter(HIGHLIGHTER_TYPE.uniqueEntries); + break; + case 'B': + column && column.toggleDataBarsRenderer(); + break; + } + } + + private handleArrowKeyDown(event: KeyboardEvent) { + if (event.keyCode < 37 || event.keyCode > 40) { + return; + } + + this.dataGrid.cellFocusManager.setFocusedCellByArrowKey(event.keyCode); + } + + private handleNumKeyDown(event: KeyboardEvent, charCode: string, column: DataGridColumn|null) { + if (event.keyCode < 48 || event.keyCode > 57) { //not numbers 1..9 + return; + } + + if (event.shiftKey && column) { + column.setDataTypePrecission(parseInt(charCode)); + } else { + this.dataGrid.columnManager.setColumnsDataTypePrecission(parseInt(charCode)); + } + } + + private handleDoubleClick(event: MouseEvent) { + event.stopPropagation(); + event.preventDefault(); + + if (this.dataGrid.isOverHeader(event)) { + return; + } + + const data = this.dataGrid.getCellData(event.clientX, event.clientY); + + if (!data || data.type === COLUMN_TYPES.index) { + return; + } + + if (selectHasDoubleClickAction(this.store.state)) { + this.dataGrid.commSignal.emit({ + event: 'DOUBLE_CLICK', + row : data.row, + column: data.column + }); + } + + if (selectDoubleClickTag(this.store.state)) { + this.dataGrid.commSignal.emit({ + event: 'actiondetails', + params: { + actionType: 'DOUBLE_CLICK', + row: data.row, + col: data.column + } + }); + } + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/BeakerxCellRenderer.ts b/js/notebook/src/tableDisplay/dataGrid/cell/BeakerxCellRenderer.ts new file mode 100644 index 0000000000..76c2c91cf7 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/BeakerxCellRenderer.ts @@ -0,0 +1,258 @@ +/* + * Copyright 2018 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 { DEFAULT_ALIGNMENT } from "../column/columnAlignment"; +import {CellRenderer, GraphicsContext, TextRenderer} from "@phosphor/datagrid"; +import { BeakerxDataGrid } from "../BeakerxDataGrid"; +import DataGridCell from "./DataGridCell"; +import { + darken, + DEFAULT_CELL_BACKGROUND, + DEFAULT_DATA_FONT_COLOR, + DEFAULT_DATA_FONT_SIZE, + DEFAULT_HEADER_FONT_COLOR, formatColor +} from "../style/dataGridStyle"; +import {BeakerxDataStore} from "../store/dataStore"; +import { + selectDataFontSize, + selectFontColor, + selectHeaderFontSize, + selectHeadersVertical, selectRenderer +} from "../model/selectors"; +import IRenderer, {RENDERER_TYPE} from "../interface/IRenderer"; + +export default class BeakerxCellRenderer extends TextRenderer { + store: BeakerxDataStore; + dataGrid: BeakerxDataGrid; + backgroundColor: CellRenderer.ConfigOption; + horizontalAlignment: CellRenderer.ConfigOption; + format: TextRenderer.FormatFunc; + font: CellRenderer.ConfigOption; + textColor: CellRenderer.ConfigOption; + + constructor(dataGrid: BeakerxDataGrid, options?: TextRenderer.IOptions) { + super(options); + + this.store = dataGrid.store; + this.dataGrid = dataGrid; + this.backgroundColor = this.getBackgroundColor.bind(this); + this.horizontalAlignment = this.getHorizontalAlignment.bind(this); + this.format = this.getFormat.bind(this); + this.font = this.getFont.bind(this); + this.textColor = this.getTextColor.bind(this); + } + + getBackgroundColor(config: CellRenderer.ICellConfig): string { + if (DataGridCell.isHeaderCell(config)) { + return DEFAULT_CELL_BACKGROUND; + } + + let selectionColor = this.dataGrid.cellSelectionManager.getBackgroundColor(config); + let highlighterColor = this.dataGrid.highlighterManager.getCellBackground(config); + let focusedColor = this.dataGrid.cellFocusManager.getFocussedCellBackground(config); + let initialColor = selectionColor && highlighterColor && darken(highlighterColor); + + return focusedColor && initialColor && darken(initialColor) || + focusedColor || + initialColor || + highlighterColor || + selectionColor || + DEFAULT_CELL_BACKGROUND; + } + + getHorizontalAlignment(config: CellRenderer.ICellConfig): string { + let column = this.dataGrid.getColumn(config); + + return column ? column.getAlignment() : DEFAULT_ALIGNMENT; + } + + getFormat(config: CellRenderer.ICellConfig) { + let column = this.dataGrid.getColumn(config); + + return DataGridCell.isHeaderCell(config) ? config.value : column.formatFn(config); + } + + getFont({ region }): string { + let fontSize = (region === 'column-header' || region === 'corner-header') + ? selectHeaderFontSize(this.store.state) + : selectDataFontSize(this.store.state); + + return `normal ${fontSize || DEFAULT_DATA_FONT_SIZE}px Lato, Helvetica, sans-serif` + } + + getTextColor(config): string { + if (config.region === 'row-header') { + return DEFAULT_DATA_FONT_COLOR; + } + + let colors = selectFontColor(this.store.state); + let dataFontColor = colors && colors[config.row] + ? formatColor(colors[config.row][config.column]) + : DEFAULT_DATA_FONT_COLOR; + + return config.region === 'column-header' || config.region === "corner-header" + ? DEFAULT_HEADER_FONT_COLOR + : dataFontColor; + } + + getRenderer(config: CellRenderer.ICellConfig): IRenderer|undefined { + const column = this.dataGrid.getColumn(config); + const renderer = selectRenderer(this.store.state, column); + const valueResolver = column.getValueResolver(); + + return { + ...renderer, + percent: (Math.abs(parseFloat(valueResolver(config.value))) / column.maxValue), + direction: valueResolver(config.value) > 0 ? 'RIGHT' : 'LEFT' + }; + } + + drawBackground(gc: GraphicsContext, config: CellRenderer.ICellConfig) { + super.drawBackground(gc, config); + + const renderer = this.getRenderer(config); + const isHeaderCell = DataGridCell.isHeaderCell(config); + + if (renderer && renderer.type === RENDERER_TYPE.DataBars && !isHeaderCell) { + const barWidth = config.width/2 * renderer.percent; + + gc.fillStyle = '#6ba2c7'; + gc.fillRect( + config.x + config.width/2 - (renderer.direction === 'RIGHT' ? 0 : barWidth), + config.y, + barWidth, + config.height - 1 + ); + } + } + + drawText(gc: GraphicsContext, config: CellRenderer.ICellConfig): void { + // Resolve the font for the cell. + let font = CellRenderer.resolveOption(this.font, config); + const renderer = this.getRenderer(config); + const isHeaderCell = DataGridCell.isHeaderCell(config); + + if ( + renderer + && renderer.type === RENDERER_TYPE.DataBars + && !renderer.includeText + && !isHeaderCell + ) { + return; + } + + // Bail if there is no font to draw. + if (!font) { + return; + } + + // Resolve the text color for the cell. + let color = CellRenderer.resolveOption(this.textColor, config); + + // Bail if there is no text color to draw. + if (!color) { + return; + } + + // Format the cell value to text. + let format = this.format; + let text = format(config); + + // Bail if there is no text to draw. + if (!text) { + return; + } + + // Resolve the vertical and horizontal alignment. + let vAlign = CellRenderer.resolveOption(this.verticalAlignment, config); + let hAlign = CellRenderer.resolveOption(this.horizontalAlignment, config); + + // Compute the padded text box height for the specified alignment. + let boxHeight = config.height - (vAlign === 'center' ? 1 : 2); + + // Bail if the text box has no effective size. + if (boxHeight <= 0) { + return; + } + + // Compute the text height for the gc font. + let textHeight = TextRenderer.measureFontHeight(font); + + // Set up the text position variables. + let textX: number; + let textY: number; + + // Compute the Y position for the text. + switch (vAlign) { + case 'top': + textY = config.y + 2 + textHeight; + break; + case 'center': + textY = config.y + config.height / 2 + textHeight / 2; + break; + case 'bottom': + textY = config.y + config.height - 2; + break; + default: + throw 'unreachable'; + } + + // Compute the X position for the text. + switch (hAlign) { + case 'left': + textX = config.x + (isHeaderCell ? 10 : 2); + break; + case 'center': + textX = config.x + config.width / 2; + break; + case 'right': + textX = config.x + config.width - 3; + break; + default: + throw 'unreachable'; + } + + // Clip the cell if the text is taller than the text box height. + if (textHeight > boxHeight) { + gc.beginPath(); + gc.rect(config.x, config.y, config.width, config.height - 1); + gc.clip(); + } + + let verticalHeader = isHeaderCell && selectHeadersVertical(this.store.state); + + // Set the gc state. + gc.textBaseline = 'bottom'; + gc.textAlign = hAlign; + + if(verticalHeader) { + gc.save(); + gc.rotate(-Math.PI/2); + + textX = -config.height + 2; + textY = config.x + config.width - 3; + gc.textBaseline = 'bottom'; + gc.textAlign = 'left'; + } + + gc.font = font; + gc.fillStyle = color; + + // Draw the text for the cell. + gc.fillText(text, textX, textY); + verticalHeader && gc.restore(); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/CellFocusManager.ts b/js/notebook/src/tableDisplay/dataGrid/cell/CellFocusManager.ts new file mode 100644 index 0000000000..8a548d4d97 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/CellFocusManager.ts @@ -0,0 +1,128 @@ +/* + * Copyright 2018 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 {BeakerxDataGrid} from "../BeakerxDataGrid"; +import {ICellData} from "../interface/ICell"; +import {CellRenderer} from "@phosphor/datagrid"; +import DataGridColumn from "../column/DataGridColumn"; +import {DEFAULT_CELL_BACKGROUND, FOCUSED_CELL_BACKGROUND} from "../style/dataGridStyle"; +import {selectBodyColumnVisibility} from "../column/selectors"; +import {COLUMN_TYPES} from "../column/enums"; + +export default class CellFocusManager { + dataGrid: BeakerxDataGrid; + focusedCellData: ICellData|null; + + constructor(dataGrid: BeakerxDataGrid) { + this.dataGrid = dataGrid; + this.focusedCellData = null; + } + + setFocusedCell(cellData: ICellData|null) { + this.focusedCellData = cellData; + } + + setFocusedCellByArrowKey(keyCode: number) { + switch (keyCode) { + case 37: // left arrow + this.setLeftFocusedCell(); + break; + case 38: // up arrow + this.setUpFocusedCell(); + break; + case 39: // right arrow + this.setRightFocusedCell(); + break; + case 40: // down arrow + this.setDownFocusedCell(); + break; + } + + this.dataGrid.repaint(); + } + + getFocussedCellBackground(config: CellRenderer.ICellConfig): string { + const cellType = DataGridColumn.getColumnTypeByRegion(config.region); + + if (!this.focusedCellData || cellType !== this.focusedCellData.type) { + return DEFAULT_CELL_BACKGROUND; + } + + return config.row === this.focusedCellData.row && config.column === this.focusedCellData.column + ? FOCUSED_CELL_BACKGROUND + : DEFAULT_CELL_BACKGROUND; + } + + private setRightFocusedCell() { + if (!this.focusedCellData) { + return; + } + + const nextColumn = this.focusedCellData.type === COLUMN_TYPES.body + ? this.focusedCellData.column + 1 + : this.focusedCellData.column; + const lastColumnIndex = selectBodyColumnVisibility(this.dataGrid.store.state) + .filter(visible => visible).length - 1; + + this.setFocusedCell({ + ...this.focusedCellData, + type: COLUMN_TYPES.body, + column: nextColumn > lastColumnIndex ? lastColumnIndex : nextColumn + }); + } + + private setLeftFocusedCell() { + if (!this.focusedCellData) { + return; + } + + const prevColumn = this.focusedCellData.column - 1; + + this.setFocusedCell({ + ...this.focusedCellData, + type: prevColumn < 0 ? COLUMN_TYPES.index : COLUMN_TYPES.body, + column: prevColumn < 0 ? 0 : prevColumn + }); + } + + private setUpFocusedCell() { + if (!this.focusedCellData) { + return; + } + + const row = this.focusedCellData.row - 1; + + this.setFocusedCell({ + ...this.focusedCellData, + row: row < 0 ? 0 : row + }); + } + + private setDownFocusedCell() { + if (!this.focusedCellData) { + return; + } + + const row = this.focusedCellData.row + 1; + const rowCount = this.dataGrid.model.rowCount('body'); + + this.setFocusedCell({ + ...this.focusedCellData, + row: row > rowCount ? rowCount : row + }); + } + +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/CellManager.ts b/js/notebook/src/tableDisplay/dataGrid/cell/CellManager.ts new file mode 100644 index 0000000000..2fefe8160c --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/CellManager.ts @@ -0,0 +1,238 @@ +/* + * Copyright 2018 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 {BeakerxDataGrid} from "../BeakerxDataGrid"; +import * as bkUtils from '../../../shared/bkUtils'; +import {IRangeCells} from "./CellSelectionManager"; +import {CellRenderer, DataModel} from "@phosphor/datagrid"; +import {COLUMN_TYPES} from "../column/enums"; + +export default class CellManager { + dataGrid: BeakerxDataGrid; + + constructor(dataGrid: BeakerxDataGrid) { + this.dataGrid = dataGrid; + } + + getSelectedCells() { + const rowsRange = this.dataGrid.cellSelectionManager.getRowsRangeCells(); + const columnsRange = this.dataGrid.cellSelectionManager.getColumnsRangeCells(); + + if (!rowsRange || !columnsRange) { + return []; + } + + return this.getCells(rowsRange, columnsRange); + } + + getAllCells() { + const startRow = this.dataGrid.rowManager.rows[0]; + const endRow = this.dataGrid.rowManager.rows[this.dataGrid.rowManager.rows.length - 1]; + const rowsRange = { + startCell: { + row: startRow.index, + column: 0, + type: COLUMN_TYPES.index, + delta: 0, + offset: 0, + offsetTop: 0 + }, + endCell: { + row: endRow.index, + column: this.dataGrid.columnManager.columns[COLUMN_TYPES.body].length - 1 || 0, + type: COLUMN_TYPES.body, + delta: 0, + offset: 0, + offsetTop: 0 + } + }; + const columnsRange = rowsRange; + + if (!rowsRange || !columnsRange) { + return []; + } + + return this.getCells(rowsRange, columnsRange); + } + + getCells(rowsRange: IRangeCells, columnsRange: IRangeCells) { + const rows = this.dataGrid.rowManager.takeRows(rowsRange.startCell.row, rowsRange.endCell.row + 1); + const columns = this.dataGrid.columnManager.takeColumnsByCells(columnsRange.startCell, columnsRange.endCell); + const cells: any = []; + + if (!columns || !rows) { + return cells; + } + + if (rows.length !== 1 || columns.length !== 1) { + cells.push(columns.map(column => column.name)); + } + + rows.forEach(row => { + let result: any[] = []; + + columns.forEach(column => { + if(column.type === COLUMN_TYPES.index) { + result.push(column.formatFn(this.createCellConfig({ + region: 'row-header', + row: row.index, + column: column.index, + value: row.index + }))); + } else { + result.push(column.formatFn(this.createCellConfig({ + region: 'body', + row: row.index, + column: column.index, + value: row.values[column.index] + }))); + } + }); + + cells.push(result); + }); + + return cells; + } + + copyToClipboard() { + let queryCommandEnabled = true; + + try { + document.execCommand('Copy'); + } catch (e) { + queryCommandEnabled = false; + } + + if (bkUtils.isElectron || !queryCommandEnabled) { + return; + } + + const cells = this.getSelectedCells(); + const cellsData = this.exportCellsTo(cells, 'tabs'); + + this.executeCopy(cellsData); + } + + CSVDownload(selectedOnly) { + const href = 'data:attachment/csv;charset=utf-8,' + encodeURI(this.getCSVFromCells(selectedOnly)); + const target = '_black'; + const filename = 'tableRows.csv'; + const anchor = document.createElement('a'); + const event = document.createEvent("MouseEvents"); + + anchor.href = href; + anchor.target = target; + anchor.download = filename; + event.initEvent("click", true, false); + anchor.dispatchEvent(event); + }; + + createCellConfig( + options: { row: number, column: number, value: any, region: DataModel.CellRegion } + ): CellRenderer.ICellConfig { + return { + region: '', + row: 0, + column: 0, + value: 0, + x: 0, + y: 0, + metadata: {}, + width: 0, + height: 0, + ...options + } + } + + private getCSVFromCells(selectedOnly: boolean) { + if (selectedOnly) { + return this.exportCellsTo(this.getSelectedCells(), 'csv'); + } + + return this.exportCellsTo(this.getAllCells(), 'csv'); + } + + private executeCopy(text: string) { + const input = document.createElement('textarea'); + + document.body.appendChild(input); + input.value = text; + input.select(); + + try { + Jupyter.keyboard_manager.enabled = false; + document.execCommand('Copy'); + Jupyter.keyboard_manager.enabled = true; + } catch(error) { + document.execCommand('Copy'); + } + + input.remove(); + } + + private exportCellsTo(cells, format) { + let fix = (s) => s.replace(/"/g, '""'); + let exportOptions = { + sep: ',', + qot: '"', + eol: '\n' + }; + + function exportCells(cells, exportOptions) { + let out = ''; + + for (let i = 0; i < cells.length; i++) { + let row = cells[i]; + + for (let j = 0; j < row.length; j++) { + if (j !== 0) { + out = out + exportOptions.sep; + } + + let cellData = row[j]; + if (cellData === null) { + cellData = ''; + } + + cellData = cellData + ''; + out = [ + out, + exportOptions.qot, + (cellData !== undefined && cellData !== null ? fix(cellData) : ''), + exportOptions.qot + ].join(''); + } + + out = out + exportOptions.eol; + } + + return out; + } + + if (format === 'tabs') { + exportOptions.sep = '\t'; + exportOptions.qot = ''; + fix = (s) => s.replace(/\t/g, ' '); + } + + if (navigator.appVersion.indexOf('Win') !== -1) { + exportOptions.eol = '\r\n'; + } + + return exportCells(cells, exportOptions); + }; +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/CellRendererFactory.ts b/js/notebook/src/tableDisplay/dataGrid/cell/CellRendererFactory.ts new file mode 100644 index 0000000000..6e24220dcc --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/CellRendererFactory.ts @@ -0,0 +1,30 @@ +/* + * 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 { BeakerxDataGrid } from "../BeakerxDataGrid"; +import BeakerxCellRenderer from "./BeakerxCellRenderer"; + +export class CellRendererFactory { + private dataGrid: BeakerxDataGrid; + + constructor(dataGrid: BeakerxDataGrid) { + this.dataGrid = dataGrid; + } + + getRenderer() { + return new BeakerxCellRenderer(this.dataGrid); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/CellSelectionManager.ts b/js/notebook/src/tableDisplay/dataGrid/cell/CellSelectionManager.ts new file mode 100644 index 0000000000..d9ce77d22a --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/CellSelectionManager.ts @@ -0,0 +1,187 @@ +/* + * Copyright 2018 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 {CellRenderer} from "@phosphor/datagrid"; +import ICellConfig = CellRenderer.ICellConfig; +import {ICellData} from "../interface/ICell"; +import {BeakerxDataGrid} from "../BeakerxDataGrid"; +import DataGridColumn from "../column/DataGridColumn"; + +export interface IRangeCells { + startCell: ICellData, + endCell: ICellData +} + +export default class CellSelectionManager { + selectedCellColor = '#B0BED9'; + startCellData: ICellData|null; + endCellData: ICellData|null; + enabled: boolean; + dataGrid: BeakerxDataGrid; + + constructor(dataGrid: BeakerxDataGrid) { + this.enabled = false; + this.dataGrid = dataGrid; + + this.bindEvents(); + } + + setStartCell(cellData: ICellData) { + this.startCellData = cellData; + } + + setEndCell(cellData: ICellData) { + this.endCellData = cellData; + } + + getColumnsRangeCells(): IRangeCells|null { + if(!this.startCellData || !this.endCellData) { + return null; + } + + let startCell = this.startCellData.column < this.endCellData.column ? this.startCellData : this.endCellData; + let endCell = this.startCellData.column < this.endCellData.column ? this.endCellData : this.startCellData; + + if(startCell.column === endCell.column && startCell.type !== endCell.type) { + startCell = this.startCellData.type < this.endCellData.type ? this.startCellData : this.endCellData; + endCell = this.startCellData.type < this.endCellData.type ? this.endCellData : this.startCellData; + } + + return { + startCell, + endCell + } + } + + getRowsRangeCells():IRangeCells|null { + if(!this.startCellData || !this.endCellData) { + return null; + } + + const startCell = this.startCellData.row < this.endCellData.row ? this.startCellData : this.endCellData; + const endCell = this.startCellData.row < this.endCellData.row ? this.endCellData : this.startCellData; + + return { + startCell, + endCell + } + } + + isBetweenRows(config: ICellConfig) { + const rowsRange = this.getRowsRangeCells(); + + if(!rowsRange) { + return false; + } + + return config.row >= rowsRange.startCell.row && config.row <= rowsRange.endCell.row + } + + isBetweenColumns(config: ICellConfig) { + const constcolumnsRange = this.getColumnsRangeCells(); + + if(!constcolumnsRange) { + return false; + } + + const colType = DataGridColumn.getColumnTypeByRegion(config.region); + + return ( + (colType === constcolumnsRange.startCell.type || colType === constcolumnsRange.endCell.type) && + config.column >= constcolumnsRange.startCell.column && + config.column <= constcolumnsRange.endCell.column + ); + } + + enable() { + this.enabled = true; + } + + clear() { + this.enabled = false; + this.startCellData = null; + this.endCellData = null; + this.dataGrid.repaint(); + } + + isSelected(config: ICellConfig) { + if (!this.enabled || !this.startCellData || !this.endCellData) { + return false; + } + + return this.isBetweenColumns(config) && this.isBetweenRows(config); + } + + getBackgroundColor(config) { + if (!this.startCellData || !this.endCellData) { + return ''; + } + + return this.isSelected(config) ? this.selectedCellColor : ''; + } + + private bindEvents() { + this.dataGrid.node.addEventListener('mouseup', this.handleMouseUp.bind(this)); + this.dataGrid.node.addEventListener('mousedown', this.handleMouseDown.bind(this)); + this.dataGrid.node.addEventListener('mousemove', this.handleBodyCellHover.bind(this)); + } + + private handleMouseDown(event: MouseEvent) { + if (this.dataGrid.isOverHeader(event)) { + return; + } + + const cellData = this.dataGrid.getCellData(event.clientX, event.clientY); + + if (!cellData) { + return; + } + + if (event.shiftKey && this.startCellData) { + return this.setEndCell(cellData); + } + + this.dataGrid.cellFocusManager.setFocusedCell(cellData); + this.setStartCell(cellData); + } + + private handleBodyCellHover(event: MouseEvent) { + if (event.buttons !== 1 || this.dataGrid.isOverHeader(event)) { + return; + } + + const cellData = this.dataGrid.getCellData(event.clientX, event.clientY); + + if (cellData) { + this.setEndCell(cellData); + this.enable(); + this.dataGrid.repaint(); + } + } + + private handleMouseUp(event: MouseEvent) { + if (this.dataGrid.isOverHeader(event)) { + return; + } + + const cellData = this.dataGrid.getCellData(event.clientX, event.clientY); + if (cellData) { + this.setEndCell(cellData); + this.enable(); + this.dataGrid.repaint(); + } + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/CellTooltip.ts b/js/notebook/src/tableDisplay/dataGrid/cell/CellTooltip.ts new file mode 100644 index 0000000000..d55c3e2a2e --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/CellTooltip.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2018 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 class CellTooltip { + timeoutId: number; + node: HTMLElement; + container: HTMLElement; + + constructor(text: string, container: HTMLElement) { + this.container = container; + this.node = document.createElement('div'); + this.node.style.position = 'absolute'; + this.node.style.visibility = 'visible'; + this.node.innerText = text; + this.node.classList.add('p-DataGrid-tooltip'); + } + + show(x: number, y: number): void { + if (this.container.contains(this.node)) { + return; + } + + this.node.style.left = `${x}px`; + this.node.style.top = `${y}px`; + + this.container.appendChild(this.node); + clearTimeout(this.timeoutId); + setTimeout(() => this.node.classList.add('visible'), 300); + } + + hide(): void { + this.node.classList.remove('visible'); + + clearTimeout(this.timeoutId); + this.timeoutId = setTimeout(() => { + if (this.container.contains(this.node)) { + this.container.removeChild(this.node); + } + }, 300); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/CellTooltipManager.ts b/js/notebook/src/tableDisplay/dataGrid/cell/CellTooltipManager.ts new file mode 100644 index 0000000000..884877204e --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/CellTooltipManager.ts @@ -0,0 +1,81 @@ +/* + * Copyright 2018 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 {BeakerxDataGrid} from "../BeakerxDataGrid"; +import {ICellData} from "../interface/ICell"; +import CellTooltip from "./CellTooltip"; +import {DEFAULT_GRID_PADDING} from "../style/dataGridStyle"; +import {selectColumnIndexByPosition} from "../column/selectors"; +import {COLUMN_TYPES} from "../column/enums"; + +export default class CellTooltipManager { + dataGrid: BeakerxDataGrid; + tooltips: string[][]; + tooltip: CellTooltip; + lastData: ICellData; + + constructor(dataGrid: BeakerxDataGrid, tooltips: string[][]) { + this.tooltips = tooltips; + this.dataGrid = dataGrid; + + if (tooltips.length) { + this.dataGrid.cellHovered.connect(this.handleCellHovered, this); + this.tooltip = new CellTooltip( + '', + this.dataGrid.node + ); + } + } + + hideTooltip() { + this.tooltip && this.tooltip.hide(); + } + + handleCellHovered(sender: BeakerxDataGrid, data: ICellData) { + if (!data || data.type === COLUMN_TYPES.index) { + return this.tooltip.hide(); + } + + if (this.isShown(data)) { + return; + } + + let column = selectColumnIndexByPosition( + sender.store.state, + data.column, + data.type + ); + this.tooltip.hide(); + this.lastData = data; + + setTimeout(() => { + let x = data.offset + data.delta + DEFAULT_GRID_PADDING; + + this.tooltip.node.innerText = this.tooltips[data.row][column] || ''; + this.tooltip.show(x, data.offsetTop); + }, 300); + } + + private isShown(data) { + if (!this.lastData) { + return false; + } + + const { row, column, type } = this.lastData; + + return row === data.row && column === data.column && type === data.type; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/cell/DataGridCell.ts b/js/notebook/src/tableDisplay/dataGrid/cell/DataGridCell.ts new file mode 100644 index 0000000000..b5194e62e2 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/cell/DataGridCell.ts @@ -0,0 +1,91 @@ +/* + * 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 { CellRenderer } from "@phosphor/datagrid"; +import {ICellData} from "../interface/ICell"; +import {BeakerxDataGrid} from "../BeakerxDataGrid"; +import {DataGridHelpers} from "../dataGridHelpers"; +import findSectionIndex = DataGridHelpers.findSectionIndex; +import {COLUMN_TYPES} from "../column/enums"; + +export default class DataGridCell { + static isHeaderCell(config: CellRenderer.ICellConfig) { + return config.region === 'column-header' || config.region === 'corner-header'; + } + + static getCellData(dataGrid: BeakerxDataGrid, clientX: number, clientY: number): ICellData|null { + if (!dataGrid.viewport) { + return null; + } + + let column: { index: number, delta: number } | null = null; + let rect = dataGrid.viewport.node.getBoundingClientRect(); + let x = clientX - rect.left; + let y = clientY - rect.top; + + // Test for a match in the corner header first. + if (x <= dataGrid.headerWidth && y <= dataGrid.headerHeight) { + if (!column && x <= dataGrid.headerWidth) { + column = findSectionIndex(dataGrid.rowHeaderSections, y); + } + + if (column) { + return { + column: column.index, + row: 0, + delta: column.delta, + type: COLUMN_TYPES.index, + offset: dataGrid.getColumnOffset(column.index, COLUMN_TYPES.index), + offsetTop: dataGrid.headerHeight + }; + } + + return null; + } + + let section = dataGrid.columnSections; + let columnType = COLUMN_TYPES.body; + let pos = x + dataGrid.scrollX - dataGrid.headerWidth; + if (x <= dataGrid.rowHeaderSections.totalSize) { + section = dataGrid.rowHeaderSections; + columnType = COLUMN_TYPES.index; + pos += dataGrid.headerWidth; + } + + let row: { index: number, delta: number } | null = DataGridCell.findHoveredRowIndex(dataGrid, y); + column = findSectionIndex(section, pos); + + if (column) { + return { + column: column.index, + delta: column.delta, + row: row ? row.index : 0, + type: columnType, + offset: dataGrid.getColumnOffset(column.index, columnType), + offsetTop: row ? dataGrid.getRowOffset(row.index) + dataGrid.headerHeight : 0 + }; + } + + return null; + } + + static findHoveredRowIndex(dataGrid: BeakerxDataGrid, y: number) { + // Convert the position into unscrolled coordinates. + let pos = y + dataGrid.scrollY - dataGrid.headerHeight; + + return findSectionIndex(dataGrid.rowSections, pos); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/column/ColumnFilter.ts b/js/notebook/src/tableDisplay/dataGrid/column/ColumnFilter.ts new file mode 100644 index 0000000000..f7ad8d893d --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/ColumnFilter.ts @@ -0,0 +1,161 @@ +/* + * Copyright 2018 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 { BeakerxDataGrid } from "../BeakerxDataGrid"; +import DataGridColumn from "./DataGridColumn"; +import {selectColumnPosition, selectColumnWidth} from "./selectors"; + +export default class ColumnFilter { + dataGrid: BeakerxDataGrid; + column: DataGridColumn; + filterNode: HTMLElement; + filterIcon: HTMLSpanElement; + clearIcon: HTMLSpanElement; + filterInput: HTMLInputElement; + debounceId: any; + useSearch: boolean; + + static DEBOUNCE_TIME_IN_MS = 250; + + constructor(dataGrid: BeakerxDataGrid, column: DataGridColumn, options: { x, y, width, height }) { + this.dataGrid = dataGrid; + this.column = column; + + this.addInputNode(options); + } + + showSearchInput(shouldFocus: boolean) { + this.useSearch = true; + this.filterIcon.classList.remove('fa-filter'); + this.filterIcon.classList.add('fa-search'); + this.showInput(shouldFocus); + } + + showFilterInput(shouldFocus: boolean) { + this.useSearch = false; + this.filterIcon.classList.add('fa-filter'); + this.filterIcon.classList.remove('fa-search'); + this.showInput(shouldFocus); + } + + hideInput() { + this.filterNode.style.visibility = 'hidden'; + } + + updateInputNode() { + this.filterNode.style.width = `${selectColumnWidth(this.dataGrid.store.state, this.column)}px`; + this.updateInputPosition(); + } + + private updateInputPosition() { + const position = this.dataGrid.getColumnOffset( + selectColumnPosition(this.dataGrid.store.state, this.column), + this.column.type + ); + + this.filterNode.style.left = `${position}px`; + } + + private showInput(shouldFocus: boolean): void { + this.updateInputNode(); + + if (this.filterNode.style.visibility === 'visible') { + return; + } + + this.filterNode.style.visibility = 'visible'; + this.dataGrid.viewport.node.appendChild(this.filterNode); + + if (shouldFocus) { + this.filterInput.focus(); + } + } + + private filterHandler(event: KeyboardEvent) { + if (event.keyCode === 27 || event.keyCode === 13 || !this.filterInput) { return; } + + if (this.useSearch) { + return this.column.search(this.createExpression(this.filterInput.value)); + } + + this.column.filter(this.createExpression(this.filterInput.value)); + } + + private debouncedFilterHandler(event: KeyboardEvent) { + clearTimeout(this.debounceId); + this.debounceId = setTimeout( + () => this.filterHandler(event), + ColumnFilter.DEBOUNCE_TIME_IN_MS + ); + } + + private createExpression(value: string) { + if (this.useSearch) { + return this.createSearchExpression(value); + } + + return this.createFilterExpression(value); + } + + private createFilterExpression(value: string): string { + return value.replace('$', `${this.column.name}`); + } + + private createSearchExpression(value: string) { + const expression = `String($).indexOf("${String(value)}") !== -1`; + + return this.createFilterExpression(expression); + } + + private addInputNode(options: { x, y, width, height }): void { + const filterNode = document.createElement('div'); + + filterNode.innerHTML = `
+ + + +
`; + + filterNode.classList.add('input-clear-growing'); + filterNode.style.width = `${options.width}px`; + filterNode.style.height = `${options.height}px`; + filterNode.style.left = `${options.x}px`; + filterNode.style.top = `${options.y}px`; + filterNode.style.position = 'absolute'; + + this.filterNode = filterNode; + this.filterIcon = this.filterNode.querySelector('.filter-icon') || new HTMLSpanElement(); + this.filterInput = this.filterNode.querySelector('input') || new HTMLInputElement(); + this.clearIcon = this.filterNode.querySelector('.clear-filter') || new HTMLSpanElement(); + this.filterInput.style.height = `${options.height}px`; + + this.bindEvents(); + } + + private bindEvents() { + this.filterInput.addEventListener('mousedown', (event) => event.stopImmediatePropagation()); + this.filterNode.addEventListener('mousedown', (event) => event.stopImmediatePropagation()); + this.filterInput.addEventListener('keyup', this.debouncedFilterHandler.bind(this)); + + if (this.clearIcon) { + this.clearIcon.addEventListener('mousedown', () => { + this.column.columnManager.resetFilters(); + }); + } + + + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/column/ColumnManager.ts b/js/notebook/src/tableDisplay/dataGrid/column/ColumnManager.ts new file mode 100644 index 0000000000..4422903d36 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/ColumnManager.ts @@ -0,0 +1,286 @@ +/* + * Copyright 2018 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 DataGridColumn from "./DataGridColumn"; +import { ITriggerOptions } from "../headerMenu/HeaderMenu"; +import { CellRenderer } from "@phosphor/datagrid"; +import { chain, find } from '@phosphor/algorithm' +import { BeakerxDataGrid } from "../BeakerxDataGrid"; +import { Signal } from '@phosphor/signaling'; +import {ICellData} from "../interface/ICell"; +import {IColumns} from "../interface/IColumn"; +import {BeakerxDataStore} from "../store/dataStore"; +import {selectColumnNames, selectHasIndex} from "../model/selectors"; +import {DataGridColumnsAction} from "../store/DataGridAction"; +import { + selectBodyColumnNames, + selectColumnIndexByPosition, + selectIndexColumnNames +} from "./selectors"; +import { + UPDATE_COLUMNS_FILTERS, UPDATE_COLUMNS_POSITION, UPDATE_COLUMNS_VISIBILITY +} from "./reducer"; +import {COLUMN_TYPES, SORT_ORDER} from "./enums"; + +export interface IBkoColumnsChangedArgs { + type: COLUMN_CHANGED_TYPES, + value: any, + column: DataGridColumn +} + +export enum COLUMN_CHANGED_TYPES { + 'columnSort', + 'columnMove' +} + +export default class ColumnManager { + store: BeakerxDataStore; + dataGrid: BeakerxDataGrid; + columns: IColumns = {}; + columnsChanged = new Signal(this); + + constructor(dataGrid: BeakerxDataGrid) { + this.dataGrid = dataGrid; + this.store = this.dataGrid.store; + } + + get bodyColumns() { + return this.columns[COLUMN_TYPES.body]; + } + + get indexColumns() { + return this.columns[COLUMN_TYPES.index]; + } + + get bodyColumnNames(): string[] { + return selectBodyColumnNames(this.store.state); + } + + get indexColumnNames(): string[] { + return selectIndexColumnNames(this.store.state); + } + + addColumns() { + let bodyColumns: DataGridColumn[] = []; + let indexColumns: DataGridColumn[] = []; + + this.columns[COLUMN_TYPES.index] = indexColumns; + this.columns[COLUMN_TYPES.body] = bodyColumns; + + this.addIndexColumns(); + this.addBodyColumns(); + } + + getColumn(config: CellRenderer.ICellConfig): DataGridColumn { + const columnType = DataGridColumn.getColumnTypeByRegion(config.region); + const columnIndex = selectColumnIndexByPosition(this.store.state, columnType, config.column); + + return this.columns[columnType][columnIndex]; + } + + getColumnByIndex(columnType: COLUMN_TYPES, index: number) { + return this.columns[columnType][index]; + } + + getColumnByPosition(columnType: COLUMN_TYPES, position: number) { + const columnIndex = selectColumnIndexByPosition(this.store.state, columnType, position); + + return this.getColumnByIndex(columnType, columnIndex); + } + + getColumnByName(columnName: string): DataGridColumn|undefined { + return find( + chain(this.bodyColumns, this.indexColumns), + (column: DataGridColumn) => column.name === columnName + ); + } + + destroy() { + this.destroyAllColumns(); + Signal.disconnectAll(this); + } + + sortByColumn(column: DataGridColumn, sortOrder: SORT_ORDER) { + this.columnsChanged.emit({ + column, + type: COLUMN_CHANGED_TYPES.columnSort, + value: sortOrder + }); + this.dataGrid.rowManager.sortByColumn(column); + this.dataGrid.model.reset(); + } + + resetFilters() { + const resetFilterFn = column => column.columnFilter.hideInput(); + + this.store.dispatch(new DataGridColumnsAction( + UPDATE_COLUMNS_FILTERS, + { + hasIndex: selectHasIndex(this.store.state), + value: selectColumnNames(this.store.state).map(name => ''), + defaultValue: [''] + })); + this.dataGrid.model.setFilterHeaderVisible(false); + this.bodyColumns.forEach(resetFilterFn); + this.indexColumns.forEach(resetFilterFn); + this.dataGrid.rowManager.filterRows(); + this.dataGrid.repaint(); + } + + showFilters(column?: DataGridColumn) { + this.showFilterInputs(false, column); + } + + showSearch(column?: DataGridColumn) { + this.showFilterInputs(true, column); + } + + updateColumnFilterNodes() { + this.bodyColumns.forEach(column => column.columnFilter.updateInputNode()); + this.indexColumns.forEach(column => column.columnFilter.updateInputNode()); + } + + updateColumnMenuTriggers() { + this.bodyColumns.forEach(column => column.menu.updateTriggerPosition()); + this.indexColumns.forEach(column => column.menu.updateTriggerPosition()); + } + + takeColumnsByCells(startCell: ICellData, endCell: ICellData) { + let result: any[] = []; + + if (endCell.type !== COLUMN_TYPES.index) { + result = this.bodyColumns + .map(column => this.columns[column.type][selectColumnIndexByPosition(this.store.state, column.type, column.index)]) + .filter(column => column.getVisible()) + .slice(startCell.column, endCell.column + 1); + } + + if (startCell.type === COLUMN_TYPES.index) { + result.unshift(this.indexColumns[0]); + } + + return result; + } + + takeColumnByCell(cellData: ICellData): DataGridColumn|null { + const column = this.dataGrid.columnManager.getColumnByPosition(cellData.type, cellData.column); + + return column ? column : null; + } + + showAllColumns() { + const columnNames = selectColumnNames(this.store.state); + const hasIndex = selectHasIndex(this.store.state); + + this.store.dispatch(new DataGridColumnsAction(UPDATE_COLUMNS_VISIBILITY, { + hasIndex, + value: columnNames.map(() => true), + defaultValue: [0] + })); + + this.dataGrid.resize(); + } + + resetColumnsAlignment() { + this.bodyColumns.forEach((column) => { + column.resetAlignment(); + }); + this.dataGrid.model.reset(); + } + + resetColumnsOrder() { + const columnNames = selectColumnNames(this.store.state); + const hasIndex = selectHasIndex(this.store.state); + + this.store.dispatch(new DataGridColumnsAction(UPDATE_COLUMNS_POSITION, { + hasIndex, + value: columnNames.map((index, order) => order), + defaultValue: [0] + })); + + this.dataGrid.resize(); + this.dataGrid.model.reset(); + } + + resetColumnStates() { + this.indexColumns.forEach(column => column.resetState()); + this.bodyColumns.forEach(column => column.resetState()); + } + + setColumnsDataTypePrecission(precission: number) { + this.indexColumns.forEach(column => column.setDataTypePrecission(precission)); + this.bodyColumns.forEach(column => column.setDataTypePrecission(precission)); + } + + recalculateMinMaxValues() { + this.recalculateColumnsMinMax(this.bodyColumns); + this.recalculateColumnsMinMax(this.indexColumns); + } + + private recalculateColumnsMinMax(columns: DataGridColumn[]) { + columns.forEach(column => { + column.addMinMaxValues(); + }); + } + + private showFilterInputs(useSearch: boolean, column?: DataGridColumn) { + const methodToCall = useSearch ? 'showSearchInput' : 'showFilterInput'; + const showInputsFn = columnItem => columnItem.columnFilter[methodToCall]( + column === columnItem, + this.dataGrid.getColumnOffset(columnItem.index, columnItem.type) + ); + + this.dataGrid.model.setFilterHeaderVisible(true); + this.bodyColumns.forEach(showInputsFn); + this.indexColumns.forEach(showInputsFn); + } + + private addBodyColumns() { + selectBodyColumnNames(this.store.state) + .forEach((name, index) => this.addColumn(name, index, COLUMN_TYPES.body)); + } + + private addIndexColumns(): void { + selectIndexColumnNames(this.store.state) + .forEach((name, index) => this.addColumn(name, index, COLUMN_TYPES.index)); + } + + private addColumn(name, index, type) { + let menuOptions: ITriggerOptions = { + x: this.dataGrid.getColumnOffset(index, type), + y: 0, + width: this.dataGrid.baseColumnHeaderSize, + height: this.dataGrid.baseColumnHeaderSize + }; + + let column = new DataGridColumn({ + index, + name, + menuOptions, + type, + }, this.dataGrid, this); + + this.columns[type].push(column); + } + + private destroyAllColumns() { + this.indexColumns.forEach((column: DataGridColumn) => column.destroy()); + this.bodyColumns.forEach((column: DataGridColumn) => column.destroy()); + + Signal.disconnectAll(this); + } + +} diff --git a/js/notebook/src/tableDisplay/dataGrid/column/DataGridColumn.ts b/js/notebook/src/tableDisplay/dataGrid/column/DataGridColumn.ts new file mode 100644 index 0000000000..19d0df8e71 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/DataGridColumn.ts @@ -0,0 +1,446 @@ +/* + * Copyright 2018 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 ColumnMenu from "../headerMenu/ColumnMenu"; +import IndexMenu from "../headerMenu/IndexMenu"; +import { BeakerxDataGrid } from "../BeakerxDataGrid"; +import {IColumnOptions} from "../interface/IColumn"; +import { ICellData } from "../interface/ICell"; +import { CellRenderer, DataModel, TextRenderer } from "@phosphor/datagrid"; +import {ALL_TYPES, getDisplayType, isDoubleWithPrecision} from "../dataTypes"; +import { minmax } from '@phosphor/algorithm'; +import { HIGHLIGHTER_TYPE } from "../interface/IHighlighterState"; +import ColumnManager, { COLUMN_CHANGED_TYPES, IBkoColumnsChangedArgs } from "./ColumnManager"; +import ColumnFilter from "./ColumnFilter"; +import {ITriggerOptions} from "../headerMenu/HeaderMenu"; +import CellTooltip from "../cell/CellTooltip"; +import { + selectColumnDataType, + selectColumnDataTypeName, + selectColumnDisplayType, + selectColumnFilter, + selectColumnHorizontalAlignment, + selectColumnKeepTrigger, selectColumnPosition, selectColumnSortOrder, + selectColumnState, selectColumnVisible, selectColumnFormatForTimes +} from "./selectors"; +import {DataGridColumnAction} from "../store/DataGridAction"; +import { + selectColumnsVisible, + selectHasIndex, + selectInitialColumnAlignment, + selectStringFormatForColumn, + selectFormatForTimes, + selectStringFormatForType, selectRenderer +} from "../model/selectors"; +import { + UPDATE_COLUMN_DISPLAY_TYPE, + UPDATE_COLUMN_FILTER, UPDATE_COLUMN_FORMAT_FOR_TIMES, + UPDATE_COLUMN_HORIZONTAL_ALIGNMENT, + UPDATE_COLUMN_POSITION, UPDATE_COLUMN_SORT_ORDER, + UPDATE_COLUMN_VISIBILITY, UPDATE_COLUMN_WIDTH +} from "./reducer"; +import {BeakerxDataStore} from "../store/dataStore"; +import {COLUMN_TYPES, SORT_ORDER} from "./enums"; +import {UPDATE_COLUMN_RENDERER} from "../model/reducer"; +import {RENDERER_TYPE} from "../interface/IRenderer"; + +export default class DataGridColumn { + index: number; + name: string; + type: COLUMN_TYPES; + menu: ColumnMenu|IndexMenu; + dataGrid: BeakerxDataGrid; + store: BeakerxDataStore; + columnManager: ColumnManager; + columnFilter: ColumnFilter; + formatFn: CellRenderer.ConfigFunc; + minValue: any; + maxValue: any; + dataTypeTooltip: CellTooltip; + + constructor(options: IColumnOptions, dataGrid: BeakerxDataGrid, columnManager: ColumnManager) { + this.index = options.index; + this.name = options.name; + this.type = options.type; + this.dataGrid = dataGrid; + this.store = dataGrid.store; + this.columnManager = columnManager; + + this.handleHeaderCellHovered = this.handleHeaderCellHovered.bind(this); + + this.assignFormatFn(); + this.createMenu(options.menuOptions); + this.addColumnFilter(options.menuOptions); + this.addDataTypeTooltip(); + this.connectToHeaderCellHovered(); + this.connectToColumnsChanged(); + this.addMinMaxValues(); + } + + static getColumnTypeByRegion(region: DataModel.CellRegion) { + if (region === 'row-header' || region === 'corner-header') { + return COLUMN_TYPES.index; + } + + return COLUMN_TYPES.body; + } + + assignFormatFn() { + this.formatFn = this.dataGrid.model.dataFormatter + .getFormatFnByDisplayType(this.getDisplayType(), this.getState()); + } + + createMenu(menuOptions: ITriggerOptions): void { + if (this.type === COLUMN_TYPES.index) { + this.menu = new IndexMenu(this, menuOptions); + + return; + } + + this.menu = new ColumnMenu(this, menuOptions); + } + + addColumnFilter(menuOptions) { + this.columnFilter = new ColumnFilter( + this.dataGrid, + this, + { + ...menuOptions, + y: this.dataGrid.baseColumnHeaderSize - 1, + width: this.dataGrid.columnSections.sectionSize(this.index) + } + ); + } + + setDisplayType(displayType: ALL_TYPES|string) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_DISPLAY_TYPE, + { value: displayType, columnIndex: this.index, columnType: this.type } + )); + this.assignFormatFn(); + this.dataGrid.setInitialSectionWidth(this); + } + + setTimeDisplayType(timeUnit) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_FORMAT_FOR_TIMES, + { value: timeUnit, columnIndex: this.index, columnType: this.type } + )); + this.setDisplayType(ALL_TYPES.datetime); + } + + hide() { + this.menu.hideTrigger(); + this.toggleVisibility(false); + } + + show() { + this.toggleVisibility(true); + } + + search(filter: string) { + if (filter === this.getFilter()) { + return; + } + + this.updateColumnFilter(filter); + this.dataGrid.rowManager.searchRows(); + this.dataGrid.model.reset(); + } + + filter(filter: string) { + if (filter === this.getFilter()) { + return; + } + + this.updateColumnFilter(filter); + this.dataGrid.rowManager.filterRows(); + this.dataGrid.model.reset(); + } + + resetFilter() { + this.updateColumnFilter(''); + this.dataGrid.rowManager.filterRows(); + this.dataGrid.model.reset(); + } + + connectToColumnsChanged() { + this.columnManager.columnsChanged.connect(this.onColumnsChanged.bind(this)); + } + + connectToHeaderCellHovered() { + this.dataGrid.headerCellHovered.connect(this.handleHeaderCellHovered); + } + + handleHeaderCellHovered(sender: BeakerxDataGrid, data: ICellData) { + const column = data && this.columnManager.getColumnByPosition(data.type, data.column); + + if(!data || column !== this) { + this.menu.hideTrigger(); + this.toggleDataTooltip(false); + + return; + } + + this.menu.showTrigger(); + this.toggleDataTooltip(true, data); + } + + setAlignment(horizontalAlignment: TextRenderer.HorizontalAlignment) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_HORIZONTAL_ALIGNMENT, + { value: horizontalAlignment, columnIndex: this.index, columnType: this.type } + )); + } + + resetAlignment() { + this.setAlignment(selectInitialColumnAlignment( + this.store.state, + this.getDataType(), + this.name + )); + } + + setWidth(width: number) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_WIDTH, + { value: width, columnIndex: this.index, columnType: this.type } + )); + this.columnManager.updateColumnFilterNodes(); + this.columnManager.updateColumnMenuTriggers(); + this.dataGrid.resize(); + } + + getState() { + return selectColumnState(this.store.state, this); + } + + getAlignment() { + return selectColumnHorizontalAlignment(this.store.state, this); + } + + getVisible() { + return selectColumnVisible(this.store.state, this); + } + + getDataType() { + return selectColumnDataType(this.store.state, this); + } + + getSortOrder() { + return selectColumnSortOrder(this.store.state, this); + } + + getFilter() { + return selectColumnFilter(this.store.state, this); + } + + getKeepTrigger() { + return selectColumnKeepTrigger(this.store.state, this); + } + + getDataTypeName(): string { + return selectColumnDataTypeName(this.store.state, this); + } + + getDisplayType() { + return selectColumnDisplayType(this.store.state, this); + } + + getFormatForTimes() { + return selectColumnFormatForTimes(this.store.state, this); + } + + getPosition() { + return selectColumnPosition(this.store.state, this); + } + + getRenderer() { + return selectRenderer(this.store.state, this); + } + + getHighlighter(highlighterType: HIGHLIGHTER_TYPE) { + return this.dataGrid.highlighterManager.getColumnHighlighters(this, highlighterType); + } + + toggleHighlighter(highlighterType: HIGHLIGHTER_TYPE) { + this.dataGrid.highlighterManager.toggleColumnHighlighter(this, highlighterType); + } + + resetHighlighters() { + this.dataGrid.highlighterManager.removeColumnHighlighter(this); + } + + sort(sortOrder: SORT_ORDER) { + this.columnManager.sortByColumn(this, sortOrder); + } + + toggleSort() { + if (this.getSortOrder() !== SORT_ORDER.ASC) { + return this.sort(SORT_ORDER.ASC); + } + + this.sort(SORT_ORDER.DESC); + } + + getValueResolver(): Function { + const dataType = this.getDataType(); + + if(dataType === ALL_TYPES.datetime || dataType === ALL_TYPES.time) { + return this.dateValueResolver; + } + + return this.defaultValueResolver; + } + + move(destination: number) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_POSITION, + { + value: destination, + columnType: this.type, + columnIndex: this.index, + hasIndex: selectHasIndex(this.store.state) + }) + ); + + this.menu.hideTrigger(); + this.dataGrid.resize(); + } + + setDataTypePrecission(precission: number) { + if (isDoubleWithPrecision(this.getDisplayType())) { + this.setDisplayType(`4.${precission}`); + } + } + + addMinMaxValues() { + let valueResolver = this.getValueResolver(); + let valuesIterator = this.dataGrid.model.getColumnValuesIterator(this); + let minMax = minmax(valuesIterator, (a:any, b:any) => { + let value1 = valueResolver(a); + let value2 = valueResolver(b); + + if (value1 === value2) { + return 0; + } + + return value1 < value2 ? -1 : 1; + }); + + this.minValue = minMax ? minMax[0] : null; + this.maxValue = minMax ? minMax[1] : null; + } + + resetState() { + this.setTimeDisplayType(selectFormatForTimes(this.store.state)); + this.setDisplayType(getDisplayType( + this.getDataType(), + selectStringFormatForType(this.store.state), + selectStringFormatForColumn(this.store.state)[this.name] + )); + this.setAlignment(selectInitialColumnAlignment(this.store.state, this.getDataType(), name)); + this.toggleVisibility(selectColumnsVisible(this.store.state)[this.name] !== false); + this.toggleDataBarsRenderer(false); + this.resetHighlighters(); + this.resetFilter(); + this.move(this.index); + this.assignFormatFn(); + this.dataGrid.setInitialSectionWidth(this); + this.dataGrid.updateWidgetWidth(); + } + + destroy() { + this.menu.destroy(); + this.dataTypeTooltip.hide(); + } + + toggleDataBarsRenderer(enable?: boolean) { + const renderer = this.getRenderer(); + const enabled = enable === false || renderer && renderer.type === RENDERER_TYPE.DataBars; + + this.store.dispatch(new DataGridColumnAction(UPDATE_COLUMN_RENDERER, { + columnType: this.type, + columnName: this.name, + value: enabled ? null : { type: RENDERER_TYPE.DataBars, includeText: true } + })); + } + + private updateColumnFilter(filter: string) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_FILTER, + { value: filter, columnIndex: this.index, columnType: this.type } + )); + } + + private toggleVisibility(value) { + this.store.dispatch(new DataGridColumnAction(UPDATE_COLUMN_VISIBILITY, { + value, + columnIndex: this.index, + columnType: this.type, + hasIndex: selectHasIndex(this.store.state) + })); + this.dataGrid.resize(); + } + + private dateValueResolver(value) { + return value.timestamp; + } + + private defaultValueResolver(value) { + return value; + } + + private onColumnsChanged(sender: ColumnManager, args: IBkoColumnsChangedArgs) { + if (args.type !== COLUMN_CHANGED_TYPES.columnSort) { + return; + } + + if (args.column === this && args.value !== SORT_ORDER.NO_SORT) { + this.setColumnSortOrder(args.value); + this.dataGrid.highlighterManager.addColumnHighlighter(this, HIGHLIGHTER_TYPE.sort); + this.menu.showTrigger(); + } else { + this.setColumnSortOrder(SORT_ORDER.NO_SORT); + this.dataGrid.highlighterManager.removeColumnHighlighter(this, HIGHLIGHTER_TYPE.sort); + this.menu.hideTrigger(); + } + } + + private setColumnSortOrder(order: SORT_ORDER) { + this.store.dispatch(new DataGridColumnAction( + UPDATE_COLUMN_SORT_ORDER, + { value: order, columnIndex: this.index, columnType: this.type }) + ); + } + + private addDataTypeTooltip() { + this.dataTypeTooltip = new CellTooltip(this.getDataTypeName(), document.body); + } + + private toggleDataTooltip(show: boolean, data?: ICellData) { + const rect = this.dataGrid.node.getBoundingClientRect(); + + if (show && data) { + return this.dataTypeTooltip.show( + Math.ceil(rect.left + data.offset + 20), + Math.ceil(rect.top - 10) + ); + } + + this.dataTypeTooltip.hide(); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/column/columnAlignment.ts b/js/notebook/src/tableDisplay/dataGrid/column/columnAlignment.ts new file mode 100644 index 0000000000..30cfc9334b --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/columnAlignment.ts @@ -0,0 +1,44 @@ +/* + * 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 { ALL_TYPES } from "../dataTypes"; +import { TextRenderer } from "@phosphor/datagrid"; + +export const LEFT: TextRenderer.HorizontalAlignment = 'left'; +export const RIGHT: TextRenderer.HorizontalAlignment = 'right'; +export const CENTER: TextRenderer.HorizontalAlignment = 'center'; + +export const DEFAULT_ALIGNMENT = LEFT; + +export const ALIGNMENTS_BY_TYPE = { + 'datetime': CENTER, + 'integer': RIGHT, + 'double': RIGHT +}; + +export const ALIGNMENTS_BY_CHAR = { + 'C': CENTER, + 'R': RIGHT, + 'L': LEFT +}; + +export const getAlignmentByType = + (type: number): TextRenderer.HorizontalAlignment => + ALIGNMENTS_BY_TYPE[ALL_TYPES[type]] || DEFAULT_ALIGNMENT; + +export const getAlignmentByChar = + (char: string): TextRenderer.HorizontalAlignment => + ALIGNMENTS_BY_CHAR[char] || DEFAULT_ALIGNMENT; diff --git a/js/notebook/src/tableDisplay/dataGrid/column/enums.ts b/js/notebook/src/tableDisplay/dataGrid/column/enums.ts new file mode 100644 index 0000000000..d492eb737a --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/enums.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2018 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 enum COLUMN_TYPES { + index, + body +} + +export enum SORT_ORDER { + ASC, + DESC, + NO_SORT +} diff --git a/js/notebook/src/tableDisplay/dataGrid/column/reducer.ts b/js/notebook/src/tableDisplay/dataGrid/column/reducer.ts new file mode 100644 index 0000000000..763a33c7fa --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/reducer.ts @@ -0,0 +1,210 @@ +/* + * Copyright 2018 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 {Reducer} from "@phosphor/datastore"; +import { + default as DataGridAction, + DataGridColumnAction, + DataGridColumnsAction +} from "../store/DataGridAction"; +import {IColumnsState, IColumnState} from "../interface/IColumn"; +import {COLUMN_TYPES} from "./enums"; + +export const UPDATE_COLUMNS_STATES = 'UPDATE_COLUMNS_STATES'; +export const UPDATE_COLUMN_STATE = 'UPDATE_COLUMNS_STATE'; +export const UPDATE_COLUMNS_VISIBILITY = 'UPDATE_COLUMNS_VISIBILITY'; +export const UPDATE_COLUMNS_POSITION = 'UPDATE_COLUMNS_POSITION'; +export const UPDATE_COLUMN_POSITION = 'UPDATE_COLUMN_POSITION'; +export const UPDATE_COLUMNS_TYPES = 'UPDATE_COLUMNS_TYPES'; +export const UPDATE_COLUMNS_NAMES = 'UPDATE_COLUMNS_NAMES'; +export const UPDATE_COLUMN_VISIBILITY = 'UPDATE_COLUMN_VISIBILITY'; +export const UPDATE_COLUMNS_FILTERS = 'UPDATE_COLUMNS_FILTERS'; +export const UPDATE_COLUMN_FILTER = 'UPDATE_COLUMN_FILTER'; +export const UPDATE_COLUMN_HORIZONTAL_ALIGNMENT = 'UPDATE_COLUMN_HORIZONTAL_ALIGNMENT'; +export const UPDATE_COLUMN_FORMAT_FOR_TIMES = 'UPDATE_COLUMN_FORMAT_FOR_TIMES'; +export const UPDATE_COLUMN_DISPLAY_TYPE = 'UPDATE_COLUMN_DISPLAY_TYPE'; +export const UPDATE_COLUMN_SORT_ORDER = 'UPDATE_COLUMN_SORT_ORDER'; +export const UPDATE_COLUMN_WIDTH = 'UPDATE_COLUMN_WIDTH'; + +const reduceColumnsVisibility = reduceColumnsState('visible'); +const reduceColumnsPosition = reduceColumnsState('position'); +const reduceColumnsNames = reduceColumnsState('name'); +const reduceColumnsTypes = reduceColumnsState('dataTypeName'); +const reduceColumnsFilters = reduceColumnsState('filter'); +const reduceColumnHorizontalAlignment = reduceColumnStateProperty('horizontalAlignment'); +const reduceColumnFilter = reduceColumnStateProperty('filter'); +const reduceColumnFormatForTimes = reduceColumnStateProperty('formatForTimes'); +const reduceColumnDisplayType = reduceColumnStateProperty('displayType'); +const reduceColumnSortOrder = reduceColumnStateProperty('sortOrder'); +const reduceColumnWidth = reduceColumnStateProperty('width'); + +const columnReducer: Reducer = ( + state: IColumnsState, + action: DataGridColumnAction|DataGridColumnsAction|DataGridAction +): IColumnsState => { + switch(action.type) { + case UPDATE_COLUMNS_STATES: + return action.payload.value; + + case UPDATE_COLUMN_STATE: + return reduceColumnState(state, action); + + case UPDATE_COLUMNS_VISIBILITY: + return reduceColumnsVisibility(state, action); + + case UPDATE_COLUMN_VISIBILITY: + return reduceColumnVisibility(state, action); + + case UPDATE_COLUMNS_POSITION: + return reduceColumnsPosition(state, action); + + case UPDATE_COLUMN_POSITION: + return reduceColumnPosition(state, action); + + case UPDATE_COLUMNS_TYPES: + return reduceColumnsTypes(state, action); + + case UPDATE_COLUMNS_NAMES: + return reduceColumnsNames(state, action); + + case UPDATE_COLUMNS_FILTERS: + return reduceColumnsFilters(state, action); + + case UPDATE_COLUMN_FILTER: + return reduceColumnFilter(state, action); + + case UPDATE_COLUMN_HORIZONTAL_ALIGNMENT: + return reduceColumnHorizontalAlignment(state, action); + + case UPDATE_COLUMN_FORMAT_FOR_TIMES: + return reduceColumnFormatForTimes(state, action); + + case UPDATE_COLUMN_DISPLAY_TYPE: + return reduceColumnDisplayType(state, action); + + case UPDATE_COLUMN_SORT_ORDER: + return reduceColumnSortOrder(state, action); + + case UPDATE_COLUMN_WIDTH: + return reduceColumnWidth(state, action); + + default: + return state; + } +}; + +function reduceColumnsState(property: string) { + return (state, action: DataGridColumnsAction) => { + const { value, hasIndex, defaultValue = [] } = action.payload; + const bodyColumnValues = hasIndex ? value.slice(1) : value; + const indexColumnValues = hasIndex ? value.slice(0, 1) : defaultValue; + + const newState = new Map(state); + + indexColumnValues.forEach(updateColumnStateProperty(state, newState, property, COLUMN_TYPES.index)); + bodyColumnValues.forEach(updateColumnStateProperty(state, newState, property, COLUMN_TYPES.body)); + + return newState; + }; +} + +function updateColumnStateProperty(state, newState, property, columnType) { + return (value, index) => { + let key = `${columnType}_${index}`; + + newState.set(key, { + ...state.get(key), + [property]: value + }); + }; +} + +function reduceColumnState(state, action) { + if (!(action instanceof DataGridColumnAction)) { + return state; + } + + const { columnType, columnIndex, value } = action.payload; + const key = `${columnType}_${columnIndex}`; + const newState = new Map(state); + + newState.set(key, { ...state.get(key), ...value }); + + return newState; +} + +function reduceColumnStateProperty(property: string) { + return (state, action) => { + if (!(action instanceof DataGridColumnAction)) { + return state; + } + + const { columnType, columnIndex, value } = action.payload; + const key = `${columnType}_${columnIndex}`; + const newState = new Map(state); + + newState.set(key, { ...state.get(key), [property]: value }); + + return newState; + }; +} + +function reduceColumnVisibility(state, action) { + const newState: IColumnsState = reduceColumnStateProperty('visible')(state, action); + const visibleStates: IColumnState[] = Array.from(newState.values()).filter( + columnState => columnState.columnType === COLUMN_TYPES.body && columnState.visible + ); + + // Move column to the end or behind the visible columns + return reduceColumnPosition(newState, new DataGridColumnAction( + UPDATE_COLUMN_POSITION, + { + ...action.payload, + value: action.payload.value ? visibleStates.length - 1 : visibleStates.length + } + )); +} + +function reduceColumnPosition(state, action) { + const { columnType, columnIndex, value, hasIndex } = action.payload; + const order = createBodyColumnsOrderArray(state, columnType); + const lastPosition = order.indexOf(columnIndex); + const resultPositions: number[] = []; + + order.splice(lastPosition, 1); + order.splice(value, 0, columnIndex); + order.forEach((index, position) => { resultPositions[index] = position; }); + + return reduceColumnsPosition(state, new DataGridColumnsAction(UPDATE_COLUMNS_POSITION, { + hasIndex, + value: resultPositions, + defaultValue: [0] + })); +} + +function createBodyColumnsOrderArray(state, columnType): number[] { + const order: number[] = []; + + state.forEach((columnState) => { + if (columnState.columnType === columnType) { + order[columnState.position] = columnState.index + } + }); + + return order; +} + +export default columnReducer; diff --git a/js/notebook/src/tableDisplay/dataGrid/column/selectors.ts b/js/notebook/src/tableDisplay/dataGrid/column/selectors.ts new file mode 100644 index 0000000000..fd51750479 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/column/selectors.ts @@ -0,0 +1,147 @@ +/* + * Copyright 2018 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 {IBeakerxDataGridState} from "../store/dataStore"; +import {selectColumnNames} from "../model/selectors"; +import {find} from "@phosphor/algorithm"; +import {IColumnsState, IColumnState} from "../interface/IColumn"; +import {ALL_TYPES} from "../dataTypes"; +import {COLUMN_TYPES, SORT_ORDER} from "./enums"; +import {createSelector} from "reselect"; + +const defaultState: IColumnState = { + name: '', + index: 0, + columnType: COLUMN_TYPES.body, + dataTypeName: '', + dataType: ALL_TYPES.string, + displayType: ALL_TYPES.string, + keepTrigger: false, + horizontalAlignment: 'left', + formatForTimes: null, + visible: true, + sortOrder: SORT_ORDER.NO_SORT, + filter: null, + position: 0 +}; + +export const selectColumnStates = (state: IBeakerxDataGridState): IColumnsState => state.columns; +export const selectColumnStatesArray = createSelector( + [selectColumnStates], + (states) => Array.from(states.values()) +); + +export const selectBodyColumnStates = createSelector( + [selectColumnStatesArray], + (states) => ( + states + .filter(columnState => columnState.columnType === COLUMN_TYPES.body) + .sort((state1, state2) => state1.index - state2.index) + )); + +export const selectIndexColumnStates = createSelector( + [selectColumnStatesArray], + (states) => ( + states + .filter(columnState => columnState.columnType === COLUMN_TYPES.index) + .sort((state1, state2) => state1.index - state2.index) +)); + +export const selectBodyColumnNames = createSelector( + [selectBodyColumnStates], + (bodyColumnStates) => bodyColumnStates.map(state => state.name) +); + +export const selectIndexColumnNames = createSelector( + [selectIndexColumnStates], + (bodyColumnStates) => bodyColumnStates.map(state => state.name) +); + +export const selectBodyColumnVisibility = createSelector( + [selectBodyColumnStates], + (bodyColumnStates) => bodyColumnStates.map(state => state.visible) +); + +export const selectColumnStateByKey = (state, key) => selectColumnStates(state).get(key) || defaultState; + +export const selectColumnState = ( + state: IBeakerxDataGridState, + column +) => selectColumnStateByKey(state, `${column.type}_${column.index}`); + +export const selectColumnDataTypeName = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).dataTypeName +); + +export const selectColumnVisible = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).visible +); + +export const selectColumnHorizontalAlignment = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).horizontalAlignment +); + +export const selectColumnDisplayType = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).displayType +); + +export const selectColumnFilter = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).filter || '' +); + +export const selectColumnDataType = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).dataType +); + +export const selectColumnSortOrder = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).sortOrder +); + +export const selectColumnKeepTrigger = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).keepTrigger +); + +export const selectColumnFormatForTimes = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).formatForTimes || {} +); + +export const selectColumnWidth = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).width || 0 +); + +export const selectColumnPosition = (state: IBeakerxDataGridState, column) => ( + selectColumnState(state, column).position || 0 +); + +const selectColumnType = (state, columnType, position) => columnType; +const selectPosition = (state, columnType, position) => position; + +export const selectColumnIndexByPosition = createSelector( + [selectColumnStatesArray, selectColumnType, selectPosition], + (states, columnType, position): number => { + const columnState: IColumnState = find(states,(stateItem: IColumnState) => ( + stateItem.columnType === columnType && stateItem.position === position + )) || defaultState; + + return columnState.index; + } +); + +export const selectOutputColumnLimit = (state: IBeakerxDataGridState) => ( + beakerx.prefs && beakerx.prefs.outputColumnLimit + ? beakerx.prefs.outputColumnLimit + : selectColumnNames(state).length +); diff --git a/js/notebook/src/tableDisplay/dataGrid/contextMenu/DataGridContextMenu.ts b/js/notebook/src/tableDisplay/dataGrid/contextMenu/DataGridContextMenu.ts new file mode 100644 index 0000000000..95d59a2544 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/contextMenu/DataGridContextMenu.ts @@ -0,0 +1,36 @@ +/* + * 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 createHeaderContextMenuItems from './createHeaderContextMenuItems'; +import createCellContextMenuItems from './createCellContextMenuItems'; +import BkoContextMenu from '../../../contextMenu/BkoContextMenu'; +import {BeakerxDataGrid} from "../BeakerxDataGrid"; +import {DataGridScope} from "../DataGridScope"; + +export default class DataGridContextMenu extends BkoContextMenu { + constructor(scope: DataGridScope) { + super({ ...scope, element: [scope.dataGrid.node] }); + } + + protected buildMenu(): void { + this.inLab ? this.buildLabMenu() : this.buildBkoMenu(); + + const menuItems = createHeaderContextMenuItems(this.scope.dataGrid, this); + const cellMenuItems = createCellContextMenuItems(this.scope.dataGrid, this); + this.createItems([ ...menuItems, ...cellMenuItems ], this.contextMenu); + this.bindEvents(); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems.ts b/js/notebook/src/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems.ts new file mode 100644 index 0000000000..82746715e5 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems.ts @@ -0,0 +1,103 @@ +/* + * 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 {BeakerxDataGrid} from "../BeakerxDataGrid"; +import DataGridContextMenu from "./DataGridContextMenu"; +import {selectContextMenuItems, selectContextMenuTags} from "../model/selectors"; +import {selectColumnIndexByPosition} from "../column/selectors"; + +export default function createCellContextMenuItems( + dataGrid: BeakerxDataGrid, + contextMenu: DataGridContextMenu +): MenuItem[] { + const selector = `#${dataGrid.wrapperId} canvas`; + const contextMenuItems = selectContextMenuItems(dataGrid.store.state); + const contextMenuTags = selectContextMenuTags(dataGrid.store.state); + const isVisible = () => { + const data = dataGrid.getCellData(contextMenu.event.clientX, contextMenu.event.clientY); + + if (!data || data.offsetTop < dataGrid.headerHeight) { + return false; + } + + return true; + }; + + function createFromModelContextMenuItems(): MenuItem[] { + return contextMenuItems.map((item: string) => ({ + selector, + isVisible, + id: `${item}_${dataGrid.wrapperId}`, + title: item, + action: (event) => { + const data = dataGrid.getCellData(event.clientX, event.clientY); + + if (!data) { + return; + } + + dataGrid.commSignal.emit({ + event: 'CONTEXT_MENU_CLICK', + itemKey : item, + row : data.row, + column : selectColumnIndexByPosition(dataGrid.store.state, data.type, data.column) + }); + } + })); + } + + function createFromModelContextMenuTags(): MenuItem[] { + const items: MenuItem[] = []; + + Object.keys(contextMenuTags).forEach((name) => { + let tag = contextMenuTags[name]; + + items.push({ + selector, + isVisible, + id: `${tag}_${dataGrid.wrapperId}`, + title: name, + action: function(event) { + const data = dataGrid.getCellData(event.clientX, event.clientY); + + if (!data) { + return; + } + + const params = { + actionType: 'CONTEXT_MENU_CLICK', + contextMenuItem: name, + row: data.row, + col: selectColumnIndexByPosition(dataGrid.store.state, data.type, data.column) + }; + + dataGrid.commSignal.emit({ + params, + event: 'actiondetails' + }); + } + }); + }); + + return items; + } + + return [ + ...createFromModelContextMenuItems(), + ...createFromModelContextMenuTags() + ] +} diff --git a/js/notebook/src/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems.ts b/js/notebook/src/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems.ts new file mode 100644 index 0000000000..0c6c9b7f85 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems.ts @@ -0,0 +1,58 @@ +/* + * 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 {BeakerxDataGrid} from "../BeakerxDataGrid"; +import DataGridContextMenu from "./DataGridContextMenu"; +import {selectHeadersVertical} from "../model/selectors"; + +export default function createHeaderContextMenuItems( + dataGrid: BeakerxDataGrid, + contextMenu: DataGridContextMenu +): MenuItem[] { + const selector = `#${dataGrid.wrapperId} canvas`; + + const rotateMenuItemAction = (headersVertical: boolean) => (event: MouseEvent) => { + dataGrid.model.setHeaderTextVertical(headersVertical); + dataGrid.resize(); + }; + + const isVisible = (headersVertical: boolean) => { + const data = dataGrid.getCellData(contextMenu.event.clientX, contextMenu.event.clientY); + + if (!data || data.offsetTop >= dataGrid.headerHeight) { + return false; + } + + return headersVertical; + }; + + return [ + { + id: `${dataGrid.wrapperId}_verticalHeaders`, + title: 'vertical headers', + action: rotateMenuItemAction(true), + isVisible: () => isVisible(!selectHeadersVertical(dataGrid.store.state)), + selector + }, { + id: `${dataGrid.wrapperId}_horizontalHeaders`, + title: 'horizontal headers', + action: rotateMenuItemAction(false), + isVisible: () => isVisible(!!selectHeadersVertical(dataGrid.store.state)), + selector + } + ] +} diff --git a/js/notebook/src/tableDisplay/dataGrid/dataGridHelpers.ts b/js/notebook/src/tableDisplay/dataGrid/dataGridHelpers.ts new file mode 100644 index 0000000000..2b44840e53 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/dataGridHelpers.ts @@ -0,0 +1,137 @@ +/* + * 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 {SectionList} from "@phosphor/datagrid/lib/sectionlist"; +import {DEFAULT_DATA_FONT_SIZE} from "./style/dataGridStyle"; + +export namespace DataGridHelpers { + const htmlCharactersReplacementMap = { + '"': '"', + '&': '&', + '\'': ''', + '/': '/', + '<': '<', + '>': '>' + }; + + export function escapeHTML(text: any): any { + if (typeof text === 'string') { + return text.replace( + /[\'&'\/<>]/g, + (a) => htmlCharactersReplacementMap[a] + ); + } + + return text; + } + + export function truncateString(text, limit = 1000): string { + if (text && text.length > limit) { + text = text.substring(0, limit); + text += '...'; + } + + return text; + } + + export function disableKeyboardManager() { + try { + Jupyter.keyboard_manager.enabled = false; + } catch (e) {} + } + + export function enableKeyboardManager() { + try { + Jupyter.keyboard_manager.enabled = true; + } catch (e) {} + } + + export function getStringSize(value: string, fontSize: Number|null|undefined) { + let spanEl: HTMLSpanElement = document.createElement('span'); + let width: number; + let height: number; + + spanEl.textContent = value; + spanEl.style.fontFamily = 'Lato, Helvetica, sans-serif'; + spanEl.style.fontSize = `${fontSize || DEFAULT_DATA_FONT_SIZE}px`; + spanEl.style.padding = '5px'; + spanEl.style.position = 'absolute'; + document.body.appendChild(spanEl); + + width = spanEl.clientWidth; + height = spanEl.clientHeight; + document.body.removeChild(spanEl); + + return { width, height }; + } + + export function findSectionIndex( + list: SectionList, + cursorPosition: number + ): { index: number, delta: number } | null { + // Bail early if the list is empty or the position is invalid. + if (list.sectionCount === 0 || cursorPosition < 0) { + return null; + } + + // Compute the delta from the end of the list. + let delta = cursorPosition - (list.totalSize - 1); + if (delta > 0) { + return null; + } + + // Test whether the hover is just past the last section. + let index = list.sectionCount - 1; + if (delta >= -list.sectionSize(index)) { + return { index, delta }; + } + + index = list.sectionIndex(cursorPosition); + delta = cursorPosition - (list.sectionOffset(index) - 1); + + if (index >= 0) { + return { index, delta }; + } + + return null; + } + + export function throttle(func: Function, limit: number): (T) => U|undefined { + let lastFunc; + let lastRan; + + return function(...args: T[]): U|undefined { + const context = this; + + if (!lastRan) { + func.apply(context, args); + lastRan = Date.now(); + + return; + } + + clearTimeout(lastFunc); + lastFunc = setTimeout(() => { + if ((Date.now() - lastRan) < limit) { + return; + } + + func.apply(context, args); + lastRan = Date.now(); + }, limit - (Date.now() - lastRan)) + } + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/dataTypes.ts b/js/notebook/src/tableDisplay/dataGrid/dataTypes.ts new file mode 100644 index 0000000000..ac44fb856b --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/dataTypes.ts @@ -0,0 +1,96 @@ +/* + * 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 consts from "../consts"; + +export enum ALL_TYPES { + 'string', + 'integer', + 'formatted integer', + 'double', + 'double with precision', + 'exponential 5' = 6, + 'exponential 15', + 'datetime', + 'boolean', + 'html', + 'int64', + 'time' +} + +const DEFAULT_DOUBLE_WITH_PRECISION_TYPE = '4.3'; + +export const getTypeByName = (typeName: string): number => { + return ALL_TYPES[typeName] || 0; +}; + +export function getDisplayType(type: ALL_TYPES, stringFormatForType?: any, stringFormatForColumn?: any) { + if (type === ALL_TYPES.datetime || type === ALL_TYPES.time) { + return ALL_TYPES.datetime; + } + + if (type === ALL_TYPES.integer) { + return ALL_TYPES['formatted integer']; + } + + if (type === ALL_TYPES.double) { + if (stringFormatForType && stringFormatForType.double || stringFormatForColumn) { + return ALL_TYPES.double; + } + + return DEFAULT_DOUBLE_WITH_PRECISION_TYPE; + } + + return ALL_TYPES.string; +} + +export function isDoubleWithPrecision(type: string|number) { + let parts = type.toString().split("."); + + return parts.length > 1 && parts[0] === '4'; +} + +export function getDoublePrecisionByType(type: string|number): string { + return type.toString().split(".")[1]; +} + +export function getAllowedTypesByType(type) { + if (type === undefined) { + return consts.scopeData.allTypes; + } + + if (type === ALL_TYPES.string) { + return consts.scopeData.allStringTypes; + } + + if (type === ALL_TYPES.double) { + return consts.scopeData.allDoubleTypes; + } + + if (type === ALL_TYPES.integer || type === ALL_TYPES.int64) { + return consts.scopeData.allIntTypes; + } + + if (type === ALL_TYPES.time || type === ALL_TYPES.datetime) { + return consts.scopeData.allTimeTypes; + } + + if (type === ALL_TYPES.boolean) { + return consts.scopeData.allBoolTypes; + } + + return consts.scopeData.allStringTypes; +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/BkoMenu.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/BkoMenu.ts new file mode 100644 index 0000000000..724b29842d --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/BkoMenu.ts @@ -0,0 +1,80 @@ +/* + * 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 { Menu } from '@phosphor/widgets' +import { Message } from '@phosphor/messaging' + +export default class BkoMenu extends Menu { + keepOpen: boolean|undefined; + trigger: HTMLElement; + + triggerActiveItem(): void { + if (!this.keepOpen) { + super.triggerActiveItem(); + return; + } + + if (!this.isAttached) { + return; + } + + const item = this.activeItem; + if (!item) { + return; + } + + const command = item.command, args = item.args; + if (this.commands.isEnabled(command, args)) { + this.commands.execute(command, args); + } + } + + protected onBeforeAttach(msg: Message): void { + super.onBeforeAttach(msg); + + if (this.parentMenu && this.parentMenu.activeItem && this.parentMenu.activeItem.type === 'submenu') { + this.hide(); + } + } + + protected onActivateRequest(msg: Message) { + super.onActivateRequest(msg); + + if (!this.parentMenu || !this.parentMenu.activeItem || this.parentMenu.activeItem.type !== 'submenu') { + return; + } + + const itemNode = this.parentMenu.contentNode.children[this.parentMenu.activeIndex]; + const itemOffset = itemNode.getBoundingClientRect().top; + this.node.style.top = `${itemOffset}px`; + + this.show(); + const rect = this.node.getBoundingClientRect(); + const clientHeight = window.innerHeight || document.documentElement.clientHeight; + + if (rect.bottom > clientHeight) { + this.node.style.top = `${itemOffset - (rect.bottom - clientHeight)}px`; + } + } + + close() { + super.close.call(this); + + setTimeout(() => { + this.trigger && this.trigger.classList.remove('opened'); + }); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/ColumnMenu.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/ColumnMenu.ts new file mode 100644 index 0000000000..73162770aa --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/ColumnMenu.ts @@ -0,0 +1,41 @@ +/* + * 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 { createColumnMenuItems } from './createColumnMenuItems'; +import HeaderMenu, { ITriggerOptions } from "./HeaderMenu"; +import DataGridColumn from "../column/DataGridColumn"; + +export default class ColumnMenu extends HeaderMenu { + + constructor( + column: DataGridColumn, + triggerOptions: ITriggerOptions, + ) { + super(column, triggerOptions); + } + + protected buildMenu(): void { + this.menu.addClass('bko-table-menu'); + this.menu.addClass('dropdown'); + + this.menu.contentNode.classList.add('dropdown-menu'); + this.menu.contentNode.classList.add('bko-table-menu-content'); + + let items = createColumnMenuItems(this.column); + + this.createItems(items, this.menu); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/HeaderMenu.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/HeaderMenu.ts new file mode 100644 index 0000000000..9bcb4bbb27 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/HeaderMenu.ts @@ -0,0 +1,315 @@ +/* + * 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 { CommandRegistry } from '@phosphor/commands'; +import { Widget } from '@phosphor/widgets'; +import { BeakerxDataGrid } from "../BeakerxDataGrid"; +import Menu from './BkoMenu'; +import MenuItem from '../../../shared/interfaces/menuItemInterface'; +import MenuInterface from '../../../shared/interfaces/menuInterface'; +import DataGridColumn from "../column/DataGridColumn"; +import {selectColumnPosition} from "../column/selectors"; +import {SORT_ORDER} from "../column/enums"; + +export interface ITriggerOptions { + x: number, + y: number, + width: number, + height: number +} + +export default abstract class HeaderMenu implements MenuInterface { + columnIndex: number; + + protected commands: CommandRegistry; + protected menu: Menu; + protected viewport: Widget; + protected triggerNode: HTMLElement; + protected dataGrid: BeakerxDataGrid; + protected column: DataGridColumn; + + private TRIGGER_CLASS_OPENED: string = 'opened'; + private TRIGGER_CLASS_SORTING_DESC: string = 'sorting_desc'; + private TRIGGER_CLASS_SORTING_ASC: string = 'sorting_asc'; + + static DEFAULT_TRIGGER_HEIGHT: number = 20; + + constructor(column: DataGridColumn, triggerOptions: ITriggerOptions) { + this.commands = new CommandRegistry(); + this.menu = new Menu({ commands: this.commands }); + this.viewport = column.dataGrid.viewport; + this.columnIndex = column.index; + this.dataGrid = column.dataGrid; + this.column = column; + + this.handleKeydownEvent = this.handleKeydownEvent.bind(this); + + this.addTrigger(triggerOptions); + this.buildMenu(); + this.attachTriggerToMenu(); + } + + protected abstract buildMenu(): void + + updateTriggerPosition() { + this.triggerNode.style.left = `${this.dataGrid.getColumnOffset( + selectColumnPosition(this.dataGrid.store.state, this.column), + this.column.type + )}px`; + } + + showTrigger(): void { + this.updateTriggerPosition(); + this.assignTriggerSortingCssClass(); + + if (this.triggerNode.style.visibility === 'visible') { + return; + } + + this.triggerNode.style.visibility = 'visible'; + this.viewport.node.appendChild(this.triggerNode); + } + + hideTrigger() { + if (this.column.getSortOrder() !== SORT_ORDER.NO_SORT && this.column.getVisible() || this.column.getKeepTrigger()) { + return; + } + + this.triggerNode.style.visibility = 'hidden'; + } + + attachTriggerToMenu() { + this.menu.trigger = this.triggerNode; + this.column.getKeepTrigger() && this.showTrigger(); + } + + open(submenuIndex?: number): void { + const menuPosition = this.getMenuPosition(this.triggerNode); + + this.menu.addClass('open'); + this.menu.open(menuPosition.left, menuPosition.top); + this.correctPosition(this.triggerNode); + + this.triggerNode.classList.add(this.TRIGGER_CLASS_OPENED); + + if (submenuIndex !== undefined) { + let item = this.menu.items[submenuIndex]; + if (item.type === 'submenu') { + this.menu.activeIndex = submenuIndex; + this.menu.triggerActiveItem(); + } + } + } + + destroy(): void { + this.menu.isAttached && this.menu.dispose(); + } + + toggleMenu(submenuIndex?: number): void { + this.triggerNode.classList.contains(this.TRIGGER_CLASS_OPENED) ? + this.triggerNode.classList.remove(this.TRIGGER_CLASS_OPENED) : + this.open(submenuIndex); + } + + createItems(items: MenuItem[], menu: Menu): void { + for (let i = 0, ien = items.length; i < ien; i++) { + let menuItem = items[i]; + + const subitems = (typeof menuItem.items == 'function') ? menuItem.items(this.column) : menuItem.items; + const hasSubitems = Array.isArray(subitems) && subitems.length; + + menuItem.separator && menu.addItem({ type: 'separator' }); + + if (!hasSubitems) { + this.addCommand(menuItem, menu); + menu.addItem({command: menuItem.title}); + + continue; + } + + menu.addItem({ type: 'submenu', submenu: this.createSubmenu(menuItem, subitems) }); + } + } + + addCommand(menuItem: MenuItem, menu: Menu): void { + this.commands.addCommand(menuItem.title, { + label: menuItem.title, + usage: menuItem.tooltip || '', + iconClass: () => { + if (menuItem.icon) { + return menuItem.icon; + } + + if (typeof menuItem.isChecked == 'function' && menuItem.isChecked(this.column)) { + return 'fa fa-check'; + } + + return ''; + }, + execute: (): void => { + if (menuItem.action && typeof menuItem.action == 'function') { + menuItem.action(this.column); + menuItem.updateLayout && menu.update(); + } + } + }); + + if (menuItem.shortcut) { + this.commands.addKeyBinding({ + keys: [menuItem.shortcut], + selector: 'body', + command: menuItem.title + }); + } + } + + createSubmenu(menuItem: MenuItem, subitems: MenuItem[]): Menu { + 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); + submenu.keepOpen = menuItem.keepOpen; + + submenu.setHidden(false); + + this.createItems(subitems, submenu); + + return submenu; + } + + protected assignTriggerSortingCssClass() { + const sortOrder = this.column.getSortOrder(); + + if (sortOrder === SORT_ORDER.ASC) { + this.triggerNode.classList.remove(this.TRIGGER_CLASS_SORTING_DESC); + this.triggerNode.classList.add(this.TRIGGER_CLASS_SORTING_ASC); + + return; + } + + if (sortOrder === SORT_ORDER.DESC) { + this.triggerNode.classList.remove(this.TRIGGER_CLASS_SORTING_ASC); + this.triggerNode.classList.add(this.TRIGGER_CLASS_SORTING_DESC); + + return; + } + + this.triggerNode.classList.remove(this.TRIGGER_CLASS_SORTING_ASC); + this.triggerNode.classList.remove(this.TRIGGER_CLASS_SORTING_DESC); + } + + protected addTrigger({ + x, y, + width = HeaderMenu.DEFAULT_TRIGGER_HEIGHT, + height = HeaderMenu.DEFAULT_TRIGGER_HEIGHT + }):void { + this.triggerNode = document.createElement('span'); + + this.triggerNode.style.height = `${height}px`; + this.triggerNode.style.width = `${width}px`; + this.triggerNode.style.position = 'absolute'; + this.triggerNode.style.left = `${x}px`; + this.triggerNode.style.top = `${y}px`; + this.triggerNode.style.cursor = 'pointer'; + this.triggerNode.classList.add('bko-column-header-menu'); + this.triggerNode.classList.add('bko-menu'); + this.triggerNode.addEventListener('mousedown', (event) => { + event.preventDefault(); + event.stopPropagation(); + + this.toggleMenu(); + }); + } + + protected getMenuPosition(trigger: any) { + const triggerHeight = trigger.height || 20; + const viewportRect = this.viewport.node.getBoundingClientRect(); + + return { + top: viewportRect.top + trigger.offsetTop + triggerHeight, + left: viewportRect.left + trigger.offsetLeft + }; + } + + protected correctPosition(trigger: any) { + const menuRectObject = this.menu.node.getBoundingClientRect(); + const triggerRectObject = trigger.getBoundingClientRect(); + + if (menuRectObject.top < triggerRectObject.bottom && menuRectObject.left <= triggerRectObject.right) { + this.menu.node.style.left = triggerRectObject.right + 'px'; + } + } + + protected handleKeydownEvent(event: KeyboardEvent): void { + this.menu.isVisible && this.menu.handleEvent(event); + } + + protected addItemsFiltering(menu: Menu): void { + const filterWrapper = document.createElement('div'); + + filterWrapper.classList.add('dropdown-menu-search'); + filterWrapper.innerHTML = ''; + + menu.node.insertBefore(filterWrapper, menu.node.children.item(0)); + + const input = filterWrapper.querySelector('input'); + + if (!input) { + return; + } + + menu.node.addEventListener('mouseup', (event: MouseEvent) => { + if (event.target !== input) { + return; + } + + input.focus(); + event.stopImmediatePropagation(); + }); + + input.addEventListener('keydown', (event: KeyboardEvent) => { + event.stopImmediatePropagation(); + }); + + input.addEventListener('keyup', (event: KeyboardEvent) => { + event.stopImmediatePropagation(); + + if (event.keyCode === 27) { + menu.close(); + + return; + } + + this.hideMenuItems(menu, input.value); + }); + } + + protected hideMenuItems(menu: Menu, filterValue: string): void { + const searchExp = filterValue ? new RegExp(filterValue, 'i') : null; + const items = menu.contentNode.querySelectorAll('.p-Menu-item'); + + for (let i = 0; i < items.length; i++) { + let item = items.item(i); + let itemClassList = item.classList; + let shouldHide = searchExp && searchExp.test ? !searchExp.test(item.innerText) : false; + + shouldHide ? itemClassList.add('hidden') : itemClassList.remove('hidden'); + } + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/IndexMenu.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/IndexMenu.ts new file mode 100644 index 0000000000..6a569f5c02 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/IndexMenu.ts @@ -0,0 +1,42 @@ +/* + * 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 { createIndexMenuItems } from './createIndexMenuItems'; +import HeaderMenu, { ITriggerOptions } from './HeaderMenu'; +import DataGridColumn from "../column/DataGridColumn"; + +export default class IndexMenu extends HeaderMenu { + + constructor( + column: DataGridColumn, + triggerOptions: ITriggerOptions + ) { + super(column, triggerOptions); + } + + protected buildMenu(): void { + this.menu.addClass('bko-header-menu'); + this.menu.addClass('bko-table-menu'); + this.menu.addClass('dropdown'); + + this.menu.contentNode.classList.add('dropdown-menu'); + this.menu.contentNode.classList.add('bko-table-menu-content'); + + let items = createIndexMenuItems(this.column); + + this.createItems(items, this.menu); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/createColumnMenuItems.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/createColumnMenuItems.ts new file mode 100644 index 0000000000..9de4baf88b --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/createColumnMenuItems.ts @@ -0,0 +1,124 @@ +/* + * 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 { createFormatMenuItems } from './createFormatMenuItems'; +import MenuItem from "../../../shared/interfaces/menuItemInterface"; +import DataGridColumn from "../column/DataGridColumn"; +import { CENTER, LEFT, RIGHT } from "../column/columnAlignment"; +import {HIGHLIGHTER_TYPE} from "../interface/IHighlighterState"; +import {selectBodyColumnVisibility} from "../column/selectors"; +import {SORT_ORDER} from "../column/enums"; + +export function createColumnMenuItems(column: DataGridColumn): MenuItem[] { + return [ + { + title: 'Hide column', + action: (column) => column.hide() + }, + { + title: 'Filter by Expression', + icon: 'fa fa-filter', + tooltip: 'filter with an expression with a variable defined for each column and $ means the current column. eg "$ > 5"', + action: (column) => column.columnManager.showFilters(column) + }, + { + title: 'Search for Substring', + icon: 'fa fa-search', + tooltip: 'search this column for a substring', + action: (column) => column.columnManager.showSearch(column) + }, + { + title: 'Format', + action: undefined, + items: createFormatMenuItems(column) + }, + { + title: 'Sort Ascending', + separator: true, + isChecked: (column) => column.getSortOrder() === SORT_ORDER.ASC, + action: (column) => column.sort(SORT_ORDER.ASC) + }, + { + title: 'Sort Descending', + isChecked: (column) => column.getSortOrder() === SORT_ORDER.DESC, + action: (column) => column.sort(SORT_ORDER.DESC) + }, + { + title: 'No Sort', + isChecked: (column) => column.getSortOrder() === SORT_ORDER.NO_SORT, + action: (column) => column.sort(SORT_ORDER.NO_SORT) + }, + { + title: 'Align Left', + separator: true, + isChecked: (column) => column.getAlignment() === LEFT, + action: (column) => { column.setAlignment(LEFT) } + }, + { + title: 'Align Center', + isChecked: (column) => column.getAlignment() === CENTER, + action: (column) => { column.setAlignment(CENTER) } + }, + { + title: 'Align Right', + isChecked: (column) => column.getAlignment() === RIGHT, + action: (column) => { column.setAlignment(RIGHT) } + }, + { + title: 'Heatmap', + shortcut: 'H', + separator: true, + isChecked: (column) => column.getHighlighter(HIGHLIGHTER_TYPE.heatmap).length, + action: (column) => column.toggleHighlighter(HIGHLIGHTER_TYPE.heatmap) + }, + { + title: 'Data Bars', + shortcut: 'B', + isChecked: (column) => !!column.getRenderer(), + action: (column) => column.toggleDataBarsRenderer() + }, + { + title: 'Color by unique', + shortcut: 'U', + isChecked: (column) => column.getHighlighter(HIGHLIGHTER_TYPE.uniqueEntries).length, + action: (column) => column.toggleHighlighter(HIGHLIGHTER_TYPE.uniqueEntries) + }, + { + title: 'Fix Left', + isChecked: (column) => {}, + action: (column) => {} + }, + { + title: 'Move column to front', + separator: true, + action: (column) => column.move(0) + }, + { + title: 'Move column to end', + action: (column) => { + let position = selectBodyColumnVisibility(column.dataGrid.store.state) + .filter(visible => visible).length - 1; + + column.move(position); + } + }, + { + title: 'Reset formatting', + separator: true, + action: (column) => column.resetState() + } + ]; +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/createFormatMenuItems.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/createFormatMenuItems.ts new file mode 100644 index 0000000000..1cb6cf0693 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/createFormatMenuItems.ts @@ -0,0 +1,86 @@ +/* + * 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/menuItemInterface"; +import { TIME_UNIT_FORMATS, scopeData } from '../../consts'; +import { ALL_TYPES, getAllowedTypesByType } from "../dataTypes"; +import DataGridColumn from "../column/DataGridColumn"; + +export function createFormatMenuItems(column: DataGridColumn) { + const types = getAllowedTypesByType(column.getDataType()); + let items: MenuItem[] = []; + + types.forEach((obj) => { + if (obj.type === 8) { //datetime + items = items.concat(createTimeSubitems()); + + return; + } + + let item: MenuItem = { + title: obj.name, + isChecked: (column) => column.getDisplayType() === obj.type + }; + + if (obj.type === 4) { //double with precision + item.items = createPrecisionSubitems(column); + } else { + item.action = (column) => column.setDisplayType(obj.type) + } + items.push(item); + }); + + return items; +} + +export function createPrecisionSubitems(column: DataGridColumn): MenuItem[] { + const items: MenuItem[] = []; + + scopeData.allPrecissions.forEach((precision) => { + let item = { + title: `${precision}`, + isChecked: (column) => `4.${precision}` === column.getDisplayType(), + action: (column) => column.setDisplayType(`4.${precision}`) + }; + + items.push(item); + }); + + return items; +} + +export function createTimeSubitems(): MenuItem[] { + const items: MenuItem[] = []; + + Object.keys(TIME_UNIT_FORMATS).forEach((key) => { + let item = { + title: TIME_UNIT_FORMATS[key].title, + isChecked: (column) => { + const displayType = column.getDisplayType(); + + return ( + displayType === ALL_TYPES.datetime || + displayType === ALL_TYPES.time + ) && TIME_UNIT_FORMATS[key].format === column.getFormatForTimes().format + }, + action: (column) => column.setTimeDisplayType(TIME_UNIT_FORMATS[key]) + }; + + items.push(item); + }); + + return items; +} diff --git a/js/notebook/src/tableDisplay/dataGrid/headerMenu/createIndexMenuItems.ts b/js/notebook/src/tableDisplay/dataGrid/headerMenu/createIndexMenuItems.ts new file mode 100644 index 0000000000..fbf19d32c6 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/headerMenu/createIndexMenuItems.ts @@ -0,0 +1,126 @@ +/* + * 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/menuItemInterface'; +import { createFormatMenuItems } from './createFormatMenuItems'; +import DataGridColumn from "../column/DataGridColumn"; +import { selectBodyColumnStates } from "../column/selectors"; +import {COLUMN_TYPES} from "../column/enums"; + +export function createIndexMenuItems(column: DataGridColumn): MenuItem[] { + + const dataGrid = column.dataGrid; + const createShowColumnSubmenu = (): MenuItem[] => { + const items: MenuItem[] = []; + const columnsStates = selectBodyColumnStates(dataGrid.store.state); + + columnsStates.forEach((state) => { + items.push({ + title: state.name, + isChecked: () => { + let column = dataGrid.columnManager.getColumnByName(state.name); + + return column && column.getVisible(); + }, + action: () => { + let column = dataGrid.columnManager.getColumnByName(state.name); + + if (!column) { return; } + + column.getVisible() ? column.hide() : column.show(); + }, + updateLayout: true + }); + }); + + return items; + }; + + return [ + { + title: 'Show All Columns', + action: () => dataGrid.columnManager.showAllColumns() + }, + { + title: 'Show Column', + enableItemsFiltering: true, + keepOpen: true, + items: createShowColumnSubmenu + }, + { + title: 'Hide All Columns', + action: () => { + dataGrid.columnManager.columns[COLUMN_TYPES.body].forEach((column) => { + column.hide(); + }); + } + }, + { + title: 'Format', + separator: true, + items: createFormatMenuItems(column) + }, + { + title: 'Clear selection', + action: () => dataGrid.cellSelectionManager.clear() + }, + { + title: 'Copy to Clipboard', + separator: true, + action: () => dataGrid.cellManager.copyToClipboard() + }, + { + title: 'Download All as CSV', + action: () => dataGrid.cellManager.CSVDownload(false) + }, + { + title: 'Download Selected as CSV', + action: () => dataGrid.cellManager.CSVDownload(true) + }, + { + title: 'Search for Substring', + icon: 'fa fa-search', + tooltip: 'search the whole table for a substring', + separator: true, + action: () => dataGrid.columnManager.showSearch() + }, + { + title: 'Filter by Expression', + icon: 'fa fa-filter', + tooltip: 'filter with an expression with a variable defined for each column', + separator: true, + action: () => dataGrid.columnManager.showFilters() + }, + { + title: 'Hide Filter', + action: () => dataGrid.columnManager.resetFilters() + }, + { + title: 'Reset All Interactions', + separator: true, + action: () => { + dataGrid.highlighterManager.removeHighlighters(); + dataGrid.cellSelectionManager.clear(); + dataGrid.rowManager.resetSorting(); + dataGrid.columnManager.resetFilters(); + dataGrid.columnManager.showAllColumns(); + dataGrid.columnManager.resetColumnsAlignment(); + dataGrid.columnManager.resetColumnsOrder(); + dataGrid.setInitialSize(); + } + } + ] +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/HeatmapHighlighter.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/HeatmapHighlighter.ts new file mode 100644 index 0000000000..d8945e430a --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/HeatmapHighlighter.ts @@ -0,0 +1,41 @@ +/* + * 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 * as d3scale from 'd3-scale'; +import { formatColor, getDefaultColor } from "../style/dataGridStyle"; +import Highlighter from "./Highlighter"; +import IHihglighterState from "../interface/IHighlighterState"; +import DataGridColumn from "../column/DataGridColumn"; +import {CellRenderer} from "@phosphor/datagrid"; + +export default class HeatmapHighlighter extends Highlighter { + colorScale: Function; + + constructor(column: DataGridColumn, state: IHihglighterState) { + super(column, state); + + this.state.minColor = formatColor(state.minColor || getDefaultColor('blue')); + this.state.maxColor = formatColor(state.maxColor || getDefaultColor('red')); + + this.colorScale = d3scale.scaleLinear() + .domain([this.state.minVal, this.state.maxVal]) + .range([this.state.minColor, this.state.maxColor]); + } + + getBackgroundColor(config: CellRenderer.ICellConfig) { + return this.colorScale(this.getValueToHighlight(config)); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/Highlighter.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/Highlighter.ts new file mode 100644 index 0000000000..9dc2cecc84 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/Highlighter.ts @@ -0,0 +1,54 @@ +/* + * 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 IHihglighterState, { HIGHLIGHTER_STYLE } from "../interface/IHighlighterState"; +import { CellRenderer } from "@phosphor/datagrid"; +import DataGridColumn from "../column/DataGridColumn"; +import { find } from "@phosphor/algorithm"; +import {DEFAULT_CELL_BACKGROUND} from "../style/dataGridStyle"; +import {BeakerxDataGridModel} from "../model/BeakerxDataGridModel"; + +export default class Highlighter { + column: DataGridColumn; + model: BeakerxDataGridModel; + state: IHihglighterState; + + constructor(column: DataGridColumn, state: IHihglighterState) { + const valueResolver = column.getValueResolver(); + + this.column = column; + this.model = column.dataGrid.model; + this.state = { ...state }; + this.state.style = state.style || HIGHLIGHTER_STYLE.SINGLE_COLUMN; + this.state.minVal = valueResolver(this.state.minVal || this.column.minValue); + this.state.maxVal = valueResolver(this.state.maxVal || this.column.maxValue); + } + + getBackgroundColor(config: CellRenderer.ICellConfig) { + return DEFAULT_CELL_BACKGROUND; + } + + getValueToHighlight(config: CellRenderer.ICellConfig) { + let value = config.value; + let valueResolver = this.column.getValueResolver(); + + if (this.state.style === HIGHLIGHTER_STYLE.FULL_ROW) { + value = this.model.rowManager.getValueByColumn(config.row, this.column.index, this.column.type); + } + + return valueResolver(value); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/HighlighterFactory.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/HighlighterFactory.ts new file mode 100644 index 0000000000..54682706ab --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/HighlighterFactory.ts @@ -0,0 +1,56 @@ +/* + * 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 IHihglighterState, { + HIGHLIGHTER_STYLE, + HIGHLIGHTER_TYPE +} from "../interface/IHighlighterState"; +import HeatmapHighlighter from "./HeatmapHighlighter"; +import DataGridColumn from "../column/DataGridColumn"; +import ThreeColorHeatmapHighlighter from "./ThreeColorHeatmapHighlighter"; +import UniqueEntriesHighlighter from "./UniqueEntriesHighlighter"; +import ValueHighlighter from "./ValueHighlighter"; +import SortHighlighter from "./SortHighlighter"; + +export default class HighlighterFactory { + static defaultHighlighterState: IHihglighterState = { + style: HIGHLIGHTER_STYLE.SINGLE_COLUMN, + type: HIGHLIGHTER_TYPE.heatmap, + colName: '', + maxColor: null, + maxVal: null, + minColor: null, + minVal: null, + midColor: null, + midVal: null, + colors: null, + }; + + static getHighlighter(config: IHihglighterState, column: DataGridColumn) { + switch (config.type) { + case HIGHLIGHTER_TYPE.heatmap: + return new HeatmapHighlighter(column, config); + case HIGHLIGHTER_TYPE.threeColorHeatmap: + return new ThreeColorHeatmapHighlighter(column, config); + case HIGHLIGHTER_TYPE.uniqueEntries: + return new UniqueEntriesHighlighter(column, config); + case HIGHLIGHTER_TYPE.value: + return new ValueHighlighter(column, config); + case HIGHLIGHTER_TYPE.sort: + return new SortHighlighter(column, config); + } + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/HighlighterManager.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/HighlighterManager.ts new file mode 100644 index 0000000000..25ece27396 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/HighlighterManager.ts @@ -0,0 +1,137 @@ +/* + * 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 IHihglighterState, { + HIGHLIGHTER_STYLE, + HIGHLIGHTER_TYPE +} from "../interface/IHighlighterState"; +import Highlighter from "./Highlighter"; +import HighlighterFactory from "./HighlighterFactory"; +import { BeakerxDataGrid } from "../BeakerxDataGrid"; +import { each, iter, filter, toArray } from "@phosphor/algorithm"; +import { CellRenderer } from "@phosphor/datagrid"; +import {DEFAULT_CELL_BACKGROUND} from "../style/dataGridStyle"; + +export default class HighlighterManager { + highlightersState: IHihglighterState[]; + highlighters: Highlighter[]; + dataGrid: BeakerxDataGrid; + + constructor(dataGrid: BeakerxDataGrid, highlightersState: IHihglighterState[]) { + this.dataGrid = dataGrid; + this.highlightersState = [...highlightersState]; + this.highlighters = []; + + this.createHighlighter = this.createHighlighter.bind(this); + this.registerHighlighter = this.registerHighlighter.bind(this); + this.unregisterHighlighter = this.unregisterHighlighter.bind(this); + + this.createHighlighters(this.highlightersState); + } + + createHighlighters(state: IHihglighterState[]) { + state.forEach(this.createHighlighter); + } + + createHighlighter(state: IHihglighterState): void { + let column = this.dataGrid.getColumnByName(state.colName); + + if (!column) { + return; + } + + this.registerHighlighter(HighlighterFactory.getHighlighter(state, column)); + } + + registerHighlighter(highlighter: Highlighter|null) { + if (!highlighter) { + throw new Error(`Can not register highlighter: ${highlighter}`); + } + + if (highlighter.state.type === HIGHLIGHTER_TYPE.sort) { + this.highlighters.unshift(highlighter); + } else { + this.highlighters.push(highlighter); + } + } + + unregisterHighlighter(highlighter: Highlighter) { + const index = this.highlighters.indexOf(highlighter); + + index !== -1 && this.highlighters.splice(index, 1); + } + + getColumnHighlighters(column, highlighterType?: HIGHLIGHTER_TYPE): Highlighter[] { + return toArray(filter( + iter(this.highlighters), + (highlighter: Highlighter) => { + return highlighterType + ? highlighter.column === column && highlighter.state.type === highlighterType + : highlighter.column === column; + } + )); + } + + addColumnHighlighter(column, highlighterType: HIGHLIGHTER_TYPE) { + this.removeColumnHighlighter(column, highlighterType); + this.registerHighlighter(HighlighterFactory.getHighlighter({ + ...HighlighterFactory.defaultHighlighterState, + type: highlighterType, + style: HIGHLIGHTER_STYLE.SINGLE_COLUMN, + colName: column.name + }, column)); + } + + removeColumnHighlighter(column, highlighterType?: HIGHLIGHTER_TYPE) { + const highlighters = this.getColumnHighlighters(column, highlighterType); + + each(highlighters, this.unregisterHighlighter); + } + + toggleColumnHighlighter(column, highlighterType: HIGHLIGHTER_TYPE) { + if (this.getColumnHighlighters(column, highlighterType).length) { + this.removeColumnHighlighter(column, highlighterType); + } else { + this.addColumnHighlighter(column, highlighterType); + } + + this.dataGrid.repaint(); + } + + removeHighlighters() { + this.highlighters.splice(0, this.highlighters.length); + this.dataGrid.repaint(); + } + + getCellBackground(config: CellRenderer.ICellConfig): string { + let background = DEFAULT_CELL_BACKGROUND; + let column = this.dataGrid.getColumn(config); + + each( + iter(this.highlighters), + (highlighter) => { + if ( + highlighter.column === column || + highlighter.state.style === HIGHLIGHTER_STYLE.FULL_ROW + ) { + background = highlighter.getBackgroundColor(config); + } + } + ); + + return background; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/SortHighlighter.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/SortHighlighter.ts new file mode 100644 index 0000000000..e8354d6208 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/SortHighlighter.ts @@ -0,0 +1,35 @@ +/* + * 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 IHihglighterState, { HIGHLIGHTER_STYLE } from "../interface/IHighlighterState"; +import DataGridColumn from "../column/DataGridColumn"; +import Highlighter from "./Highlighter"; +import {CellRenderer} from "@phosphor/datagrid"; + +export default class SortHighlighter extends Highlighter { + oddRowColor = 'rgb(249, 249, 249)'; + evenRowColor = 'rgb(241, 241, 241)'; + + constructor(column: DataGridColumn, state: IHihglighterState) { + super(column, state); + + this.state.style = HIGHLIGHTER_STYLE.SINGLE_COLUMN; + } + + getBackgroundColor(config: CellRenderer.ICellConfig) { + return config.row % 2 === 0 ? this.evenRowColor : this.oddRowColor; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter.ts new file mode 100644 index 0000000000..07aa2b33d2 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter.ts @@ -0,0 +1,38 @@ +/* + * 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 * as d3scale from 'd3-scale'; +import { formatColor } from "../style/dataGridStyle"; +import IHihglighterState from "../interface/IHighlighterState"; +import DataGridColumn from "../column/DataGridColumn"; +import HeatmapHighlighter from "./HeatmapHighlighter"; + +export default class ThreeColorHeatmapHighlighter extends HeatmapHighlighter { + constructor(column: DataGridColumn, state: IHihglighterState) { + super(column, state); + + if (typeof this.state.minVal !== 'number' || typeof this.state.maxVal !== 'number' ) { + throw new Error('Min and Max values are not set'); + } + + this.state.midVal = column.getValueResolver()(this.state.midVal || (this.state.minVal + this.state.maxVal / 2)); + this.state.midColor = formatColor(state.midColor); + + this.colorScale = d3scale.scaleLinear() + .domain([this.state.minVal, this.state.midVal, this.state.maxVal]) + .range([this.state.minColor, this.state.midColor, this.state.maxColor]); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter.ts new file mode 100644 index 0000000000..399141a5cc --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter.ts @@ -0,0 +1,58 @@ +/* + * 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 Highlighter from "./Highlighter"; +import IHihglighterState from "../interface/IHighlighterState"; +import DataGridColumn from "../column/DataGridColumn"; +import { reduce, each } from "@phosphor/algorithm"; +import { CellRenderer } from "@phosphor/datagrid"; +import {DEFAULT_CELL_BACKGROUND} from "../style/dataGridStyle"; + +export default class UniqueEntriesHighlighter extends Highlighter { + uniqueValues: any[] = []; + uniqueColors = {}; + + constructor(column: DataGridColumn, state: IHihglighterState) { + super(column, state); + + this.generateUniqueValues(); + this.generateUniqueColors(); + } + + getBackgroundColor(config: CellRenderer.ICellConfig) { + return this.uniqueColors[this.getValueToHighlight(config)] || DEFAULT_CELL_BACKGROUND; + } + + generateColor(colorNum, colors) { + return "hsl(" + (colorNum * (360 / colors)) + ", 75%, 85%)"; + } + + generateUniqueValues() { + reduce( + this.model.getColumnValuesIterator(this.column), + (acc, value) => acc.indexOf(value) === -1 && acc.push(value) && acc || acc, + this.uniqueValues + ); + } + + generateUniqueColors() { + let valueResolver = this.column.getValueResolver(); + + each(this.uniqueValues, (value, index) => { + this.uniqueColors[valueResolver(value)] = this.generateColor(index, this.uniqueValues.length); + }); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/highlighter/ValueHighlighter.ts b/js/notebook/src/tableDisplay/dataGrid/highlighter/ValueHighlighter.ts new file mode 100644 index 0000000000..b1412184c0 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/highlighter/ValueHighlighter.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 IHihglighterState, { HIGHLIGHTER_STYLE } from "../interface/IHighlighterState"; +import DataGridColumn from "../column/DataGridColumn"; +import Highlighter from "./Highlighter"; +import {CellRenderer} from "@phosphor/datagrid"; +import {DEFAULT_CELL_BACKGROUND, formatColor} from "../style/dataGridStyle"; + +export default class ValueHighlighter extends Highlighter { + constructor(column: DataGridColumn, state: IHihglighterState) { + super(column, state); + + this.state.style = HIGHLIGHTER_STYLE.SINGLE_COLUMN; + this.state.colors = this.state.colors || []; + } + + getBackgroundColor(config: CellRenderer.ICellConfig) { + return this.state.colors && formatColor(this.state.colors[config.row]) || DEFAULT_CELL_BACKGROUND; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/index.ts b/js/notebook/src/tableDisplay/dataGrid/index.ts new file mode 100644 index 0000000000..d0121bbb46 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/index.ts @@ -0,0 +1,17 @@ +/* + * 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 * from './DataGridScope'; diff --git a/js/notebook/src/tableDisplay/dataGrid/interface/ICell.ts b/js/notebook/src/tableDisplay/dataGrid/interface/ICell.ts new file mode 100644 index 0000000000..1a3846d5a5 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/interface/ICell.ts @@ -0,0 +1,26 @@ +/* + * 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 {COLUMN_TYPES} from "../column/enums"; + +export interface ICellData { + type: COLUMN_TYPES, + column: number, + row: number, + delta: number, + offset: number, + offsetTop: number +} diff --git a/js/notebook/src/tableDisplay/dataGrid/interface/IColumn.ts b/js/notebook/src/tableDisplay/dataGrid/interface/IColumn.ts new file mode 100644 index 0000000000..e16c6d2994 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/interface/IColumn.ts @@ -0,0 +1,57 @@ +/* + * 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 { ITriggerOptions } from "../headerMenu/HeaderMenu"; +import DataGridColumn from "../column/DataGridColumn"; +import {TextRenderer} from "@phosphor/datagrid"; +import {ALL_TYPES} from "../dataTypes"; +import {COLUMN_TYPES, SORT_ORDER} from "../column/enums"; + +export interface IColumn { + index: number, + type: COLUMN_TYPES +} + +export interface IColumnOptions { + index: number, + name: string, + type: COLUMN_TYPES, + menuOptions: ITriggerOptions +} + +export interface IColumns { + [key: number]: DataGridColumn[] +} + +export type IColumnsState = Map; + +export interface IColumnState { + name: string, + index: number, + columnType: COLUMN_TYPES, + dataTypeName: string, + dataType: ALL_TYPES, + displayType: ALL_TYPES|string, + keepTrigger: boolean, + horizontalAlignment: TextRenderer.HorizontalAlignment, + formatForTimes: any, + visible: boolean, + sortOrder: SORT_ORDER, + filter: string|null, + position: number, + width?: number + renderer?: number +} diff --git a/js/notebook/src/tableDisplay/dataGrid/interface/IDataGridModelState.ts b/js/notebook/src/tableDisplay/dataGrid/interface/IDataGridModelState.ts new file mode 100644 index 0000000000..8adea4dd07 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/interface/IDataGridModelState.ts @@ -0,0 +1,50 @@ +/* + * 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 IHihglighterState from "./IHighlighterState"; + +export default interface IDataModelState { + alignmentForColumn?: {}, + alignmentForType?: {}, + cellHighlighters: IHihglighterState[], + columnNames: string[], + columnOrder: string[], + columnsFrozen?: {}, //@todo + columnsFrozenRight?: {}, //feature is dropped + columnsVisible: {}, + contextMenuItems?: string[], + contextMenuTags?: {}, + dataFontSize?: number|null, + doubleClickTag?: string|null, + fontColor?: string[], + hasDoubleClickAction?: boolean, + hasIndex: boolean, + headerFontSize?: number|null, + headersVertical?: boolean, + rendererForColumn?: {}, //@todo Needs the DataBars highlighter + rendererForType?: {}, //@todo Needs the DataBars highlighter + stringFormatForColumn: {}, + stringFormatForTimes?: string|null, + stringFormatForType?: {}, + subtype?: string, + timeZone?: string, + timeStrings?: any, + tooManyRows?: boolean, + tooltips: string[][], + type?: string, + types: string[], + values: any, +} diff --git a/js/notebook/src/tableDisplay/dataGrid/interface/IDataGridScopeOptions.ts b/js/notebook/src/tableDisplay/dataGrid/interface/IDataGridScopeOptions.ts new file mode 100644 index 0000000000..d3ed042d40 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/interface/IDataGridScopeOptions.ts @@ -0,0 +1,24 @@ +/* + * 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 { DataGrid } from "@phosphor/datagrid"; + +export default interface IDataGridScopeOptions extends DataGrid.IOptions { + element: HTMLElement + data: any, + widgetModel: any, + widgetView: any +} diff --git a/js/notebook/src/tableDisplay/dataGrid/interface/IHighlighterState.ts b/js/notebook/src/tableDisplay/dataGrid/interface/IHighlighterState.ts new file mode 100644 index 0000000000..8626e535b7 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/interface/IHighlighterState.ts @@ -0,0 +1,41 @@ +/* + * 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 enum HIGHLIGHTER_STYLE { + SINGLE_COLUMN = 'SINGLE_COLUMN', + FULL_ROW = 'FULL_ROW' +} + +export enum HIGHLIGHTER_TYPE { + heatmap = 'HeatmapHighlighter', + uniqueEntries = 'UniqueEntriesHighlighter', + threeColorHeatmap = 'ThreeColorHeatmapHighlighter', + value = 'ValueHighlighter', + sort = 'SortHighlighter' +} + +export default interface IHihglighterState { + colName: string, + maxColor: string|null, + maxVal: number|null, + minColor: string|null, + minVal: number|null, + midColor: string|null, + midVal: number|null, + style: HIGHLIGHTER_STYLE, + type: HIGHLIGHTER_TYPE, + colors: string[]|null +} diff --git a/js/notebook/src/tableDisplay/dataGrid/interface/IRenderer.ts b/js/notebook/src/tableDisplay/dataGrid/interface/IRenderer.ts new file mode 100644 index 0000000000..032323a7e8 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/interface/IRenderer.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2018 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 enum RENDERER_TYPE { + DataBars = 'DataBars' +} + +export default interface IRenderer { + type: RENDERER_TYPE, + includeText: boolean, + percent?: number + direction?: 'RIGHT'|'LEFT' +} diff --git a/js/notebook/src/tableDisplay/dataGrid/modal/ColumnLimitModal.ts b/js/notebook/src/tableDisplay/dataGrid/modal/ColumnLimitModal.ts new file mode 100644 index 0000000000..7f97648d4b --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/modal/ColumnLimitModal.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2018 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 '../../../global.env'; +import ColumnManager from "../column/ColumnManager"; +import createModalTemplate from './columnLimitModalTemplate'; +import {BeakerxDataGrid} from "../BeakerxDataGrid"; +import {selectColumnNames} from "../model/selectors"; +import {BeakerxDataStore} from "../store/dataStore"; +import {selectOutputColumnLimit} from "../column/selectors"; + +export default class ColumnLimitModal { + store: BeakerxDataStore; + columnManager: ColumnManager; + container: HTMLElement; + modalId: string; + + constructor(dataGrid: BeakerxDataGrid, container: HTMLElement) { + this.store = dataGrid.store; + this.columnManager = dataGrid.columnManager; + this.container = container; + this.modalId = dataGrid.id; + + this.init(); + } + + shouldOpenModal() { + return selectOutputColumnLimit(this.store.state) < selectColumnNames(this.store.state).length; + } + + init() { + if (!this.shouldOpenModal()) { + return; + } + + const modal = document.createElement('div'); + + modal.id = this.modalId; + modal.style.display = 'none'; + modal.innerHTML = createModalTemplate( + selectOutputColumnLimit(this.store.state), + selectColumnNames(this.store.state).length + ); + + this.container.appendChild(modal); + this.bindEvents(modal); + + setTimeout(() => { modal.style.display = 'block'; }); + } + + bindEvents(modal) { + const buttons = modal.querySelectorAll('button') || []; + + buttons[0].addEventListener('mouseup', () => { + this.container.removeChild(modal); + this.columnManager.indexColumns[0].menu.open(1); + }); + buttons[1].addEventListener('mouseup', () => this.container.removeChild(modal)); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/modal/columnLimitModalTemplate.ts b/js/notebook/src/tableDisplay/dataGrid/modal/columnLimitModalTemplate.ts new file mode 100644 index 0000000000..fd888f247a --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/modal/columnLimitModalTemplate.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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 function createModalTemplate( + outputColumnLimit: number, + columnNumber: number +): string { + return ` +
+ +
+ `; +}; diff --git a/js/notebook/src/tableDisplay/dataGrid/model/BeakerxDataGridModel.ts b/js/notebook/src/tableDisplay/dataGrid/model/BeakerxDataGridModel.ts new file mode 100644 index 0000000000..38499f2e50 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/model/BeakerxDataGridModel.ts @@ -0,0 +1,137 @@ +/* + * 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 { DataModel } from "@phosphor/datagrid"; +import { ALL_TYPES } from '../dataTypes'; +import { DataFormatter } from '../DataFormatter'; +import DataGridColumn from "../column/DataGridColumn"; +import IDataModelState from '../interface/IDataGridModelState'; +import { MapIterator, iter } from '@phosphor/algorithm'; +import { IColumn } from "../interface/IColumn"; +import ColumnManager from "../column/ColumnManager"; +import RowManager from "../row/RowManager"; +import DataGridRow from "../row/DataGridRow"; +import {BeakerxDataStore} from "../store/dataStore"; +import { + selectColumnsVisible, + selectHasIndex, + selectValues +} from "./selectors"; +import DataGridAction from "../store/DataGridAction"; +import {UPDATE_MODEL_DATA} from "./reducer"; +import {selectBodyColumnVisibility, selectColumnIndexByPosition} from "../column/selectors"; +import {COLUMN_TYPES} from "../column/enums"; + +export class BeakerxDataGridModel extends DataModel { + store: BeakerxDataStore; + dataFormatter: DataFormatter; + columnManager: ColumnManager; + rowManager: RowManager; + headerRowsCount: number; + + static DEFAULT_INDEX_COLUMN_TYPE = ALL_TYPES[1]; // integer + static DEFAULT_INDEX_COLUMN_NAME = 'index'; + + private _data: Array; + + constructor(store: BeakerxDataStore, columnManager: ColumnManager, rowManager: RowManager) { + super(); + + this.addProperties(store, columnManager, rowManager); + } + + reset() { + this.emitChanged({ type: 'model-reset' }); + } + + emitChanged(args: DataModel.ChangedArgs) { + super.emitChanged(args); + } + + addProperties(store: BeakerxDataStore, columnManager: ColumnManager, rowManager: RowManager) { + this.store = store; + this.dataFormatter = new DataFormatter(store); + this.columnManager = columnManager; + this.rowManager = rowManager; + this.headerRowsCount = 1; + + this._data = selectValues(store.state); + + this.setState({ + columnsVisible: selectColumnsVisible(this.store.state) || {} + }); + } + + updateData(state: IDataModelState) { + this.store.dispatch(new DataGridAction(UPDATE_MODEL_DATA, state)); + this.columnManager.resetColumnStates(); + this._data = selectValues(this.store.state); + this.rowManager.createRows(this._data, selectHasIndex(this.store.state)); + this.reset(); + } + + rowCount(region: DataModel.RowRegion): number { + return region === 'body' ? this.rowManager.rows.length : this.headerRowsCount; + } + + columnCount(region: DataModel.ColumnRegion): number { + return region === 'body' + ? selectBodyColumnVisibility(this.store.state).filter((value) => value).length + : 1; + } + + data(region: DataModel.CellRegion, row: number, position: number): any { + const columnType = DataGridColumn.getColumnTypeByRegion(region); + const index = selectColumnIndexByPosition(this.store.state, columnType, position); + const dataGridRow = this.rowManager.getRow(row); + + if (region === 'row-header') { + return dataGridRow.index; + } + + if (region === 'column-header') { + return row === 0 ? this.columnManager.bodyColumnNames[index] : ''; + } + + if (region === 'corner-header') { + return row === 0 ? this.columnManager.indexColumnNames[index] : ''; + } + + return dataGridRow.values[index]; + } + + setState(state) { + this.store.dispatch(new DataGridAction(UPDATE_MODEL_DATA, state)); + } + + setFilterHeaderVisible(visible: boolean) { + this.headerRowsCount = visible ? 2 : 1; + this.reset(); + } + + getColumnValuesIterator(column: IColumn): MapIterator { + if (!selectHasIndex(this.store.state) && column.type === COLUMN_TYPES.index) { + return new MapIterator(iter(this.rowManager.rows), (row) => row.index); + } + + return new MapIterator(iter(this.rowManager.rows), (row) => row.values[column.index]); + } + + setHeaderTextVertical(headersVertical: boolean) { + this.setState({ headersVertical }); + this.reset(); + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/model/reducer.ts b/js/notebook/src/tableDisplay/dataGrid/model/reducer.ts new file mode 100644 index 0000000000..451f695055 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/model/reducer.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2018 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 {Reducer} from "@phosphor/datastore"; +import IDataModelState from "../interface/IDataGridModelState"; +import DataGridAction, {DataGridColumnAction} from "../store/DataGridAction"; + +export const UPDATE_MODEL_DATA = 'UPDATE_MODEL_DATA'; +export const UPDATE_COLUMN_RENDERER = 'UPDATE_COLUMN_RENDERER'; + +const dataGridModelReducer: Reducer = ( + state: IDataModelState, + action: DataGridAction|DataGridColumnAction +): IDataModelState => { + switch(action.type) { + case UPDATE_MODEL_DATA: + return { ...state, ...action.payload }; + + case UPDATE_COLUMN_RENDERER: + return { + ...state, + rendererForColumn: { + ...state.rendererForColumn, + [action.payload.columnName]: action.payload.value + } + } + } + + return state; +}; + +export default dataGridModelReducer; diff --git a/js/notebook/src/tableDisplay/dataGrid/model/selectors.ts b/js/notebook/src/tableDisplay/dataGrid/model/selectors.ts new file mode 100644 index 0000000000..bc9d9ffee9 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/model/selectors.ts @@ -0,0 +1,117 @@ +/* + * Copyright 2018 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 IDataModelState from "../interface/IDataGridModelState"; +import IHihglighterState from "../interface/IHighlighterState"; +import {getAlignmentByChar, getAlignmentByType} from "../column/columnAlignment"; +import {ALL_TYPES} from "../dataTypes"; +import {TIME_UNIT_FORMATS} from "../../consts"; +import { createSelector } from 'reselect' + +export const selectModel = (state): IDataModelState => state.model; +export const selectValues = (state) => selectModel(state).values; +export const selectHasIndex = (state) => selectModel(state).hasIndex; +export const selectTooltips = (state) => selectModel(state).tooltips; +export const selectCellHighlighters = (state): IHihglighterState[] => selectModel(state).cellHighlighters || []; +export const selectHeadersVertical = (state) => selectModel(state).headersVertical; +export const selectHeaderFontSize = (state) => selectModel(state).headerFontSize; +export const selectDataFontSize = (state) => selectModel(state).dataFontSize; +export const selectFontColor = (state) => selectModel(state).fontColor; +export const selectColumnNames = (state) => selectModel(state).columnNames || []; +export const selectColumnTypes = (state) => selectModel(state).types; +export const selectColumnOrder = (state) => selectModel(state).columnOrder; +export const selectColumnsVisible = (state) => selectModel(state).columnsVisible || {}; +export const selectAlignmentForColumn = (state, dataType, columnName) => (selectModel(state).alignmentForColumn || {})[columnName]; +export const selectAlignmentForType = (state, dataType) => (selectModel(state).alignmentForType || {})[ALL_TYPES[dataType]]; +export const selectAlignmentByType = (state, dataType) => getAlignmentByType(dataType); +export const selectHasDoubleClickAction = (state) => selectModel(state).hasDoubleClickAction; +export const selectDoubleClickTag = (state) => selectModel(state).doubleClickTag; +export const selectContextMenuItems = (state) => selectModel(state).contextMenuItems || []; +export const selectContextMenuTags = (state) => selectModel(state).contextMenuTags || {}; +export const selectStringFormatForType = (state) => selectModel(state).stringFormatForType; +export const selectStringFormatForColumn = (state) => selectModel(state).stringFormatForColumn || {}; +export const selectStringFormatForTimes = (state) => selectModel(state).stringFormatForTimes; +export const selectFormatForTimes = (state) => TIME_UNIT_FORMATS[selectStringFormatForTimes(state)]; +export const selectTimeStrings = (state) => selectModel(state).timeStrings; +export const selectRendererForColumn = (state, column) => selectModel(state).rendererForColumn[column.name]; +export const selectRendererForType = (state, column) => selectModel(state).rendererForType[column.getDataTypeName()]; +export const selectTimeZone = (state) => selectModel(state).timeZone; +export const selectInitialColumnAlignment = createSelector( +[selectAlignmentForColumn, selectAlignmentForType, selectAlignmentByType], +(alignmentForColumn, alignmentForType, alignmentByType) => { + if (alignmentForType) { + return getAlignmentByChar(alignmentForType); + } + + if (alignmentForColumn) { + return getAlignmentByChar(alignmentForColumn); + } + + return alignmentByType; + } +); + +// Returns the map columnIndex => position +export const selectInitialColumnPositions = createSelector( + [selectColumnOrder, selectColumnNames, selectColumnsVisible], + (columnOrder, columnNames, columnsVisible) => { + const hasInitialOrder = columnOrder && columnOrder.length > 0; + const positions = columnNames.map((name, index) => index); + + if (hasInitialOrder) { + columnOrder.reverse().forEach((name) => { + const columnIndex = columnNames.indexOf(name); + + if (columnIndex === -1) { + return true; + } + + const columnPosition = positions.indexOf(columnIndex); + + positions.splice(columnPosition, 1); + positions.unshift(columnIndex); + }); + } + + Object.keys(columnsVisible).forEach((name, index) => { + if (columnsVisible[name] === false) { + let columnIndex = columnNames.indexOf(name); + let indexToRemove = positions.indexOf(columnIndex); + let removed = positions.splice(indexToRemove, 1)[0]; + + positions.push(removed); + } + }); + + const result: number[] = []; + + positions.forEach((column: number, position: number) => { + result[column] = position; + }); + + return result; +}); + +export const selectRenderer = createSelector( + [selectRendererForColumn, selectRendererForType], + (columnRenderer, typeRenderer) => { + if (columnRenderer || columnRenderer === null) { + return columnRenderer; + } + + return typeRenderer; + } +); diff --git a/js/notebook/src/tableDisplay/dataGrid/row/DataGridRow.ts b/js/notebook/src/tableDisplay/dataGrid/row/DataGridRow.ts new file mode 100644 index 0000000000..47e239193f --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/row/DataGridRow.ts @@ -0,0 +1,25 @@ +/* + * 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 class DataGridRow { + index: number; + values: any[]; + + constructor(index: number, values: any[]) { + this.index = index; + this.values = values; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/row/RowManager.ts b/js/notebook/src/tableDisplay/dataGrid/row/RowManager.ts new file mode 100644 index 0000000000..41103e8d26 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/row/RowManager.ts @@ -0,0 +1,213 @@ +/* + * 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 DataGridRow from "./DataGridRow"; +import { MapIterator, iter, toArray, filter } from '@phosphor/algorithm'; +import DataGridColumn from "../column/DataGridColumn"; +import {ALL_TYPES} from "../dataTypes"; +import ColumnManager from "../column/ColumnManager"; +import {COLUMN_TYPES, SORT_ORDER} from "../column/enums"; + +export default class RowManager { + rowsIterator: MapIterator; + rows: DataGridRow[]; + filterExpression: string; + expressionVars: string; + sortedBy: DataGridColumn; + columnManager: ColumnManager; + + constructor(data: any[], hasIndex: boolean, columnManager: ColumnManager) { + this.columnManager = columnManager; + this.createRows(data, hasIndex); + + this.evaluateSearchExpression = this.evaluateSearchExpression.bind(this); + this.evaluateFilterExpression = this.evaluateFilterExpression.bind(this); + } + + createRows(data, hasIndex) { + hasIndex ? this.createRowsWithIndex(data) : this.createRowsWithGeneratedIndex(data); + } + + createRowsWithGeneratedIndex(data) { + this.rowsIterator = new MapIterator( + iter(data), + (values, index) => new DataGridRow(index, values) + ); + this.rows = toArray(this.rowsIterator.clone()); + } + + createRowsWithIndex(data) { + this.rowsIterator = new MapIterator( + iter(data), + (values) => new DataGridRow(values[0], values.slice(1) + )); + + this.rows = toArray(this.rowsIterator.clone()); + } + + getRow(index): DataGridRow { + return this.rows[index]; + } + + sortByColumn(column: DataGridColumn) { + const sortOrder = column.getSortOrder(); + + this.sortedBy = column; + + if (column.type === COLUMN_TYPES.index || sortOrder === SORT_ORDER.NO_SORT) { + return this.sortRows(column.index, sortOrder, this.indexValueResolver); + } + + if (column.getDataType() === ALL_TYPES.datetime || column.getDataType() === ALL_TYPES.time) { + return this.sortRows(column.index, sortOrder, this.dateValueResolver); + } + + return this.sortRows(column.index, sortOrder); + } + + sortRows(columnIndex: number, sortOrder: SORT_ORDER, valueResolver?: Function): void { + const shouldReverse = sortOrder === SORT_ORDER.DESC; + const resolverFn = valueResolver ? valueResolver : this.defaultValueResolver; + + this.rows = this.rows.sort((row1, row2) => { + let value1 = resolverFn(row1, columnIndex); + let value2 = resolverFn(row2, columnIndex); + let result = 0; + + if (value1 > value2) { + result = 1; + } + + if (value1 < value2) { + result = -1; + } + + return shouldReverse ? -result : result; + }); + } + + resetSorting() { + if (this.sortedBy) { + this.sortedBy.sort(SORT_ORDER.NO_SORT); + } + } + + defaultValueResolver(row: DataGridRow, columnIndex: number) { + return row.values[columnIndex]; + } + + dateValueResolver(row, columnIndex: number) { + return row.values[columnIndex].timestamp; + } + + indexValueResolver(row, columnIndex: number) { + return row.index; + } + + createFilterExpressionVars() { + this.expressionVars = ''; + + const agregationFn = (column: DataGridColumn) => { + if (column.type === COLUMN_TYPES.index) { + this. expressionVars += `var ${column.name} = row.index;`; + } else { + this. expressionVars += `var ${column.name} = row.values[${column.index}];`; + } + }; + + this.columnManager.columns[COLUMN_TYPES.index].forEach(agregationFn); + this.columnManager.columns[COLUMN_TYPES.body].forEach(agregationFn); + } + + searchRows() { + this.filterRows(this.evaluateSearchExpression); + } + + filterRows(evalFn?: Function) { + const columns = this.columnManager.columns; + + this.createFilterExpression(); + + if (!this.filterExpression) { + this.rows = toArray(this.rowsIterator.clone()); + + return; + } + + const formatFns = {}; + formatFns[COLUMN_TYPES.index] = columns[COLUMN_TYPES.index].map(column => column.formatFn); + formatFns[COLUMN_TYPES.body] = columns[COLUMN_TYPES.body].map(column => column.formatFn); + + try { + this.rows = toArray(filter( + this.rowsIterator.clone(), + (row) => evalFn ? evalFn(row, formatFns) : this.evaluateFilterExpression(row, formatFns) + )); + this.sortedBy && this.sortByColumn(this.sortedBy); + } catch (e) {} + } + + takeRows(start: number, end: number) { + return this.rows.slice(start, end); + } + + createFilterExpression(): void { + let expressionParts: string[] = []; + const agregationFn = (column: DataGridColumn) => { + let filter = column.getFilter(); + + if (filter) { + expressionParts.push(filter); + } + }; + + this.columnManager.columns[COLUMN_TYPES.index].forEach(agregationFn); + this.columnManager.columns[COLUMN_TYPES.body].forEach(agregationFn); + + this.filterExpression = expressionParts.join(' && ').trim(); + } + + evaluateFilterExpression(row, formatFns) { + const evalInContext = function(expression: string) { + const row = { ...this.row }; + const result = eval(expression); + + return result !== undefined ? result : true; + }.bind({ row }); + + return evalInContext(String(`${this.expressionVars} ${this.filterExpression}`)); + } + + evaluateSearchExpression(row, formatFns) { + const evalInContext = function(expression: string) { + const row = { + index: formatFns[COLUMN_TYPES.index][0]({ row: this.row.index, value: this.row.index, column: 0 }), + values: this.row.values.map((value, index) => formatFns[COLUMN_TYPES.body][index]({ value, row: this.row.index, column: index })) + }; + const result = eval(expression); + + return result !== undefined ? result : true; + }.bind({ row }); + + return evalInContext(String(`${this.expressionVars} ${this.filterExpression}`)); + } + + getValueByColumn(row: number, columnIndex: number, columnType: COLUMN_TYPES) { + return columnType === COLUMN_TYPES.body + ? this.getRow(row).values[columnIndex] + : this.getRow(row).index; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/store/DataGridAction.ts b/js/notebook/src/tableDisplay/dataGrid/store/DataGridAction.ts new file mode 100644 index 0000000000..0a75ae97a1 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/store/DataGridAction.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2018 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 {Action} from "@phosphor/datastore"; +import {COLUMN_TYPES} from "../column/enums"; + +export default class DataGridAction extends Action { + payload: any; + + constructor(type: string, payload: any) { + super(type); + + this.payload = payload; + } +} + +export class DataGridColumnsAction extends DataGridAction { + payload: { + value: any, + hasIndex?: boolean, + defaultValue?: any + }; + + constructor(type: string, payload: any) { + super(type, payload); + + this.payload = payload; + } +} + +export class DataGridColumnAction extends DataGridAction { + payload: { + columnType: COLUMN_TYPES, + columnIndex: number, + columnName?: string, + hasIndex?: boolean, + value: any + }; + + constructor(type: string, payload: any) { + super(type, payload); + + this.payload = payload; + } +} diff --git a/js/notebook/src/tableDisplay/dataGrid/store/dataStore.ts b/js/notebook/src/tableDisplay/dataGrid/store/dataStore.ts new file mode 100644 index 0000000000..d98e2324c5 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/store/dataStore.ts @@ -0,0 +1,147 @@ +/* + * Copyright 2018 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 {DataStore, combineReducers} from "@phosphor/datastore"; +import dataGridModelReducer from "../model/reducer"; +import IDataModelState from "../interface/IDataGridModelState"; +import columnReducer from "../column/reducer"; +import { + selectInitialColumnAlignment, + selectColumnNames, + selectColumnOrder, + selectColumnsVisible, + selectColumnTypes, + selectHasIndex, + selectInitialColumnPositions, + selectStringFormatForColumn, + selectStringFormatForType, selectFormatForTimes +} from "../model/selectors"; +import {BeakerxDataGridModel} from "../model/BeakerxDataGridModel"; +import {selectOutputColumnLimit} from "../column/selectors"; +import {getDisplayType, getTypeByName} from "../dataTypes"; +import {COLUMN_TYPES, SORT_ORDER} from "../column/enums"; +import {IColumnsState, IColumnState} from "../interface/IColumn"; + +export interface IBeakerxDataGridState { + model: IDataModelState, + columns: IColumnsState +} + +export type BeakerxDataStore = DataStore; + +export default function createStore(initialState: IDataModelState) { + return new DataStore(combineReducers({ + model: dataGridModelReducer, + columns: columnReducer + }), { model: initialState, columns: createInitialColumnsState(initialState) }); +} + +export function createInitialColumnsState(initialState: IDataModelState): IColumnsState { + const initialColumnsState: IColumnsState = new Map(); + const state = { model: initialState, columns: initialColumnsState }; + const names = addColumnNamesState(state); + const types = addColumnTypesState(state); + const visibility = addColumnsVisibilityState(state); + const positions = addColumnsPositions(state); + + const addColumnState = (columnType: COLUMN_TYPES) => (name, index) => { + let key = `${columnType}_${index}`; + let dataType = getTypeByName(types[columnType][index]); + + initialColumnsState.set(key, { + name, + index, + dataType, + columnType, + filter: null, + formatForTimes: selectFormatForTimes(state), + sortOrder: SORT_ORDER.NO_SORT, + horizontalAlignment: selectInitialColumnAlignment(state, dataType, name), + keepTrigger: columnType === COLUMN_TYPES.index, + position: positions[columnType][index], + visible: visibility[columnType][index], + dataTypeName: types[columnType][index], + displayType: getDisplayType( + dataType, + selectStringFormatForType(state), + selectStringFormatForColumn(state)[name] + ) + }); + }; + + names[COLUMN_TYPES.index].forEach(addColumnState(COLUMN_TYPES.index)); + names[COLUMN_TYPES.body].forEach(addColumnState(COLUMN_TYPES.body)); + + return initialColumnsState; +} + +function addColumnsVisibilityState(state: IBeakerxDataGridState) { + const columnOrder = selectColumnOrder(state); + const columnsVisible = selectColumnsVisible(state); + const columnNames = selectColumnNames(state); + const hasInitialOrder = columnOrder && columnOrder.length > 0; + const outputColumnLimit = selectOutputColumnLimit(state); + const addVisibilityStateItem = (name, index) => { + if (index >= outputColumnLimit) { + return false; + } + + if (hasInitialOrder) { + return columnOrder.indexOf(name) !== -1; + } + + return columnsVisible[name] !== undefined ? columnsVisible[name] : true; + }; + + return createColumnsState({ + value: columnNames.map(addVisibilityStateItem), + defaultValue: [true] + }, state); +} + +function addColumnsPositions(state: IBeakerxDataGridState) { + return createColumnsState({ + value: selectInitialColumnPositions(state), + defaultValue: [0] + }, state); +} + +function addColumnNamesState(state: IBeakerxDataGridState) { + const value = selectColumnNames(state); + + return createColumnsState({ + value, + defaultValue: [BeakerxDataGridModel.DEFAULT_INDEX_COLUMN_NAME] + }, state); +} + +function addColumnTypesState(state: IBeakerxDataGridState) { + const value = selectColumnTypes(state); + + return createColumnsState({ + value, + defaultValue: [BeakerxDataGridModel.DEFAULT_INDEX_COLUMN_TYPE] + }, state); +} + +function createColumnsState({ value, defaultValue }, state) { + const hasIndex = selectHasIndex(state); + + return { + [COLUMN_TYPES.body]: hasIndex ? value.slice(1) : value, + [COLUMN_TYPES.index]: hasIndex ? value.slice(0, 1) : defaultValue + }; +} diff --git a/js/notebook/src/tableDisplay/dataGrid/style/dataGrid.scss b/js/notebook/src/tableDisplay/dataGrid/style/dataGrid.scss new file mode 100644 index 0000000000..70810ec6c1 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/style/dataGrid.scss @@ -0,0 +1,173 @@ +/* + * 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. + */ + +//Colors +$alto: #DADADA; +$gallery: #F0F0F0; +$silver: #CDCDCD; +$silverChalice: #A0A0A0; +$highlightBlue: #39a9ed; + +$gridBackgroundColor: $gallery; +$borderColor: $silverChalice; +$buttonActive: $silver; +$gridBorder: 1px solid $borderColor; +$tooltipTransition: opacity 300ms ease-in-out 300ms; + +.p-DataGrid { + min-width: 64px; + max-width: 100%; + min-height: 330px; + padding: 20px; + border: 1px solid white; + font-family: "Lato", Helvetica, sans-serif; + + .bko-menu { + opacity: 1; + + &:hover { + background-color: $highlightBlue; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAWCAYAAADjGu3TAAAAI0lEQVQoFWP8DwQMWAATFjGwED0kcNmNW5xx1B/ogUPFiAIAYSAMFvNwTdIAAAAASUVORK5CYII='); + } + + &.sorting_asc, + &.sorting_desc { + background-size: 18px; + } + + &.sorting_asc { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg=='); + + &:hover { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAANklEQVR42mNgGNLgPxBQzSAYoNSgrf9RwVaquIoi1wH1NfzHDqZSxVVkue4/EYBhFIyCUYAGABPAkfD1W4bSAAAAAElFTkSuQmCC"); + } + } + + &.sorting_desc { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII='); + + &:hover { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAL0lEQVR42mNgGAWjYBSggf9EAKoZSI7rtuIwayrVvEtJ2KG7bivVIoNqsTu00ycAka+SJUKi7ZcAAAAASUVORK5CYII="); + } + } + } +} + +.p-DataGrid-tooltip { + background-color: #ffffff; + box-shadow: 0 0 3px #333333; + border-radius: 3px; + padding: 5px 10px; + opacity: 0; + -webkit-transition: $tooltipTransition; + -moz-transition: $tooltipTransition; + -ms-transition: $tooltipTransition; + -o-transition: $tooltipTransition; + transition: $tooltipTransition; + font-family: "Lato", Helvetica, sans-serif; + z-index: 900; + + &.visible { + opacity: 1; + } +} + +.p-DataGrid-viewport { + border: $gridBorder; +} + +.p-DataGrid-scrollCorner { + background-color: $gridBackgroundColor; + border-right: $gridBorder; + border-bottom: $gridBorder; +} + +.p-DataGrid-scrollCorner::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 1px; + height: 1px; + background-color: $borderColor; +} + +.p-ScrollBar { + &[data-orientation='horizontal'] { + min-height: 16px; + max-height: 16px; + min-width: 45px; + border-bottom: $gridBorder; + border-left: $gridBorder; + border-right: $gridBorder; + + .p-ScrollBar-thumb { + height: 100%; + min-width: 15px; + border-left: $gridBorder; + border-right: $gridBorder; + } + } + + &[data-orientation='vertical'] { + min-width: 16px; + max-width: 16px; + min-height: 45px; + border-right: $gridBorder; + border-top: $gridBorder; + border-bottom: $gridBorder; + + .p-ScrollBar-thumb { + width: 100%; + min-height: 15px; + border-top: $gridBorder; + border-bottom: $gridBorder; + } + } +} + +.p-ScrollBar-button { + background-color: $gridBackgroundColor; + background-position: center center; + min-height: 15px; + max-height: 15px; + min-width: 15px; + max-width: 15px; + + &:hover { + background-color: $alto; + } + + &.p-mod-active { + background-color: $buttonActive; + } +} + +.p-ScrollBar-track { + background: $gridBackgroundColor; +} + +.p-ScrollBar-thumb { + background: $silver; + + &:hover { + background: $silver; + } + + &.p-mod-active { + background: $silverChalice; + } +} \ No newline at end of file diff --git a/js/notebook/src/tableDisplay/dataGrid/style/dataGridStyle.ts b/js/notebook/src/tableDisplay/dataGrid/style/dataGridStyle.ts new file mode 100644 index 0000000000..03999fbe58 --- /dev/null +++ b/js/notebook/src/tableDisplay/dataGrid/style/dataGridStyle.ts @@ -0,0 +1,84 @@ +/* + * 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 { DataGrid } from '@phosphor/datagrid'; +import * as bkUtils from '../../../shared/bkUtils'; + +import './dataGrid.scss'; + +const bkHelper = require('../../../shared/bkHelper'); +const GLOBALS = require('../../../shared/bkGlobals'); + +export const DEFAULT_CELL_BACKGROUND = ''; +export const FOCUSED_CELL_BACKGROUND = 'rgb(200, 200, 200)'; +export const DEFAULT_DATA_FONT_SIZE = 13; +export const DEFAULT_HEADER_FONT_COLOR = '#515A5A'; +export const DEFAULT_DATA_FONT_COLOR = '#000000'; +export const DEFAULT_GRID_PADDING = 20; +export const DEFAULT_GRID_BORDER_WIDTH = 1; +export const MIN_COLUMN_WIDTH = 40; +export const DEFAULT_ROW_HEIGHT = 24; + +export const DEFAULT_COLORS = { + [GLOBALS.THEMES.DEFAULT]: { + red: rgbToHex(241, 88, 84), + blue: rgbToHex(93, 165, 218), + green: rgbToHex(96, 189, 104) + }, + [GLOBALS.THEMES.AMBIANCE]: { + red: rgbToHex(191, 39, 31), + blue: rgbToHex(46, 119, 191), + green: rgbToHex(75, 160, 75) + } +}; + +export function rgbToHex(r, g, b) { + return formatColor(bkUtils.rgbaToHex(r, g, b)); +} + +// Darken function for color in 'rgb(r, g, b)' format +export function darken(color: string, factor = 0.8): string { + const match = color.match(/\((.*)\)/); + + if (!match) { + return color; + } + + const rgb: string[] = match[1].split(', '); + const rgbArray = [ + Math.ceil(parseInt(rgb[0]) * factor), + Math.ceil(parseInt(rgb[1]) * factor), + Math.ceil(parseInt(rgb[2]) * factor) + ]; + + return rgbToHex(rgbArray[0],rgbArray[1],rgbArray[2]); +} + +export function getDefaultColor(color) { + return DEFAULT_COLORS[bkHelper.getTheme()][color]; +} + +export function formatColor(hexColor) { + //remove alpha + return hexColor.length > 7 ? '#' + hexColor.substr(3) : hexColor; +} + +export const silverStripeStyle: DataGrid.IStyle = { + ...DataGrid.defaultStyle, + voidColor: '#ffffff', + headerBackgroundColor: '#E6E6E6', + rowBackgroundColor: i => i % 2 === 0 ? '#f9f9f9' : '' +}; diff --git a/js/notebook/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts b/js/notebook/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts index cf531ce79b..69db501eaf 100644 --- a/js/notebook/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts +++ b/js/notebook/src/tableDisplay/tableHeaderMenu/createFormatMenuItems.ts @@ -16,8 +16,7 @@ import MenuItem from "../../shared/interfaces/menuItemInterface"; import _ from 'underscore'; - -const tableConsts = require('../consts'); +import { TIME_UNIT_FORMATS } from '../consts'; const getColumnTypes = (scope: any, colIdx: number): object[] => { if (colIdx === 0) { @@ -110,8 +109,8 @@ export function createPrecisionSubitems(scope): MenuItem[] { export function createTimeSubitems(scope): MenuItem[] { const items: MenuItem[] = []; - _.forEach(tableConsts.TIME_UNIT_FORMATS, function(value, unit) { - if (tableConsts.TIME_UNIT_FORMATS.hasOwnProperty(unit)) { + _.forEach(TIME_UNIT_FORMATS, function(value, unit) { + if (TIME_UNIT_FORMATS.hasOwnProperty(unit)) { let item = { title: value.title, isChecked: function(colIdx) { diff --git a/js/notebook/src/tableDisplay/tableScope.js b/js/notebook/src/tableDisplay/tableScope.js index ce90d4bf18..45b8520c22 100644 --- a/js/notebook/src/tableDisplay/tableScope.js +++ b/js/notebook/src/tableDisplay/tableScope.js @@ -28,7 +28,6 @@ define([ './../shared/bkUtils', './cellHighlighters', './../shared/bkHelper', - './consts', 'jquery-ui/ui/widgets/tooltip', './tableUtils' ], function( @@ -45,13 +44,13 @@ define([ bkUtils, cellHighlighters, bkHelper, - tableConsts, tooltip, tableUtils ) { var jQuery = $; var updateTableEventListener; + var tableConsts = require('./consts').default; tableUtils.setJqExtentions(); diff --git a/js/notebook/src/tree/Types/IUIOptions.ts b/js/notebook/src/tree/Types/IUIOptions.ts index d530899990..c3c1496680 100644 --- a/js/notebook/src/tree/Types/IUIOptions.ts +++ b/js/notebook/src/tree/Types/IUIOptions.ts @@ -20,4 +20,5 @@ export default interface IUIOptions { wide_cells: boolean; show_publication: boolean; auto_save: boolean; + use_data_grid: boolean; } diff --git a/js/notebook/src/tree/Utils/BeakerXApi.ts b/js/notebook/src/tree/Utils/BeakerXApi.ts index 30cd4ef652..414438b23c 100644 --- a/js/notebook/src/tree/Utils/BeakerXApi.ts +++ b/js/notebook/src/tree/Utils/BeakerXApi.ts @@ -37,6 +37,7 @@ export default class BeakerXApi { wide_cells: true, show_publication: true, auto_save: true, + use_data_grid: true, }, version: 2 }; diff --git a/js/notebook/src/tree/Widgets/UIOptions/UIOptionsWidget.ts b/js/notebook/src/tree/Widgets/UIOptions/UIOptionsWidget.ts index f7388624fe..eda16591f8 100644 --- a/js/notebook/src/tree/Widgets/UIOptions/UIOptionsWidget.ts +++ b/js/notebook/src/tree/Widgets/UIOptions/UIOptionsWidget.ts @@ -32,6 +32,7 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface public readonly IMPROVE_FONTS_SELECTOR = '#improve_fonts'; public readonly SHOW_PUBLICATION_SELECTOR = '#show_publication'; public readonly AUTO_SAVE_SELECTOR = '#auto_save'; + public readonly USE_DATA_GRID_SELECTOR = '#use_data_grid'; public readonly UI_OPTIONS = [ { @@ -64,6 +65,12 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface label: 'Auto save notebooks', isLabSupported: true, }, + { + id: 'use_data_grid', + name: 'use_data_grid', + label: 'Use PhosphorJS DataGrid for TableDisplay Widget', + isLabSupported: true, + }, ]; public readonly HTML_ELEMENT_TEMPLATE = ` @@ -93,6 +100,7 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface this.WIDE_CELLS_SELECTOR, this.SHOW_PUBLICATION_SELECTOR, this.AUTO_SAVE_SELECTOR, + this.USE_DATA_GRID_SELECTOR, ].join(',')) .on('change', this.optionsChangedHandler.bind(this)); } @@ -105,6 +113,7 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface this.setImproveFonts(options.improve_fonts); this.setShowPublication(options.show_publication); this.setAutoSave(options.auto_save); + this.setUseDataGrid(options.use_data_grid); } protected onActivateRequest(): void { @@ -190,5 +199,11 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface .prop('checked', checked); } + private setUseDataGrid(checked: boolean) { + this.$node + .find(this.USE_DATA_GRID_SELECTOR) + .prop('checked', checked); + } + private _options: IUIOptions; } diff --git a/js/notebook/src/tsconfig.json b/js/notebook/src/tsconfig.json index 1778c75478..8f535ed7f8 100644 --- a/js/notebook/src/tsconfig.json +++ b/js/notebook/src/tsconfig.json @@ -8,7 +8,10 @@ "allowJs": true, "moduleResolution": "Node", "noEmit": true, - "lib": ["dom", "es5", "es2015.promise"], - "types": [] + "lib": ["dom", "es5", "es2015"], + "types": [], + "paths": { + "jquery": ["jquery/dist/jquery.js"] + } } -} +} \ No newline at end of file diff --git a/js/notebook/test/mocha.opts b/js/notebook/test/mocha.opts new file mode 100644 index 0000000000..28bdd63adb --- /dev/null +++ b/js/notebook/test/mocha.opts @@ -0,0 +1,6 @@ +--require tsconfig-paths/register +--require ts-node/register +--require ignore-styles +--require ./test/setup.js +--watch-extensions ts,tsx +./test/**/*.spec.ts diff --git a/js/notebook/test/setup.js b/js/notebook/test/setup.js new file mode 100644 index 0000000000..91ffc3ef61 --- /dev/null +++ b/js/notebook/test/setup.js @@ -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. + */ + +var jsdom = require('jsdom'); + +global.beakerx = {}; +global.window = new jsdom.JSDOM().window; +global.document = window.document; +global.Element = window.Element; +global.HTMLElement = window.HTMLElement; +global.HTMLSpanElement = window.HTMLSpanElement; +global.HTMLInputElement = window.HTMLInputElement; +global.MouseEvent = window.MouseEvent; +global.KeyboardEvent = window.KeyboardEvent; + +window.HTMLCanvasElement.prototype.getContext = function() { + return {}; +}; + +global.navigator = window.navigator; +global.define = function() {}; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/BeakerxDataGrid.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/BeakerxDataGrid.spec.ts new file mode 100644 index 0000000000..2b086de1be --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/BeakerxDataGrid.spec.ts @@ -0,0 +1,64 @@ +/* + * 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 * as sinon from 'sinon'; +import { expect } from 'chai'; +import { Widget } from "@phosphor/widgets"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import { BeakerxDataGridModel } from "@beakerx/tableDisplay/dataGrid/model/BeakerxDataGridModel"; +import modelStateMock from "./mock/modelStateMock"; +import ColumnManager from "@beakerx/tableDisplay/dataGrid/column/ColumnManager"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('BeakerxDataGrid', () => { + let dataGrid; + let dataStore; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should create the columnManager', () => { + expect(dataGrid.columnManager).to.be.an.instanceof(ColumnManager); + }); + + it('should have the model property of type BeakerxDataGridModel', () => { + expect(dataGrid.model).to.be.an.instanceof(BeakerxDataGridModel); + }); + + it('should have the viewport set', () => { + expect(dataGrid).to.have.property('viewport'); + expect(dataGrid.viewport).to.be.an.instanceof(Widget); + }); + + it('should implement destroy method', () => { + const eventManagerStub = sinon.stub(dataGrid.eventManager, 'destroy'); + const columnsDestroyStub = sinon.stub(dataGrid.columnManager, 'destroy'); + + dataGrid.destroy(); + + expect(eventManagerStub.calledOnce).to.be.true; + expect(columnsDestroyStub.calledOnce).to.be.true; + + eventManagerStub.restore(); + columnsDestroyStub.restore(); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/DataFormatter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/DataFormatter.spec.ts new file mode 100644 index 0000000000..4667723edd --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/DataFormatter.spec.ts @@ -0,0 +1,337 @@ +/* + * 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 * as sinon from 'sinon'; +import { expect, assert } from 'chai'; +import { DataFormatter } from '@beakerx/tableDisplay/dataGrid/DataFormatter'; +import { TIME_UNIT_FORMATS } from '@beakerx/tableDisplay/consts'; +import * as moment from 'moment-timezone/builds/moment-timezone-with-data'; +import modelStateMock from "./mock/modelStateMock"; +import {BeakerxDataGrid} from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import columnOptionsMock from "./mock/columnOptionsMock"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import {ALL_TYPES} from "@beakerx/tableDisplay/dataGrid/dataTypes"; +import cellConfigMock from "./mock/cellConfigMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +declare var require: Function; + +describe('DataFormatter', () => { + const dataStore = createStore(modelStateMock); + const dataFormatter = new DataFormatter(dataStore); + const cellConfig = cellConfigMock; + + it('should implement getFormatFnByDisplayType method', () => { + expect(dataFormatter.getFormatFnByDisplayType).to.be.a('function'); + }); + + describe('getFormatFnByDisplayType', () => { + it('should throw Error while called withoud param', () => { + assert.throws( + () => { dataFormatter.getFormatFnByDisplayType(undefined); }, + Error, + "Cannot read property 'toString' of undefined" + ); + }); + + it('should return function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.integer)).to.be.a('function'); + }); + + it('should return "string" function', () => { + expect(dataFormatter.getFormatFnByDisplayType('')).to.equal(dataFormatter['string']); + }); + + it('should return "string" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.string)).to.equal(dataFormatter['string']); + }); + + it('should return "integer" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.integer)).to.equal(dataFormatter['integer']); + }); + + it('should return "formattedInteger" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES['formatted integer'])).to.equal(dataFormatter['formattedInteger']); + }); + + it('should return "double" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.double)).to.equal(dataFormatter['double']); + }); + + it('should return "exponential_5" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES['exponential 5'])).to.equal(dataFormatter['exponential_5']); + }); + + it('should return "exponential_15" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES['exponential 15'])).to.equal(dataFormatter['exponential_15']); + }); + + it('should return "datetime" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.datetime).toString()).to.equal(dataFormatter['datetimeWithFormat']({}).toString()); + }); + + it('should return "boolean" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.boolean)).to.equal(dataFormatter['boolean']); + }); + + it('should return "html" function', () => { + expect(dataFormatter.getFormatFnByDisplayType(ALL_TYPES.html)).to.equal(dataFormatter['html']); + }); + + it('should return "doubleWithPrecision" function', () => { + expect(dataFormatter.getFormatFnByDisplayType('4.3').toString()).to.equal((dataFormatter['doubleWithPrecision']('3')).toString()); + }); + }); + + describe('dataFormatter.string', () => { + const stringFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES.string); + + it('should return empty string', () => { + expect(stringFormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should not escape html characters', () => { + expect(stringFormatFn({ ...cellConfig, value: '&test<>"Works"Ok/<>' })) + .to.equal('&test<>"Works"Ok/<>'); + }); + + it('should convert to date', () => { + expect(stringFormatFn({ ...cellConfig, value: { timestamp: 1516697673043, type: 'Date' }})) + .to.equal('20180123 09:54:33.043 +0100'); + }); + + it('should return given value', () => { + expect(stringFormatFn({ ...cellConfig, value: 1 })).to.equal(1); + expect(stringFormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(stringFormatFn({ ...cellConfig, value: '' })).to.equal(''); + expect(stringFormatFn({ ...cellConfig, value: 0 })).to.equal(0); + expect(stringFormatFn({ ...cellConfig, value: false })).to.equal(false); + }); + + }); + + describe('dataFormatter.integer', () => { + const integerFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES.integer); + + it('should return integer', () => { + expect(integerFormatFn({ ...cellConfig, value: '1' })).to.equal(1); + expect(integerFormatFn({ ...cellConfig, value: '0' })).to.equal(0); + expect(integerFormatFn({ ...cellConfig, value: '123' })).to.equal(123); + expect(integerFormatFn({ ...cellConfig, value: 123 })).to.equal(123); + expect(integerFormatFn({ ...cellConfig, value: 1 })).to.equal(1); + }); + + it('should return empty value', () => { + expect(integerFormatFn({ ...cellConfig, value: undefined })).to.equal(undefined); + expect(integerFormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(integerFormatFn({ ...cellConfig, value: 0 })).to.equal(0); + expect(integerFormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should return NaN', () => { + expect(integerFormatFn({ ...cellConfig, value: false }).toString()).to.equal('NaN'); + expect(integerFormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('NaN'); + expect(integerFormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('NaN'); + }); + + }); + + describe('dataFormatter.formattedInteger', () => { + const formattedIntegerFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES['formatted integer']); + + it('should return formatted integer', () => { + expect(formattedIntegerFormatFn({ ...cellConfig, value: '1' })).to.equal('1'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: '0' })).to.equal('0'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: '123' })).to.equal('123'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 123 })).to.equal('123'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 1 })).to.equal('1'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 0 })).to.equal('0'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 1230 })).to.equal('1,230'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 123023 })).to.equal('123,023'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 1123023 })).to.equal('1,123,023'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: '1123023' })).to.equal('1,123,023'); + }); + + it('should return empty value', () => { + expect(formattedIntegerFormatFn({ ...cellConfig, value: undefined })).to.equal(undefined); + expect(formattedIntegerFormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(formattedIntegerFormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should return NaN', () => { + expect(formattedIntegerFormatFn({ ...cellConfig, value: false }).toString()).to.equal('NaN'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('NaN'); + expect(formattedIntegerFormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('NaN'); + }); + + }); + + describe('dataFormatter.double', () => { + const doubleFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES.double); + + it('should return formatted double', () => { + expect(doubleFormatFn({ ...cellConfig, value: '1' })).to.equal(1); + expect(doubleFormatFn({ ...cellConfig, value: '1.2' })).to.equal(1.2); + expect(doubleFormatFn({ ...cellConfig, value: 1.2 })).to.equal(1.2); + }); + + it('should return empty value', () => { + expect(doubleFormatFn({ ...cellConfig, value: undefined })).to.equal(undefined); + expect(doubleFormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(doubleFormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should return NaN', () => { + expect(doubleFormatFn({ ...cellConfig, value: false }).toString()).to.equal('NaN'); + expect(doubleFormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('NaN'); + expect(doubleFormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('NaN'); + }); + + }); + + describe('dataFormatter.doubleWithPrecision', () => { + const doubleWithPrecisionFormatFn = dataFormatter.getFormatFnByDisplayType('4.3'); + + it('should return formatted double with precission', () => { + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: '1' })).to.equal('1.000'); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: '1.2' })).to.equal('1.200'); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: 1.2 })).to.equal('1.200'); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: 1.23456 })).to.equal('1.235'); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: 1.23446 })).to.equal('1.234'); + }); + + it('should return empty value', () => { + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: undefined })).to.equal(undefined); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should return NaN', () => { + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: false }).toString()).to.equal('NaN'); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('NaN'); + expect(doubleWithPrecisionFormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('NaN'); + }); + + }); + + describe('dataFormatter.exponential_5', () => { + const exponential_5FormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES['exponential 5']); + + it('should return formatted exponential_5', () => { + expect(exponential_5FormatFn({ ...cellConfig, value: '1' })).to.equal('1.00000e+0'); + expect(exponential_5FormatFn({ ...cellConfig, value: 1234 })).to.equal('1.23400e+3'); + expect(exponential_5FormatFn({ ...cellConfig, value: 0 })).to.equal('0.00000e+0'); + }); + + it('should return empty value', () => { + expect(exponential_5FormatFn({ ...cellConfig, value: undefined })).to.equal(undefined); + expect(exponential_5FormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(exponential_5FormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should return NaN', () => { + expect(exponential_5FormatFn({ ...cellConfig, value: false }).toString()).to.equal('NaN'); + expect(exponential_5FormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('NaN'); + expect(exponential_5FormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('NaN'); + }); + + }); + + describe('dataFormatter.exponential_15', () => { + const exponential_15FormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES['exponential 15']); + + it('should return formatted exponential_15', () => { + expect(exponential_15FormatFn({ ...cellConfig, value: '1' })).to.equal('1.000000000000000e+0'); + expect(exponential_15FormatFn({ ...cellConfig, value: 1234 })).to.equal('1.234000000000000e+3'); + expect(exponential_15FormatFn({ ...cellConfig, value: 12343456 })).to.equal('1.234345600000000e+7'); + expect(exponential_15FormatFn({ ...cellConfig, value: 0 })).to.equal('0.000000000000000e+0'); + }); + + it('should return empty value', () => { + expect(exponential_15FormatFn({ ...cellConfig, value: undefined })).to.equal(undefined); + expect(exponential_15FormatFn({ ...cellConfig, value: null })).to.equal(null); + expect(exponential_15FormatFn({ ...cellConfig, value: '' })).to.equal(''); + }); + + it('should return NaN', () => { + expect(exponential_15FormatFn({ ...cellConfig, value: false }).toString()).to.equal('NaN'); + expect(exponential_15FormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('NaN'); + expect(exponential_15FormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('NaN'); + }); + + }); + + describe('dataFormatter.datetime', () => { + const datetimeFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES.datetime); + const bkUtils = require('@beakerx/shared/bkUtils'); + + before (() => { + sinon.stub( + bkUtils, + 'formatTimestamp', + ).callsFake((value) => moment(new Date(value)).format(TIME_UNIT_FORMATS.DATETIME.format)); + }); + + after(() => { + bkUtils.formatTimestamp.restore() + }); + + it('should return formatted datetime', () => { + expect(datetimeFormatFn({ ...cellConfig, value: { timestamp: 1516697673043, type: 'Date' }})) + .to.equal('20180123 09:54:33.043 +0100'); + + expect(datetimeFormatFn({ ...cellConfig, value: 1516703121 })).to.equal('20180123 11:25:21.000 +0100'); + }); + + it('should return Invalid date', () => { + expect(datetimeFormatFn({ ...cellConfig, value: NaN }).toString()).to.equal('Invalid date'); + expect(datetimeFormatFn({ ...cellConfig, value: 'something' }).toString()).to.equal('Invalid date'); + }); + + }); + + describe('dataFormatter.boolean', () => { + const booleanFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES.boolean); + + it('should return "true"', () => { + expect(booleanFormatFn({ ...cellConfig, value: 'something' })).to.equal('true'); + expect(booleanFormatFn({ ...cellConfig, value: true })).to.equal('true'); + expect(booleanFormatFn({ ...cellConfig, value: 0 })).to.equal('true'); + }); + + it('should return "false"', () => { + expect(booleanFormatFn({ ...cellConfig, value: NaN })).to.equal('false'); + expect(booleanFormatFn({ ...cellConfig, value: false })).to.equal('false'); + expect(booleanFormatFn({ ...cellConfig, value: undefined })).to.equal('false'); + expect(booleanFormatFn({ ...cellConfig, value: null })).to.equal('false'); + expect(booleanFormatFn({ ...cellConfig, value: '' })).to.equal('false'); + }); + + }); + + describe('dataFormatter.html', () => { + const booleanFormatFn = dataFormatter.getFormatFnByDisplayType(ALL_TYPES.html); + const testObject = { someProp: '' }; + + it('should return given value', () => { + expect(booleanFormatFn({ ...cellConfig, value: 'something' })).to.equal('something'); + expect(booleanFormatFn({ ...cellConfig, value: true })).to.equal(true); + expect(booleanFormatFn({ ...cellConfig, value: 0 })).to.equal(0); + expect(booleanFormatFn({ ...cellConfig, value: testObject })).to.equal(testObject); + }); + + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/DataGridScope.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/DataGridScope.spec.ts new file mode 100644 index 0000000000..9e28a7d0d5 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/DataGridScope.spec.ts @@ -0,0 +1,72 @@ +/* + * 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 modelStateMock from "./mock/modelStateMock"; + +declare var require: Function; + +import { expect, assert } from 'chai'; +import * as sinon from 'sinon'; +import { Widget } from "@phosphor/widgets"; +import { DataGridScope } from '@beakerx/tableDisplay/dataGrid/DataGridScope'; + +const element = document.createElement('div'); + +describe('dataGridScope', () => { + const scopeOptions = { + element, + widgetView: new Widget({}), + widgetModel: new Widget({}), + data: modelStateMock + }; + const dataGridScope = new DataGridScope(scopeOptions); + + it('should fail initialization', () => { + assert.throws( + () => { new DataGridScope({ ...scopeOptions, data: {} }); }, + Error, + "Cannot read property 'iter' of undefined" + ); + }); + + it('should implement "render" method', () => { + expect(dataGridScope).to.have.property('render'); + expect(dataGridScope.render).to.be.a('function'); + }); + + it('should implement "doDestroy" method', () => { + expect(dataGridScope).to.have.property('doDestroy'); + expect(dataGridScope.doDestroy).to.be.a('function'); + }); + + it('should call "dispose" method when calling "doDestroy"', () => { + const dataGridMock = sinon.mock(dataGridScope['dataGrid']); + + dataGridMock.expects('destroy'); + dataGridScope.doDestroy(); + dataGridMock.verify(); + dataGridMock.restore(); + }); + + it('should call "attach" method when calling "render"', () => { + const widgetMock = sinon.mock(Widget); + + widgetMock.expects('attach'); + dataGridScope.render(); + widgetMock.verify(); + widgetMock.restore(); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/EventManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/EventManager.spec.ts new file mode 100644 index 0000000000..361d53a324 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/EventManager.spec.ts @@ -0,0 +1,130 @@ +/* + * Copyright 2018 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 * as sinon from 'sinon'; +import { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "./mock/modelStateMock"; +import EventManager from "@beakerx/tableDisplay/dataGrid/EventManager"; +import cellDataMock from "./mock/cellDataMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('EventManager', () => { + let dataGrid; + let eventManager; + let dataStore; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + eventManager = dataGrid.eventManager; + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should be an instance of EventManager', () => { + expect(eventManager).to.be.an.instanceof(EventManager); + }); + + it('should implement handleEvent method', () => { + expect(eventManager).to.have.property('handleEvent'); + expect(eventManager.handleEvent).to.be.a('Function'); + }); + + it('should implement destroy method', () => { + expect(eventManager).to.have.property('destroy'); + expect(eventManager.destroy).to.be.a('Function'); + }); + + it('should implement DOM event handlers', () => { + expect(eventManager).to.have.property('handleMouseDown'); + expect(eventManager).to.have.property('handleMouseOut'); + expect(eventManager).to.have.property('handleMouseWheel'); + expect(eventManager).to.have.property('handleKeyDown'); + expect(eventManager).to.have.property('handleDoubleClick'); + }); + + it('should implement DataGrid event handlers', () => { + expect(eventManager).to.have.property('handleHeaderClick'); + expect(eventManager).to.have.property('handleHeaderCellHover'); + }); + + it('should implement handleMouseDown event handler', () => { + expect(eventManager).to.have.property('handleMouseDown'); + expect(eventManager.handleMouseDown).to.be.a('Function'); + eventManager.handleMouseDown(new MouseEvent('mousedown', { buttons: 1 })); + expect(dataGrid.focused).to.be.true; + expect(dataGrid.node.classList.contains('bko-focused')).to.be.true; + }); + + it('should implement handleMouseOut event handler', () => { + expect(eventManager).to.have.property('handleMouseDown'); + expect(eventManager.handleMouseOut).to.be.a('Function'); + eventManager.handleMouseOut(new MouseEvent('mouseout')); + expect(dataGrid.focused).to.be.false; + expect(dataGrid.node.classList.contains('bko-focused')).to.be.false; + }); + + it('should implement handleMouseWheel event handler', () => { + const mouseWheelHandler = sinon.spy(); + + expect(eventManager).to.have.property('handleMouseWheel'); + expect(eventManager.handleMouseWheel).to.be.a('Function'); + expect(dataGrid.focused).to.be.false; + + eventManager.handleMouseWheel(new MouseEvent('mousewheel'), mouseWheelHandler); + expect(mouseWheelHandler.called).to.be.false; + + dataGrid.focused = true; + eventManager.handleMouseWheel(new MouseEvent('mousewheel'), mouseWheelHandler); + expect(mouseWheelHandler.called).to.be.true; + }); + + it('should implement handleKeyDown event handler', () => { + const highlighterStub = sinon.stub(eventManager, 'handleHighlighterKeyDown'); + const numStub = sinon.stub(eventManager, 'handleNumKeyDown'); + const arrowStub = sinon.stub(eventManager, 'handleArrowKeyDown'); + const columnStub = sinon.stub(dataGrid.columnManager, 'takeColumnByCell'); + + expect(eventManager).to.have.property('handleKeyDown'); + expect(eventManager.handleKeyDown).to.be.a('Function'); + + const event = new KeyboardEvent('keydown', { key: 'ArrowUp', code: 'ArrowUp' }); + + eventManager.handleKeyDown(event); + + expect(highlighterStub.calledOnce).to.be.true; + expect(numStub.calledOnce).to.be.true; + expect(arrowStub.calledOnce).to.be.true; + expect(columnStub.calledOnce).to.be.false + + dataGrid.cellFocusManager.setFocusedCell(cellDataMock); + eventManager.handleKeyDown(event); + + expect(highlighterStub.calledTwice).to.be.true; + expect(numStub.calledTwice).to.be.true; + expect(arrowStub.calledTwice).to.be.true; + expect(columnStub.calledOnce).to.be.true; + + highlighterStub.restore(); + numStub.restore(); + arrowStub.restore(); + columnStub.restore(); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/CellFocusManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellFocusManager.spec.ts new file mode 100644 index 0000000000..31de75a7c2 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellFocusManager.spec.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import CellFocusManager from "@beakerx/tableDisplay/dataGrid/cell/CellFocusManager"; +import cellDataMock from "../mock/cellDataMock"; +import cellConfigMock from "../mock/cellConfigMock"; +import { + DEFAULT_CELL_BACKGROUND, + FOCUSED_CELL_BACKGROUND +} from "@beakerx/tableDisplay/dataGrid/style/dataGridStyle"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('CellFocusManager', () => { + let dataGrid; + let dataStore; + let cellFocusManager; + let focusedCell = { ...cellDataMock }; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + cellFocusManager = dataGrid.cellFocusManager; + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should be an instance of CellFocusManager', () => { + expect(cellFocusManager).to.be.an.instanceof(CellFocusManager); + }); + + it('should have the focusedCellData property', () => { + expect(cellFocusManager).to.have.property('focusedCellData'); + }); + + it('should implement setFocusedCell method', () => { + expect(cellFocusManager).to.have.property('setFocusedCell'); + expect(cellFocusManager.setFocusedCell).to.be.a('Function'); + + cellFocusManager.setFocusedCell(focusedCell); + expect(cellFocusManager.focusedCellData).to.equal(focusedCell); + }); + + it('should implement getFocussedCellBackground method', () => { + expect(cellFocusManager).to.have.property('getFocussedCellBackground'); + expect(cellFocusManager.getFocussedCellBackground).to.be.a('Function'); + expect(cellFocusManager.getFocussedCellBackground(cellConfigMock)).to.equal(FOCUSED_CELL_BACKGROUND); + expect(cellFocusManager.getFocussedCellBackground({ ...cellConfigMock, column: 1 })).to.equal(DEFAULT_CELL_BACKGROUND); + }); + + it('should implement setFocusedCellByArrowKey method', () => { + expect(cellFocusManager).to.have.property('setFocusedCellByArrowKey'); + expect(cellFocusManager.setFocusedCellByArrowKey).to.be.a('Function'); + + cellFocusManager.setFocusedCellByArrowKey(39); // right arrow + expect(cellFocusManager.focusedCellData.column).to.equal(1); + expect(cellFocusManager.focusedCellData.row).to.equal(0); + expect(cellFocusManager.focusedCellData.type).to.equal(COLUMN_TYPES.body); + + cellFocusManager.setFocusedCellByArrowKey(40); // down arrow + expect(cellFocusManager.focusedCellData.column).to.equal(1); + expect(cellFocusManager.focusedCellData.row).to.equal(1); + expect(cellFocusManager.focusedCellData.type).to.equal(COLUMN_TYPES.body); + + cellFocusManager.setFocusedCellByArrowKey(37); // left arrow + expect(cellFocusManager.focusedCellData.column).to.equal(0); + expect(cellFocusManager.focusedCellData.row).to.equal(1); + expect(cellFocusManager.focusedCellData.type).to.equal(COLUMN_TYPES.body); + + cellFocusManager.setFocusedCellByArrowKey(38); // up arrow + expect(cellFocusManager.focusedCellData.column).to.equal(0); + expect(cellFocusManager.focusedCellData.row).to.equal(0); + expect(cellFocusManager.focusedCellData.type).to.equal(COLUMN_TYPES.body); + + cellFocusManager.setFocusedCellByArrowKey(37); // left arrow + expect(cellFocusManager.focusedCellData.column).to.equal(0); + expect(cellFocusManager.focusedCellData.row).to.equal(0); + expect(cellFocusManager.focusedCellData.type).to.equal(COLUMN_TYPES.index); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/CellManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellManager.spec.ts new file mode 100644 index 0000000000..7b9ebd8acc --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellManager.spec.ts @@ -0,0 +1,122 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import CellManager from "@beakerx/tableDisplay/dataGrid/cell/CellManager"; +import cellDataMock from "../mock/cellDataMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('CellManager', () => { + let dataGrid; + let dataStore; + let cellManager; + let cellSelectionManager; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + cellManager = dataGrid.cellManager; + cellSelectionManager = dataGrid.cellSelectionManager; + cellSelectionManager.setStartCell(cellDataMock); + cellSelectionManager.setEndCell({ ...cellDataMock, column: 1 }); + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should be an instance of CellManager', () => { + expect(cellManager).to.be.an.instanceof(CellManager); + }); + + it('should implement getSelectedCells method', () => { + expect(cellManager).to.have.property('getSelectedCells'); + expect(cellManager.getSelectedCells).to.be.a('Function'); + expect(cellManager.getSelectedCells()).to.have.length(2); + }); + + it('should implement getAllCells method', () => { + const allCells = cellManager.getAllCells(); + + expect(cellManager).to.have.property('getAllCells'); + expect(cellManager.getAllCells).to.be.a('Function'); + expect(allCells).to.have.length(3); + expect(allCells[0][0]).to.equal('index'); + expect(allCells[0][1]).to.equal('test'); + }); + + it('should implement getCells method', () => { + expect(cellManager).to.have.property('getCells'); + expect(cellManager.getCells).to.be.a('Function'); + const rowsRange = cellSelectionManager.getRowsRangeCells(); + const columnsRange = cellSelectionManager.getColumnsRangeCells(); + + expect(cellManager.getCells(rowsRange, columnsRange)).to.have.length(2); + }); + + it('should implement copyToClipboard method', () => { + expect(cellManager).to.have.property('copyToClipboard'); + expect(cellManager.copyToClipboard).to.be.a('Function'); + }); + + it('should implement CSVDownload method', () => { + expect(cellManager).to.have.property('CSVDownload'); + expect(cellManager.CSVDownload).to.be.a('Function'); + }); + + it('should implement createCellConfig method', () => { + expect(cellManager).to.have.property('createCellConfig'); + expect(cellManager.createCellConfig).to.be.a('Function'); + + const cellConfig = cellManager.createCellConfig({ + row: 1, + column: 1, + value: 2, + region: 'body' + }); + + expect(cellConfig.row).to.equal(1); + expect(cellConfig.column).to.equal(1); + expect(cellConfig.value).to.equal(2); + expect(cellConfig.region).to.equal('body'); + + expect(cellConfig).to.have.property('x'); + expect(cellConfig).to.have.property('y'); + expect(cellConfig).to.have.property('width'); + expect(cellConfig).to.have.property('height'); + }); + + it('should implement exportCellsTo method', () => { + expect(cellManager).to.have.property('exportCellsTo'); + expect(cellManager.exportCellsTo).to.be.a('Function'); + const cells = cellManager.getSelectedCells(); + const resultCsv = `"test","column"\n"1","2"\n`; + const resultTabs = `test\tcolumn\n1\t2\n`; + + expect(cellManager.exportCellsTo(cells, 'csv')).to.equal(resultCsv); + expect(cellManager.exportCellsTo(cells, 'tabs')).to.equal(resultTabs); + }); + + it('should implement getCSVFromCells method', () => { + expect(cellManager).to.have.property('getCSVFromCells'); + expect(cellManager.getCSVFromCells).to.be.a('Function'); + const result = `"test","column"\n"1","2"\n`; + + expect(cellManager.getCSVFromCells(true)).to.equal(result); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/CellRendererFactory.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellRendererFactory.spec.ts new file mode 100644 index 0000000000..4d0fcaa272 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellRendererFactory.spec.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 { expect } from 'chai'; +import { CellRendererFactory } from "@beakerx/tableDisplay/dataGrid/cell/CellRendererFactory"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import { TextRenderer } from "@phosphor/datagrid"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('CellRendererFactory', () => { + let dataGrid; + let cellRendererFactory; + let dataStore; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + cellRendererFactory = new CellRendererFactory(dataGrid); + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should implement getRenderer method', () => { + expect(cellRendererFactory).to.have.property('getRenderer'); + }); + + it('should return CellRenderer', () => { + expect(cellRendererFactory.getRenderer()).to.be.an.instanceof(TextRenderer); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/CellSelectionManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellSelectionManager.spec.ts new file mode 100644 index 0000000000..e73d3b47fe --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellSelectionManager.spec.ts @@ -0,0 +1,132 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import CellSelectionManager from "@beakerx/tableDisplay/dataGrid/cell/CellSelectionManager"; +import {ICellData} from "@beakerx/tableDisplay/dataGrid/interface/ICell"; +import cellConfigMock from "../mock/cellConfigMock"; +import cellDataMock from "../mock/cellDataMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('CellSelectionManager', () => { + let dataGrid; + let dataStore; + let cellSelectionManager; + let startCell: ICellData = { ...cellDataMock, type: COLUMN_TYPES.index }; + let endCell: ICellData = { ...cellDataMock, row: 2, column: 3 }; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + cellSelectionManager = dataGrid.cellSelectionManager; + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should be an instance of CellSelectionManager', () => { + expect(cellSelectionManager).to.be.an.instanceof(CellSelectionManager); + }); + + it('should implement setStartCell method', () => { + expect(cellSelectionManager).to.have.property('setStartCell'); + expect(cellSelectionManager.setStartCell).to.be.a('Function'); + + cellSelectionManager.setStartCell(startCell); + expect(cellSelectionManager.startCellData).to.equal(startCell); + }); + + it('should implement setEndCell method', () => { + expect(cellSelectionManager).to.have.property('setEndCell'); + expect(cellSelectionManager.setEndCell).to.be.a('Function'); + + cellSelectionManager.setEndCell(endCell); + expect(cellSelectionManager.endCellData).to.equal(endCell); + }); + + it('should implement getColumnsRangeCells method', () => { + expect(cellSelectionManager).to.have.property('getColumnsRangeCells'); + expect(cellSelectionManager.getColumnsRangeCells).to.be.a('Function'); + + const columnRange = cellSelectionManager.getColumnsRangeCells(); + expect(columnRange.startCell).to.equal(startCell); + expect(columnRange.endCell).to.equal(endCell); + }); + + it('should implement getRowsRangeCells method', () => { + expect(cellSelectionManager).to.have.property('getRowsRangeCells'); + expect(cellSelectionManager.getRowsRangeCells).to.be.a('Function'); + + const rowsRange = cellSelectionManager.getRowsRangeCells(); + expect(rowsRange.startCell).to.equal(startCell); + expect(rowsRange.endCell).to.equal(endCell); + }); + + it('should implement isBetweenRows method', () => { + expect(cellSelectionManager).to.have.property('isBetweenRows'); + expect(cellSelectionManager.isBetweenRows).to.be.a('Function'); + + expect(cellSelectionManager.isBetweenRows(cellConfigMock)).to.be.true; + expect(cellSelectionManager.isBetweenRows({ ...cellConfigMock, row: 3 })).to.be.false; + }); + + it('should implement isBetweenColumns method', () => { + expect(cellSelectionManager).to.have.property('isBetweenColumns'); + expect(cellSelectionManager.isBetweenColumns).to.be.a('Function'); + + expect(cellSelectionManager.isBetweenColumns(cellConfigMock)).to.be.true; + expect(cellSelectionManager.isBetweenColumns({ ...cellConfigMock, column: 4 })).to.be.false; + }); + + it('should implement enable method', () => { + expect(cellSelectionManager).to.have.property('enable'); + expect(cellSelectionManager.enable).to.be.a('Function'); + + cellSelectionManager.enable(); + expect(cellSelectionManager.enabled).to.be.true; + }); + + it('should implement isSelected method', () => { + expect(cellSelectionManager).to.have.property('isSelected'); + expect(cellSelectionManager.isSelected).to.be.a('Function'); + + expect(cellSelectionManager.isSelected(cellConfigMock)).to.be.true; + expect(cellSelectionManager.isSelected({ ...cellConfigMock, column: 4 })).to.be.false; + }); + + it('should implement getBackgroundColor method', () => { + expect(cellSelectionManager).to.have.property('getBackgroundColor'); + expect(cellSelectionManager.getBackgroundColor).to.be.a('Function'); + + expect(cellSelectionManager.getBackgroundColor(cellConfigMock)).to.equal(cellSelectionManager.selectedCellColor); + expect(cellSelectionManager.getBackgroundColor({ ...cellConfigMock, column: 4 })).to.equal(''); + }); + + it('should implement clear method', () => { + expect(cellSelectionManager).to.have.property('clear'); + expect(cellSelectionManager.clear).to.be.a('Function'); + + cellSelectionManager.clear(); + expect(cellSelectionManager.enabled).to.be.false; + expect(cellSelectionManager.startCellData).to.be.null; + expect(cellSelectionManager.endCellData).to.be.null; + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/CellTooltip.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellTooltip.spec.ts new file mode 100644 index 0000000000..7a27df0218 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellTooltip.spec.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import CellTooltip from "@beakerx/tableDisplay/dataGrid/cell/CellTooltip"; + +describe('CellTooltip', () => { + const tooltip = new CellTooltip('test', document.body); + + it('should return true', () => { + expect(tooltip).to.be.an.instanceof(CellTooltip); + }); + + it('should implement show method', () => { + tooltip.show(1,2); + + expect(tooltip.node.style.left).to.equal('1px'); + expect(tooltip.node.style.top).to.equal('2px'); + + tooltip.show(3,4); + + expect(tooltip.node.style.left).to.equal('1px'); + expect(tooltip.node.style.top).to.equal('2px'); + }); + + it('should implement hide method', () => { + tooltip.hide(); + + expect(tooltip.node.classList.contains('visible')).to.be.false; + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/CellTooltipManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellTooltipManager.spec.ts new file mode 100644 index 0000000000..31b5ca7c13 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/CellTooltipManager.spec.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import CellTooltipManager from "@beakerx/tableDisplay/dataGrid/cell/CellTooltipManager"; +import modelStateMock from "../mock/modelStateMock"; +import {BeakerxDataGrid} from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import CellTooltip from "@beakerx/tableDisplay/dataGrid/cell/CellTooltip"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('CellTooltipManager', () => { + let dataGrid; + let dataStore; + let cellTooltipManager; + let tooltips = [['test', 'test2'], ['test3', 'test4']]; + + before(() => { + dataStore = createStore({ ...modelStateMock, tooltips }); + dataGrid = new BeakerxDataGrid({}, dataStore); + cellTooltipManager = dataGrid.cellTooltipManager; + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should be an instance of CellTooltipManager', () => { + expect(cellTooltipManager).to.be.an.instanceof(CellTooltipManager); + }); + + it('should have the tooltips property', () => { + expect(cellTooltipManager).to.have.property('tooltips'); + expect(cellTooltipManager.tooltips).to.equal(tooltips); + }); + + it('should have the tooltip property', () => { + expect(cellTooltipManager).to.have.property('tooltip'); + expect(cellTooltipManager.tooltip).to.be.an.instanceof(CellTooltip); + }); + + it('should implement the handleCellHovered method', () => { + expect(cellTooltipManager).to.have.property('handleCellHovered'); + expect(cellTooltipManager.handleCellHovered).to.be.a('Function'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/cell/DataGridCell.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/cell/DataGridCell.spec.ts new file mode 100644 index 0000000000..3e8974057e --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/cell/DataGridCell.spec.ts @@ -0,0 +1,33 @@ +/* + * 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 { expect } from 'chai'; +import { CellRenderer } from "@phosphor/datagrid"; +import DataGridCell from "@beakerx/tableDisplay/dataGrid/cell/DataGridCell"; +import CellConfigMock from "../mock/cellConfigMock"; + +describe('DataGridCell', () => { + describe('isHeaderCell', () => { + it('should return true', () => { + expect(DataGridCell.isHeaderCell({ ...CellConfigMock, region: 'column-header' })).to.be.true; + }); + + it('should return false', () => { + let mock: CellRenderer.ICellConfig = { ...CellConfigMock }; + expect(DataGridCell.isHeaderCell(mock)).to.be.false; + }); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/column/ColumnFilter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/column/ColumnFilter.spec.ts new file mode 100644 index 0000000000..f9410334bd --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/column/ColumnFilter.spec.ts @@ -0,0 +1,72 @@ +/* + * 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 { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import DataGridColumn from '@beakerx/tableDisplay/dataGrid/column/DataGridColumn'; +import modelStateMock from "../mock/modelStateMock"; +import ColumnFilter from "@beakerx/tableDisplay/dataGrid/column/ColumnFilter"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +declare var require: Function; + +describe('ColumnFilter', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const columnManager = dataGrid.columnManager; + const bodyDataGridColumn = new DataGridColumn({ + type: COLUMN_TYPES.body, + index: 0, + name: 'index', + menuOptions: { x: 0, y: 0, height: 20, width: 20 } + }, dataGrid, columnManager); + const columnFilter = bodyDataGridColumn.columnFilter; + + it('should be an instance of ColumnFilter', () => { + expect(columnFilter).to.be.an.instanceof(ColumnFilter); + }); + + it('should have HTML node properties', () => { + expect(columnFilter).to.have.property('filterNode'); + expect(columnFilter.filterNode).to.be.an.instanceof(HTMLElement); + expect(columnFilter).to.have.property('filterIcon'); + expect(columnFilter.filterIcon).to.be.an.instanceof(HTMLSpanElement); + expect(columnFilter).to.have.property('clearIcon'); + expect(columnFilter.clearIcon).to.be.an.instanceof(HTMLSpanElement); + expect(columnFilter).to.have.property('filterInput'); + expect(columnFilter.filterInput).to.be.an.instanceof(HTMLInputElement); + }); + + it('should show the filter input', () => { + columnFilter.showFilterInput(false); + expect(columnFilter.filterNode.style.visibility).to.equal('visible'); + expect(columnFilter.filterIcon.classList.contains('fa-filter')).to.be.true; + expect(columnFilter.useSearch).to.be.false; + }); + + it('should show the search input', () => { + columnFilter.showSearchInput(false); + expect(columnFilter.filterNode.style.visibility).to.equal('visible'); + expect(columnFilter.filterIcon.classList.contains('fa-search')).to.be.true; + expect(columnFilter.useSearch).to.be.true; + }); + + it('should hide the input', () => { + columnFilter.hideInput(); + expect(columnFilter.filterNode.style.visibility).to.equal('hidden'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/column/ColumnManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/column/ColumnManager.spec.ts new file mode 100644 index 0000000000..437a397d61 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/column/ColumnManager.spec.ts @@ -0,0 +1,69 @@ +/* + * 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 * as sinon from 'sinon'; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import {expect} from "chai"; +import {BeakerxDataGrid} from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import ColumnManager from "@beakerx/tableDisplay/dataGrid/column/ColumnManager"; +import cellConfigMock from "../mock/cellConfigMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('ColumnManager', () => { + let dataGrid; + let dataStore; + let columnManager; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + columnManager = dataGrid.columnManager; + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should create index column', () => { + expect(columnManager.columns).to.have.property(`${COLUMN_TYPES.index}`); + expect(columnManager.columns[COLUMN_TYPES.index]).to.have.length(1); + expect(columnManager.columns[COLUMN_TYPES.index][0]).to.be.an.instanceof(DataGridColumn); + }); + + it('should create body column', () => { + expect(columnManager.columns).to.have.property(`${COLUMN_TYPES.body}`); + expect(columnManager.columns[COLUMN_TYPES.body]).to.have.length(2); + expect(columnManager.columns[COLUMN_TYPES.body][0]).to.be.an.instanceof(DataGridColumn); + }); + + it('should return column', () => { + expect(columnManager.getColumn(cellConfigMock)).to.equal(columnManager.columns[COLUMN_TYPES.body][0]); + }); + + it('should return column by column name', () => { + expect(columnManager.getColumnByName('test')).to.equal(columnManager.columns[COLUMN_TYPES.body][0]); + }); + + it('should implement destroy method', () => { + const destroyStub = sinon.stub(columnManager, 'destroyAllColumns'); + + columnManager.destroy(); + expect(destroyStub.calledOnce).to.be.true; + destroyStub.restore(); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/column/DataGridColumn.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/column/DataGridColumn.spec.ts new file mode 100644 index 0000000000..10413f57c7 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/column/DataGridColumn.spec.ts @@ -0,0 +1,100 @@ +/* + * 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 * as sinon from 'sinon'; +import { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import DataGridColumn from '@beakerx/tableDisplay/dataGrid/column/DataGridColumn'; +import ColumnMenu from "@beakerx/tableDisplay/dataGrid/headerMenu/ColumnMenu"; +import IndexMenu from "@beakerx/tableDisplay/dataGrid/headerMenu/IndexMenu"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +declare var require: Function; + +describe('DataGridColumn', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const columnManager = dataGrid.columnManager; + + describe('DataGridColumn.type === "body"', () => { + const bodyDataGridColumn = columnManager.bodyColumns[0]; + + it('should have the body column type set', () => { + expect(bodyDataGridColumn.type).to.equal(COLUMN_TYPES.body); + }); + + it('should create the ColumnMenu', () => { + expect(bodyDataGridColumn.menu).to.be.an.instanceof(ColumnMenu); + }); + + it('should change the trigger state', () => { + bodyDataGridColumn.handleHeaderCellHovered( + dataGrid, { type: COLUMN_TYPES.body, column: 0, row: 0, delta: 0, offset: 10, offsetTop: 10 } + ); + + expect(bodyDataGridColumn.menu['triggerNode'].style.visibility).to.equal('visible'); + }); + + it('should implement move method', () => { + expect(bodyDataGridColumn).to.have.property('move'); + expect(bodyDataGridColumn.move).to.be.a('Function'); + + bodyDataGridColumn.move(1); + expect(bodyDataGridColumn.getPosition()).to.equal(1); + + bodyDataGridColumn.hide(); + expect(bodyDataGridColumn.getPosition()).to.equal(1); + expect(columnManager.bodyColumns[1].getPosition()).to.equal(0); + + bodyDataGridColumn.show(); + bodyDataGridColumn.move(0); + + expect(bodyDataGridColumn.getPosition()).to.equal(0); + }); + + it('should call toggleVisibility', () => { + const stub = sinon.stub(bodyDataGridColumn, 'toggleVisibility'); + + bodyDataGridColumn.hide(); + bodyDataGridColumn.show(); + + expect(stub.calledTwice).to.be.true; + stub.restore(); + }); + }); + + describe('DataGridColumn.type === "index"', () => { + const indexDataGridColumn = columnManager.indexColumns[0]; + + it('should have the index column type set', () => { + expect(indexDataGridColumn.type).to.equal(COLUMN_TYPES.index); + }); + + it('should create the ColumnMenu', () => { + expect(indexDataGridColumn.menu).to.be.an.instanceof(IndexMenu); + }); + + it('should change the trigger state', () => { + indexDataGridColumn.handleHeaderCellHovered( + dataGrid, { type: COLUMN_TYPES.index, column: 0, row: 0, delta: 0, offset: 0, offsetTop: 0 } + ); + expect(indexDataGridColumn.menu['triggerNode'].style.visibility).to.equal('visible'); + }); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/column/columnAlignment.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/column/columnAlignment.spec.ts new file mode 100644 index 0000000000..2bc2d7dd2c --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/column/columnAlignment.spec.ts @@ -0,0 +1,41 @@ +/* + * 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 { expect } from 'chai'; +import { ALL_TYPES } from "@beakerx/tableDisplay/dataGrid/dataTypes"; +import { + DEFAULT_ALIGNMENT, + getAlignmentByType +} from "@beakerx/tableDisplay/dataGrid/column/columnAlignment"; + +describe('columnAlignment', () => { + it('should return default alignment', () => { + expect(getAlignmentByType(ALL_TYPES.string)).to.equal(DEFAULT_ALIGNMENT); + }); + + it('should return left alignment for string type', () => { + expect(getAlignmentByType(ALL_TYPES.html)).to.equal('left'); + }); + + it('should return right alignment for integer and double type', () => { + expect(getAlignmentByType(ALL_TYPES.integer)).to.equal('right'); + expect(getAlignmentByType(ALL_TYPES.double)).to.equal('right'); + }); + + it('should return center alignment for integer type', () => { + expect(getAlignmentByType(ALL_TYPES.datetime)).to.equal('center'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/DataGridContextMenu.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/DataGridContextMenu.spec.ts new file mode 100644 index 0000000000..243ba05d50 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/DataGridContextMenu.spec.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import {ContextMenu, Widget} from '@phosphor/widgets'; +import modelStateMock from "../mock/modelStateMock"; +import { DataGridScope } from '@beakerx/tableDisplay/dataGrid/DataGridScope'; +import DataGridContextMenu from "@beakerx/tableDisplay/dataGrid/contextMenu/DataGridContextMenu"; + +describe('DataGridContextMenu', () => { + let dataGridScope; + let contextMenu; + + before(() => { + const scopeOptions = { + element: document.createElement('div'), + widgetView: new Widget({}), + widgetModel: new Widget({}), + data: modelStateMock + }; + + dataGridScope = new DataGridScope(scopeOptions); + contextMenu = dataGridScope.contextMenu; + }); + + after(() => { + dataGridScope.doDestroy(); + }); + + it('should be an instance of DataGridContextMenu', () => { + expect(contextMenu).to.be.an.instanceof(DataGridContextMenu); + }); + + it('should have the contextMenu property', () => { + expect(contextMenu).to.have.property('contextMenu'); + expect(contextMenu.contextMenu).to.be.an.instanceof(ContextMenu); + }); + + it('should implement buildMenu method', () => { + expect(contextMenu).to.have.property('buildMenu'); + expect(contextMenu.buildMenu).to.be.a('Function'); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems.spec.ts new file mode 100644 index 0000000000..aa76cbd9ca --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems.spec.ts @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import {Widget} from '@phosphor/widgets'; +import modelStateMock from "../mock/modelStateMock"; +import { DataGridScope } from '@beakerx/tableDisplay/dataGrid/DataGridScope'; +import createCellContextMenuItems from "@beakerx/tableDisplay/dataGrid/contextMenu/createCellContextMenuItems"; + +describe('createCellContextMenuItems', () => { + let dataGridScope; + let contextMenu; + + before(() => { + const scopeOptions = { + element: document.createElement('div'), + widgetView: new Widget({}), + widgetModel: { model_id: '123-123-123' }, + data: { + ...modelStateMock, + contextMenuItems: ['test', 'test2'], + contextMenuTags: { run: 'run', run2: 'run2'} + } + }; + + dataGridScope = new DataGridScope(scopeOptions); + contextMenu = dataGridScope.contextMenu; + }); + + after(() => { + dataGridScope.doDestroy(); + }); + + it('should be a Function', () => { + expect(createCellContextMenuItems).to.be.a('Function'); + }); + + it('should create the header context menu items', () => { + let items = createCellContextMenuItems(dataGridScope.dataGrid, contextMenu); + + expect(items).to.have.length(4); + expect(items[0]).to.have.property('title'); + expect(items[0].title).to.equal('test'); + expect(items[0]).to.have.property('id'); + expect(items[0].id).to.equal('test_wrap_123-123-123'); + expect(items[0]).to.have.property('selector'); + expect(items[0].selector).to.equal('#wrap_123-123-123 canvas'); + expect(items[2]).to.have.property('title'); + expect(items[2].title).to.equal('run'); + expect(items[2]).to.have.property('id'); + expect(items[2].id).to.equal('run_wrap_123-123-123'); + expect(items[2]).to.have.property('selector'); + expect(items[2].selector).to.equal('#wrap_123-123-123 canvas'); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems.spec.ts new file mode 100644 index 0000000000..7fcb22baab --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems.spec.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import {Widget} from '@phosphor/widgets'; +import modelStateMock from "../mock/modelStateMock"; +import { DataGridScope } from '@beakerx/tableDisplay/dataGrid/DataGridScope'; +import createHeaderContextMenuItems from "@beakerx/tableDisplay/dataGrid/contextMenu/createHeaderContextMenuItems"; + +describe('createHeaderContextMenuItems', () => { + let dataGridScope; + let contextMenu; + + before(() => { + const scopeOptions = { + element: document.createElement('div'), + widgetView: new Widget({}), + widgetModel: { model_id: '123-123-123' }, + data: modelStateMock + }; + + dataGridScope = new DataGridScope(scopeOptions); + contextMenu = dataGridScope.contextMenu; + }); + + after(() => { + dataGridScope.doDestroy(); + }); + + it('should be a Function', () => { + expect(createHeaderContextMenuItems).to.be.a('Function'); + }); + + it('should create the header context menu items', () => { + let items = createHeaderContextMenuItems(dataGridScope.dataGrid, contextMenu); + + expect(items).to.have.length(2); + expect(items[0]).to.have.property('title'); + expect(items[0].title).to.equal('vertical headers'); + expect(items[0]).to.have.property('id'); + expect(items[0].id).to.equal('wrap_123-123-123_verticalHeaders'); + expect(items[0]).to.have.property('selector'); + expect(items[0].selector).to.equal('#wrap_123-123-123 canvas'); + expect(items[0]).to.have.property('isVisible'); + expect(items[0].isVisible).to.be.a('Function'); + + contextMenu.event = new MouseEvent('contextmenu', { clientX: 50, clientY: 0 }); + expect(items[0].isVisible({})).to.be.true; + + expect(items[1]).to.have.property('title'); + expect(items[1].title).to.equal('horizontal headers'); + expect(items[1]).to.have.property('id'); + expect(items[1].id).to.equal('wrap_123-123-123_horizontalHeaders'); + expect(items[1]).to.have.property('selector'); + expect(items[1].selector).to.equal('#wrap_123-123-123 canvas'); + expect(items[1]).to.have.property('isVisible'); + expect(items[1].isVisible).to.be.a('Function'); + expect(items[1].isVisible({})).to.be.false; + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/dataGridHelpers.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/dataGridHelpers.spec.ts new file mode 100644 index 0000000000..6662dce5cf --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/dataGridHelpers.spec.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 { expect } from 'chai'; +import { DataGridHelpers } from '@beakerx/tableDisplay/dataGrid/dataGridHelpers'; + +describe('DataGridHelpers', () => { + it('should export escapeHTML helper function', () => { + expect(DataGridHelpers.escapeHTML).to.be.a('function'); + }); + + it('should escape html characters', () => { + const testObject = {}; + + expect(DataGridHelpers.escapeHTML('&test<>"Works"Ok/<>')) + .to.equal('&test<>"Works"Ok/<>'); + expect(DataGridHelpers.escapeHTML(testObject)).to.equal(testObject); + }); + + it('should export truncateString helper function', () => { + expect(DataGridHelpers.truncateString).to.be.a('function'); + }); + + it('should truncate string', () => { + const testObject = {}; + + expect(DataGridHelpers.truncateString('testString')).to.equal('testString'); + expect(DataGridHelpers.truncateString('testString', 2)).to.equal('te...'); + expect(DataGridHelpers.truncateString('testString', 0)).to.equal('...'); + expect(DataGridHelpers.truncateString(testObject)).to.equal(testObject); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/dataTypes.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/dataTypes.spec.ts new file mode 100644 index 0000000000..faccb0552c --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/dataTypes.spec.ts @@ -0,0 +1,78 @@ +/* + * 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 { expect } from 'chai'; +import {getTypeByName, getDisplayType, ALL_TYPES} from '@beakerx/tableDisplay/dataGrid/dataTypes'; + +describe('dataTypes', () => { + describe('getTypeByName', () => { + it('should be a function', () => { + expect(getTypeByName).to.be.a('function'); + }); + + it('should return number', () => { + expect(getTypeByName('integer')).to.be.a('number'); + }); + + it('should return 0 as default', () => { + expect(getTypeByName('some dummy value')).to.equal(0); + }); + + it('should return proper type number', () => { + expect(getTypeByName('string')).to.equal(0); + expect(getTypeByName('integer')).to.equal(1); + expect(getTypeByName('formatted integer')).to.equal(2); + expect(getTypeByName('double')).to.equal(3); + expect(getTypeByName('double with precision')).to.equal(4); + expect(getTypeByName('exponential 5')).to.equal(6); + expect(getTypeByName('exponential 15')).to.equal(7); + expect(getTypeByName('datetime')).to.equal(8); + expect(getTypeByName('boolean')).to.equal(9); + expect(getTypeByName('html')).to.equal(10); + expect(getTypeByName('int64')).to.equal(11); + expect(getTypeByName('time')).to.equal(12); + }); + }); + + describe('getDisplayType', () => { + it('should be a function', () => { + expect(getDisplayType).to.be.a('function'); + }); + + it('should return number', () => { + expect(getDisplayType(65)).to.be.a('number'); + }); + + it('should return 0 by default', () => { + expect(getDisplayType(45)).to.equal(0); + }); + + it('should return display type as number', () => { + expect(getDisplayType(ALL_TYPES.string)).to.equal(0); + expect(getDisplayType(ALL_TYPES.integer)).to.equal(2); + expect(getDisplayType(ALL_TYPES.int64)).to.equal(0); + expect(getDisplayType(ALL_TYPES['formatted integer'])).to.equal(0); + expect(getDisplayType(ALL_TYPES['exponential 5'])).to.equal(0); + expect(getDisplayType(ALL_TYPES['exponential 15'])).to.equal(0); + expect(getDisplayType(ALL_TYPES.html)).to.equal(0); + expect(getDisplayType(ALL_TYPES.boolean)).to.equal(0); + expect(getDisplayType(ALL_TYPES.double)).to.equal('4.3'); + expect(getDisplayType(ALL_TYPES['double with precision'])).to.equal(0); + expect(getDisplayType(ALL_TYPES.datetime)).to.equal(8); + expect(getDisplayType(ALL_TYPES.time)).to.equal(8); + }); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/BkoMenu.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/BkoMenu.spec.ts new file mode 100644 index 0000000000..f94bce1de5 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/BkoMenu.spec.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import { Menu } from '@phosphor/widgets'; +import BkoMenu from '@beakerx/tableDisplay/dataGrid/headerMenu/BkoMenu'; +import {CommandRegistry} from '@phosphor/commands'; + +describe('BkoMenu', () => { + let bkoMenu; + + before(() => { + bkoMenu = new BkoMenu({ commands: new CommandRegistry() }); + }); + + after(() => { + bkoMenu.dispose(); + }); + + it('should be an instance of Menu', () => { + expect(bkoMenu).to.be.an.instanceof(Menu); + }); + + it('should implement the triggerActiveItem method', () => { + expect(bkoMenu).to.have.property('triggerActiveItem'); + expect(bkoMenu.triggerActiveItem).to.be.a('Function'); + }); + + it('should implement the close method', () => { + expect(bkoMenu).to.have.property('close'); + expect(bkoMenu.close).to.be.a('Function'); + }); + + it('should implement the onBeforeAttach method', () => { + expect(bkoMenu).to.have.property('onBeforeAttach'); + expect(bkoMenu.onBeforeAttach).to.be.a('Function'); + }); + + it('should implement the onActivateRequest method', () => { + expect(bkoMenu).to.have.property('onActivateRequest'); + expect(bkoMenu.onActivateRequest).to.be.a('Function'); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/ColumnMenu.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/ColumnMenu.spec.ts new file mode 100644 index 0000000000..7f9c79ee73 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/ColumnMenu.spec.ts @@ -0,0 +1,96 @@ +/* + * 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 * as sinon from 'sinon'; +import { expect } from 'chai'; +import { Menu } from '@phosphor/widgets'; +import ColumnMenu from '@beakerx/tableDisplay/dataGrid/headerMenu/ColumnMenu'; +import { createColumnMenuItems } from '@beakerx/tableDisplay/dataGrid/headerMenu/createColumnMenuItems'; +import HeaderMenu from '@beakerx/tableDisplay/dataGrid/headerMenu/HeaderMenu'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import menuOptionsMock from "../mock/menuOptionsMock"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('ColumnMenu', () => { + let columnMenu; + let column; + let dataGrid; + let dataStore; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + column = new DataGridColumn({ + index: 0, + type: COLUMN_TYPES.body, + name: 'test', + menuOptions: menuOptionsMock + }, dataGrid, dataGrid.columnManager); + + columnMenu = column.menu; + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should be an instance of HeaderMenu', () => { + expect(columnMenu).to.be.an.instanceof(HeaderMenu); + }); + + it('should implement buildMenu method', () => { + expect(columnMenu).to.have.property('buildMenu'); + }); + + it('should create the triggerNode HTMLElement', () => { + expect(columnMenu).to.have.property('triggerNode'); + expect(columnMenu['triggerNode']).to.be.an.instanceof(HTMLElement); + }); + + it('should create the menu property instance of PhosphorJS Menu', () => { + expect(columnMenu).to.have.property('menu'); + expect(columnMenu['menu']).to.be.an.instanceof(Menu); + }); + + it('should create index menu items', () => { + let items = createColumnMenuItems(column); + + expect(columnMenu['menu'].items).to.have.length.gte(items.length); + }); + + it('should call the Menu.open method', () => { + const stub = sinon.stub(columnMenu['menu'], 'open'); + + columnMenu.open(); + columnMenu.toggleMenu(); + columnMenu.toggleMenu(); + expect(stub.calledTwice).to.be.true; + + stub.restore(); + }); + + it('should call the createItems method', () => { + const stub = sinon.stub(columnMenu, 'createItems'); + + columnMenu['buildMenu'](); + expect(stub.calledOnce).to.be.true; + + stub.restore(); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/IndexMenu.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/IndexMenu.spec.ts new file mode 100644 index 0000000000..ec73bf7650 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/IndexMenu.spec.ts @@ -0,0 +1,96 @@ +/* + * 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 * as sinon from 'sinon'; +import { expect } from 'chai'; +import { Menu } from '@phosphor/widgets'; +import IndexMenu from '@beakerx/tableDisplay/dataGrid/headerMenu/IndexMenu'; +import { createIndexMenuItems } from '@beakerx/tableDisplay/dataGrid/headerMenu/createIndexMenuItems'; +import HeaderMenu from '@beakerx/tableDisplay/dataGrid/headerMenu/HeaderMenu'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import menuOptionsMock from "../mock/menuOptionsMock"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('IndexMenu', () => { + let dataGrid; + let dataStore; + let indexMenu; + let column; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + column = new DataGridColumn({ + index: 0, + type: COLUMN_TYPES.index, + name: 'index', + menuOptions: menuOptionsMock + }, dataGrid, dataGrid.columnManager); + indexMenu = new IndexMenu(column, menuOptionsMock); + }); + + after(() => { + dataGrid.destroy(); + }); + + + it('should be an instance of HeaderMenu', () => { + expect(indexMenu).to.be.an.instanceof(HeaderMenu); + }); + + it('should implement buildMenu method', () => { + expect(indexMenu).to.have.property('buildMenu'); + }); + + it('should create the triggerNode HTMLElement', () => { + expect(indexMenu).to.have.property('triggerNode'); + expect(indexMenu['triggerNode']).to.be.an.instanceof(HTMLElement); + }); + + it('should create the menu property instance of PhosphorJS Menu', () => { + expect(indexMenu).to.have.property('menu'); + expect(indexMenu['menu']).to.be.an.instanceof(Menu); + }); + + it('should create index menu items', () => { + let items = createIndexMenuItems(column); + + expect(indexMenu['menu'].items).to.have.length.gte(items.length); + }); + + it('should call the Menu.open method', () => { + const stub = sinon.stub(indexMenu['menu'], 'open'); + + indexMenu.open(); + indexMenu.toggleMenu(); + indexMenu.toggleMenu(); + expect(stub.calledTwice).to.be.true; + + stub.restore(); + }); + + it('should call the createItems method', () => { + const stub = sinon.stub(indexMenu, 'createItems'); + + indexMenu.buildMenu(); + expect(stub.calledOnce).to.be.true; + + stub.restore(); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createColumnMenuItems.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createColumnMenuItems.spec.ts new file mode 100644 index 0000000000..2522e5b181 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createColumnMenuItems.spec.ts @@ -0,0 +1,53 @@ +/* + * 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 { expect } from 'chai'; +import { createColumnMenuItems } from '@beakerx/tableDisplay/dataGrid/headerMenu/createColumnMenuItems'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import menuOptionsMock from "../mock/menuOptionsMock"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('createColumnMenuItems', () => { + let dataGrid; + let dataStore; + let column; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + column = new DataGridColumn({ + index: 0, + type: COLUMN_TYPES.index, + name: 'index', + menuOptions: menuOptionsMock + }, dataGrid, dataGrid.columnManager); + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should create column menu items', () => { + let formatMenuItems = createColumnMenuItems(column); + + expect(formatMenuItems).to.be.an.instanceof(Array); + expect(formatMenuItems).to.have.length(17); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createFormatMenuItems.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createFormatMenuItems.spec.ts new file mode 100644 index 0000000000..8678f2b722 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createFormatMenuItems.spec.ts @@ -0,0 +1,76 @@ +/* + * 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 { expect } from 'chai'; +import { + createFormatMenuItems, + createPrecisionSubitems, + createTimeSubitems +} from '@beakerx/tableDisplay/dataGrid/headerMenu/createFormatMenuItems'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import { scopeData, TIME_UNIT_FORMATS } from '@beakerx/tableDisplay/consts'; +import menuOptionsMock from "../mock/menuOptionsMock"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('createFormatMenuItems', () => { + let dataGrid; + let dataStore; + let column; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + column = new DataGridColumn({ + index: 0, + type: COLUMN_TYPES.index, + name: 'index', + menuOptions: menuOptionsMock + }, dataGrid, dataGrid.columnManager); + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should create format menu items', () => { + let expectedLength = scopeData.allIntTypes.length + Object.keys(TIME_UNIT_FORMATS).length - 1; // datetime is not duplicated + let formatMenuItems = createFormatMenuItems(column); + + expect(formatMenuItems).to.be.an.instanceof(Array); + expect(formatMenuItems).to.have.length(expectedLength); + }); + + describe('createPrecisionSubitems', () => { + it('should create precission menu items', () => { + let precissionMenuItems = createPrecisionSubitems(column); + + expect(precissionMenuItems).to.be.an.instanceof(Array); + expect(precissionMenuItems).to.have.length(scopeData.allPrecissions.length); + }); + }); + + describe('createTimeSubitems', () => { + it('should create time menu items', () => { + let timeMenuItems = createTimeSubitems(); + + expect(timeMenuItems).to.be.an.instanceof(Array); + expect(timeMenuItems).to.have.length(Object.keys(TIME_UNIT_FORMATS).length); + }); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createIndexMenuItems.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createIndexMenuItems.spec.ts new file mode 100644 index 0000000000..e47a3feb80 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/headerMenu/createIndexMenuItems.spec.ts @@ -0,0 +1,53 @@ +/* + * 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 { expect } from 'chai'; +import { createIndexMenuItems } from '@beakerx/tableDisplay/dataGrid/headerMenu/createIndexMenuItems'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import menuOptionsMock from "../mock/menuOptionsMock"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('createIndexMenuItems', () => { + let dataGrid; + let dataStore; + let column; + + before(() => { + dataStore = createStore(modelStateMock); + dataGrid = new BeakerxDataGrid({}, dataStore); + column = new DataGridColumn({ + index: 0, + type: COLUMN_TYPES.index, + name: 'index', + menuOptions: menuOptionsMock + }, dataGrid, dataGrid.columnManager); + }); + + after(() => { + dataGrid.destroy(); + }); + + it('should create index menu items', () => { + let indexMenuItems = createIndexMenuItems(column); + + expect(indexMenuItems).to.be.an.instanceof(Array); + expect(indexMenuItems).to.have.length(12); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HeatmapHighlighter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HeatmapHighlighter.spec.ts new file mode 100644 index 0000000000..586de7506e --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HeatmapHighlighter.spec.ts @@ -0,0 +1,64 @@ +/* + * 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 { expect } from 'chai'; +import HeatmapHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/HeatmapHighlighter"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import columnOptionsMock from "../mock/columnOptionsMock"; +import Highlighter from "@beakerx/tableDisplay/dataGrid/highlighter/Highlighter"; +import cellConfigMock from "../mock/cellConfigMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('HeatmapHighlighter', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const column = new DataGridColumn( + columnOptionsMock, + dataGrid, + dataGrid.columnManager + ); + + const heatmapHighlighter = new HeatmapHighlighter( + column, + highlighterStateMock + ); + + it('should be an instance of highlighter', () => { + expect(heatmapHighlighter).to.be.an.instanceof(Highlighter); + }); + + it('should have the getBackgroundColor method', () => { + expect(heatmapHighlighter).to.have.property('getBackgroundColor'); + }); + + it('should have the minColor state property', () => { + expect(heatmapHighlighter.state).to.have.property('minColor'); + }); + + it('should have the maxColor state property', () => { + expect(heatmapHighlighter.state).to.have.property('maxColor'); + }); + + it('should return proper backgroud color', () => { + expect(heatmapHighlighter.getBackgroundColor(cellConfigMock)).to.equal('rgb(255, 0, 0)'); + + const config = { ...cellConfigMock, value: 0 }; + expect(heatmapHighlighter.getBackgroundColor(config)).to.equal('rgb(0, 0, 255)'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/Highlighter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/Highlighter.spec.ts new file mode 100644 index 0000000000..8ac8cc0fc6 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/Highlighter.spec.ts @@ -0,0 +1,51 @@ +/* + * 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 { expect } from 'chai'; +import Highlighter from "@beakerx/tableDisplay/dataGrid/highlighter/Highlighter"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import columnOptionsMock from "../mock/columnOptionsMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('Highlighter', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const column = new DataGridColumn( + columnOptionsMock, + dataGrid, + dataGrid.columnManager + ); + + let highlighter = new Highlighter( + column, + highlighterStateMock + ); + + it('should be an instance of highlighter', () => { + expect(highlighter).to.be.an.instanceof(Highlighter); + }); + + it('should have the column property', () => { + expect(highlighter).to.have.property('column'); + }); + + it('should have the state property', () => { + expect(highlighter).to.have.property('state'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HighlighterFactory.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HighlighterFactory.spec.ts new file mode 100644 index 0000000000..c738a79f62 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HighlighterFactory.spec.ts @@ -0,0 +1,45 @@ +/* + * 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 { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import HighlighterFactory from "@beakerx/tableDisplay/dataGrid/highlighter/HighlighterFactory"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import modelStateMock from "../mock/modelStateMock"; +import columnOptionsMock from "../mock/columnOptionsMock"; +import HeatmapHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/HeatmapHighlighter"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('HighlighterFactory', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const column = new DataGridColumn( + columnOptionsMock, + dataGrid, + dataGrid.columnManager + ); + + it('should return HeatmapHighlighter', () => { + expect(HighlighterFactory.getHighlighter(highlighterStateMock, column)) + .to.be.an.instanceof(HeatmapHighlighter); + }); + + it('should return undefined', () => { + expect(HighlighterFactory.getHighlighter({}, column)) + .to.equal(undefined); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HighlighterManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HighlighterManager.spec.ts new file mode 100644 index 0000000000..7c495b3ffd --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/HighlighterManager.spec.ts @@ -0,0 +1,69 @@ +/* + * 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 { expect } from 'chai'; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import HighlighterManager from "@beakerx/tableDisplay/dataGrid/highlighter/HighlighterManager"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import modelStateMock from "../mock/modelStateMock"; +import HeatmapHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/HeatmapHighlighter"; +import cellConfigMock from "../mock/cellConfigMock"; +import {HIGHLIGHTER_TYPE} from "@beakerx/tableDisplay/dataGrid/interface/IHighlighterState"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('HighlighterManager', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const highlighterManager = new HighlighterManager(dataGrid, [highlighterStateMock]); + const highlighter = highlighterManager.highlighters[0]; + + it('should have property highlightersState', () => { + expect(highlighterManager).to.have.property('highlightersState'); + }); + + it('should have property highlighters', () => { + expect(highlighterManager).to.have.property('highlighters'); + }); + + it('should create the HeatmapHighlighter', () => { + expect(highlighterManager.highlighters[0]).to.be.an.instanceof(HeatmapHighlighter); + }); + + it('should implement getCellBackground method', () => { + expect(highlighterManager).to.have.property('getCellBackground'); + }); + + it('should return proper background', () => { + expect(highlighterManager.getCellBackground(cellConfigMock)).to.equal('rgb(255, 0, 0)'); + }); + + it('should return heatmap highlighter', () => { + expect(highlighterManager.getColumnHighlighters(dataGrid.columnManager.columns[1][0], HIGHLIGHTER_TYPE.heatmap)[0]) + .to.equal(highlighterManager.highlighters[0]); + }); + + it('should unregister highlighter', () => { + highlighterManager.unregisterHighlighter(highlighter); + + expect(highlighterManager.highlighters).to.have.length(0); + }); + + it('should register highlighter', () => { + highlighterManager.registerHighlighter(highlighter); + + expect(highlighterManager.highlighters).to.have.length(1); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter.spec.ts new file mode 100644 index 0000000000..5a8309f895 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter.spec.ts @@ -0,0 +1,72 @@ +/* + * 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 { expect } from 'chai'; +import HeatmapHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/HeatmapHighlighter"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import columnOptionsMock from "../mock/columnOptionsMock"; +import cellConfigMock from "../mock/cellConfigMock"; +import ThreeColorHeatmapHighlighter + from "@beakerx/tableDisplay/dataGrid/highlighter/ThreeColorHeatmapHighlighter"; +import { HIGHLIGHTER_TYPE } from "@beakerx/tableDisplay/dataGrid/interface/IHighlighterState"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('ThreeColorHeatmapHighlighter', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const column = new DataGridColumn( + columnOptionsMock, + dataGrid, + dataGrid.columnManager + ); + + let threeColorHeatmapHighlighter = new ThreeColorHeatmapHighlighter( + column, + { ...highlighterStateMock, type: HIGHLIGHTER_TYPE.threeColorHeatmap } + ); + + it('should be an instance of highlighter', () => { + expect(threeColorHeatmapHighlighter).to.be.an.instanceof(HeatmapHighlighter); + }); + + it('should have the getBackgroundColor method', () => { + expect(threeColorHeatmapHighlighter).to.have.property('getBackgroundColor'); + }); + + it('should have the minColor state property', () => { + expect(threeColorHeatmapHighlighter.state).to.have.property('minColor'); + }); + + it('should have the maxColor state property', () => { + expect(threeColorHeatmapHighlighter.state).to.have.property('maxColor'); + }); + + it('should have the midColor state property', () => { + expect(threeColorHeatmapHighlighter.state).to.have.property('midColor'); + }); + + it('should return proper backgroud color', () => { + expect(threeColorHeatmapHighlighter.getBackgroundColor(cellConfigMock)) + .to.equal('rgb(255, 0, 0)'); + expect(threeColorHeatmapHighlighter.getBackgroundColor({ ...cellConfigMock, value: 0 })) + .to.equal('rgb(0, 0, 255)'); + expect(threeColorHeatmapHighlighter.getBackgroundColor({ ...cellConfigMock, value: 0.5 })) + .to.equal('rgb(0, 255, 0)'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter.spec.ts new file mode 100644 index 0000000000..9009f51a36 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter.spec.ts @@ -0,0 +1,62 @@ +/* + * 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 { expect } from 'chai'; +import UniqueEntriesHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/UniqueEntriesHighlighter"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import columnOptionsMock from "../mock/columnOptionsMock"; +import cellConfigMock from "../mock/cellConfigMock"; +import { HIGHLIGHTER_TYPE } from "@beakerx/tableDisplay/dataGrid/interface/IHighlighterState"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('UniqueEntriesHighlighter', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const column = new DataGridColumn( + columnOptionsMock, + dataGrid, + dataGrid.columnManager + ); + + let uniqueEntriesHighlighter = new UniqueEntriesHighlighter( + column, + { ...highlighterStateMock, type: HIGHLIGHTER_TYPE.uniqueEntries } + ); + + it('should be an instance of highlighter', () => { + expect(uniqueEntriesHighlighter).to.be.an.instanceof(UniqueEntriesHighlighter); + }); + + it('should have the getBackgroundColor method', () => { + expect(uniqueEntriesHighlighter).to.have.property('getBackgroundColor'); + }); + + it('should have the midColor state property', () => { + expect(uniqueEntriesHighlighter.state).to.have.property('colors'); + }); + + it('should return proper backgroud color', () => { + expect(uniqueEntriesHighlighter.getBackgroundColor(cellConfigMock)) + .to.equal('hsl(0, 75%, 85%)'); + expect(uniqueEntriesHighlighter.getBackgroundColor({ ...cellConfigMock, value: 0 })) + .to.equal('hsl(180, 75%, 85%)'); + expect(uniqueEntriesHighlighter.getBackgroundColor({ ...cellConfigMock, value: 0.5 })) + .to.equal(''); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/highlighter/ValuesHighlighter.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/ValuesHighlighter.spec.ts new file mode 100644 index 0000000000..d5e4235ab8 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/highlighter/ValuesHighlighter.spec.ts @@ -0,0 +1,60 @@ +/* + * 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 { expect } from 'chai'; +import ValueHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/ValueHighlighter"; +import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn"; +import highlighterStateMock from "../mock/highlighterStateMock"; +import { BeakerxDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import columnOptionsMock from "../mock/columnOptionsMock"; +import cellConfigMock from "../mock/cellConfigMock"; +import { HIGHLIGHTER_TYPE } from "@beakerx/tableDisplay/dataGrid/interface/IHighlighterState"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('ValueHighlighter', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const column = new DataGridColumn( + columnOptionsMock, + dataGrid, + dataGrid.columnManager + ); + + let valueHighlighter = new ValueHighlighter( + column, + { ...highlighterStateMock, type: HIGHLIGHTER_TYPE.value } + ); + + it('should be an instance of highlighter', () => { + expect(valueHighlighter).to.be.an.instanceof(ValueHighlighter); + }); + + it('should have the getBackgroundColor method', () => { + expect(valueHighlighter).to.have.property('getBackgroundColor'); + }); + + it('should have the midColor state property', () => { + expect(valueHighlighter.state).to.have.property('colors'); + }); + + it('should return proper backgroud color', () => { + expect(valueHighlighter.getBackgroundColor(cellConfigMock)) + .to.equal('#ff0000'); + expect(valueHighlighter.getBackgroundColor({ ...cellConfigMock, row: 1 })) + .to.equal('#00ff00'); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/mock/cellConfigMock.ts b/js/notebook/test/src/tableDisplay/dataGrid/mock/cellConfigMock.ts new file mode 100644 index 0000000000..ed8532cc65 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/mock/cellConfigMock.ts @@ -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. + */ + +import { CellRenderer } from "@phosphor/datagrid"; + +const cellConfigMock: CellRenderer.ICellConfig = { + region: "body", + value: 1, + column: 0, + row: 0, + height: 0, + width: 0, + x: 0, + y: 0, + metadata: {} +}; + +export default cellConfigMock; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/mock/cellDataMock.ts b/js/notebook/test/src/tableDisplay/dataGrid/mock/cellDataMock.ts new file mode 100644 index 0000000000..4dd6801dac --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/mock/cellDataMock.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2018 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 {ICellData} from "@beakerx/tableDisplay/dataGrid/interface/ICell"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +const cellDataMock: ICellData = { + row: 0, + column: 0, + type: COLUMN_TYPES.body, + offset: 0, + offsetTop: 0, + delta: 0 +}; + +export default cellDataMock; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/mock/columnOptionsMock.ts b/js/notebook/test/src/tableDisplay/dataGrid/mock/columnOptionsMock.ts new file mode 100644 index 0000000000..3ecb9239a2 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/mock/columnOptionsMock.ts @@ -0,0 +1,27 @@ +/* + * 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 {IColumnOptions} from "@beakerx/tableDisplay/dataGrid/interface/IColumn"; +import {COLUMN_TYPES} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +let columnOptionsMock: IColumnOptions = { + index: 0, + name: 'test', + type: COLUMN_TYPES.body, + menuOptions: { x: 0, y: 0, height: 20, width: 20 } +}; + +export default columnOptionsMock; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/mock/highlighterStateMock.ts b/js/notebook/test/src/tableDisplay/dataGrid/mock/highlighterStateMock.ts new file mode 100644 index 0000000000..00b8a02461 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/mock/highlighterStateMock.ts @@ -0,0 +1,35 @@ +/* + * 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 IHihglighterState, { + HIGHLIGHTER_STYLE, + HIGHLIGHTER_TYPE +} from "@beakerx/tableDisplay/dataGrid/interface/IHighlighterState"; + +let highlighterStateMock: IHihglighterState = { + colName: 'test', + maxColor: '#ff0000', + maxVal: 1, + midColor: '#00ff00', + midVal: 0.5, + minColor: '#0000ff', + minVal: 0, + style: HIGHLIGHTER_STYLE.SINGLE_COLUMN, + type: HIGHLIGHTER_TYPE.heatmap, + colors: ['#ff0000', '#00ff00', '#0000ff'] +}; + +export default highlighterStateMock; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/mock/menuOptionsMock.ts b/js/notebook/test/src/tableDisplay/dataGrid/mock/menuOptionsMock.ts new file mode 100644 index 0000000000..f249084b93 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/mock/menuOptionsMock.ts @@ -0,0 +1,26 @@ +/* + * 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 { ITriggerOptions } from "@beakerx/tableDisplay/dataGrid/headerMenu/HeaderMenu"; + +const menuOptionsMock: ITriggerOptions = { + x: 0, + y: 0, + width: 20, + height: 20 +}; + +export default menuOptionsMock; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/mock/modelStateMock.ts b/js/notebook/test/src/tableDisplay/dataGrid/mock/modelStateMock.ts new file mode 100644 index 0000000000..d9e6d4e18b --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/mock/modelStateMock.ts @@ -0,0 +1,53 @@ +/* + * 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 IDataGridModelState from "@beakerx/tableDisplay/dataGrid/interface/IDataGridModelState"; + +let modelStateMock: IDataGridModelState = { + cellHighlighters: [], + columnNames: ['test', 'column'], + hasIndex: false, + stringFormatForColumn: {}, + types: ['integer', 'integer'], + values: [[1, 2],[0, 0]], + columnsVisible: {}, + tooltips: [], + hasDoubleClickAction: false, + columnOrder: [], + headerFontSize: null, + fontColor: null, + dataFontSize: null, + alignmentForColumn: {}, + alignmentForType: {}, + columnsFrozen: {}, + columnsFrozenRight: {}, + contextMenuItems: [], + contextMenuTags: {}, + doubleClickTag: null, + formatForTimes: null, + headersVertical: false, + rendererForColumn: {}, + rendererForType: {}, + stringFormatForTimes: null, + stringFormatForType: {}, + subtype: 'Tabledisplay', + timeStrings: null, + timeZone: null, + tooManyRows: false, + type: 'Tabledisplay', +}; + +export default modelStateMock; diff --git a/js/notebook/test/src/tableDisplay/dataGrid/model/BeakerxDataGridModel.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/model/BeakerxDataGridModel.spec.ts new file mode 100644 index 0000000000..9870bdb454 --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/model/BeakerxDataGridModel.spec.ts @@ -0,0 +1,90 @@ +/* + * 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 { expect } from 'chai'; +import { BeakerxDataGridModel } from '../../../../../src/tableDisplay/dataGrid/model/BeakerxDataGridModel'; +import { DataModel } from "@phosphor/datagrid"; +import modelStateMock from "../mock/modelStateMock"; +import {BeakerxDataGrid} from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; + +describe('BeakerxDataGridModel', () => { + describe('BeakerxDataGridModel.hasIndex === false', () => { + const dataStore = createStore(modelStateMock); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const beakerxDataGridModel = dataGrid.model; + + it('should be instance of DataModel', () => { + expect(beakerxDataGridModel).to.be.an.instanceof(DataModel); + }); + + it('should implement the data method', () => { + expect(beakerxDataGridModel).to.have.property('data'); + }); + + it('should return proper data', () => { + expect(beakerxDataGridModel.data('corner-header', 0, 0)).to.equal('index'); + expect(beakerxDataGridModel.data('column-header', 0, 0)).to.equal('test'); + expect(beakerxDataGridModel.data('row-header', 0, 0)).to.equal(0); + expect(beakerxDataGridModel.data('body', 0, 0)).to.equal(1); + }); + + it('should implement the rowCount method', () => { + expect(beakerxDataGridModel).to.have.property('rowCount'); + }); + + it('should return the proper row count', () => { + expect(beakerxDataGridModel.rowCount('body')).to.equal(2); + expect(beakerxDataGridModel.rowCount('column-header')).to.equal(1); + }); + + it('should implement the columnCount method', () => { + expect(beakerxDataGridModel).to.have.property('columnCount'); + }); + + it('should return the proper column count', () => { + expect(beakerxDataGridModel.columnCount('body')).to.equal(2); + expect(beakerxDataGridModel.columnCount('row-header')).to.equal(1); + }); + }); + + describe('BeakerxDataGridModel.hasIndex === true', () => { + const dataStore = createStore({ + ...modelStateMock, + hasIndex: true + }); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const beakerxDataGridModel = dataGrid.model; + + it('should return proper data', () => { + expect(beakerxDataGridModel.data('corner-header', 0, 0)).to.equal('test'); + expect(beakerxDataGridModel.data('column-header', 0, 0)).to.equal('column'); + expect(beakerxDataGridModel.data('row-header', 0, 0)).to.equal(1); + expect(beakerxDataGridModel.data('body', 0, 0)).to.equal(2); + }); + + it('should return the proper row count', () => { + expect(beakerxDataGridModel.rowCount('body')).to.equal(2); + expect(beakerxDataGridModel.rowCount('column-header')).to.equal(1); + }); + + it('should return the proper column count', () => { + expect(beakerxDataGridModel.columnCount('body')).to.equal(1); + expect(beakerxDataGridModel.columnCount('row-header')).to.equal(1); + }); + }); + +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/row/DataGridRow.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/row/DataGridRow.spec.ts new file mode 100644 index 0000000000..0938b5431d --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/row/DataGridRow.spec.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import DataGridRow from "@beakerx/tableDisplay/dataGrid/row/DataGridRow"; + +describe('DataGridRow', () => { + const values = [1,2,3,4]; + const row = new DataGridRow(0,values); + + it('should have index property', () => { + expect(row).to.have.property('index'); + expect(row.index).to.equal(0); + }); + + it('should have values property', () => { + expect(row).to.have.property('values'); + expect(row.values).to.equal(values); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/row/RowManager.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/row/RowManager.spec.ts new file mode 100644 index 0000000000..449f58772a --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/row/RowManager.spec.ts @@ -0,0 +1,116 @@ +/* + * Copyright 2018 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 { expect } from 'chai'; +import RowManager from "@beakerx/tableDisplay/dataGrid/row/RowManager"; +import {BeakerxDataGrid} from "@beakerx/tableDisplay/dataGrid/BeakerxDataGrid"; +import modelStateMock from "../mock/modelStateMock"; +import createStore from "@beakerx/tableDisplay/dataGrid/store/dataStore"; +import {SORT_ORDER} from "@beakerx/tableDisplay/dataGrid/column/enums"; + +describe('RowManager', () => { + const values = [[1,2,3,4], [5,6,7,8]]; + const columnNames = ['test', 'test1', 'test2', 'test3']; + const types = ['double', 'double', 'double', 'double']; + const modelState = { ...modelStateMock, values, columnNames, types }; + const dataStore = createStore(modelState); + const dataGrid = new BeakerxDataGrid({}, dataStore); + + describe('hasIndex = false', () => { + const rowManager = dataGrid.rowManager; + + it('should have rows property', () => { + expect(rowManager).to.have.property('rows'); + expect(rowManager.rows).to.have.length(values.length); + }); + + it('should implement sort valueResolvers', () => { + expect(rowManager).to.have.property('defaultValueResolver'); + expect(rowManager).to.have.property('dateValueResolver'); + expect(rowManager).to.have.property('indexValueResolver'); + expect(typeof rowManager.defaultValueResolver).to.equal('function'); + expect(typeof rowManager.dateValueResolver).to.equal('function'); + expect(typeof rowManager.indexValueResolver).to.equal('function'); + }); + + it('should return proper row values', () => { + expect(rowManager.getRow(0).values[0]).to.equal(1); + expect(rowManager.getRow(1).values[1]).to.equal(6); + }); + + it('should sort rows', () => { + rowManager.sortRows(0, SORT_ORDER.DESC); + + expect(rowManager.getRow(0).values[0]).to.equal(5); + expect(rowManager.getRow(1).values[0]).to.equal(1); + }); + + it('should filter rows', () => { + const column = dataGrid.columnManager.getColumnByName('test'); + column.filter(column.columnFilter['createFilterExpression']('$>1')); + + expect(rowManager.getRow(0).values[0]).to.equal(5); + expect(rowManager.rows).to.have.length(1); + }); + + it('should search rows', () => { + const column = dataGrid.columnManager.getColumnByName('test'); + column.search(column.columnFilter['createSearchExpression']('5')); + + expect(rowManager.getRow(0).values[0]).to.equal(5); + expect(rowManager.rows).to.have.length(1); + }); + }); + + describe('hasIndex = true', () => { + const dataStore = createStore({ ...modelState, hasIndex: true }); + const dataGrid = new BeakerxDataGrid({}, dataStore); + const rowManager = dataGrid.rowManager; + + it('should have rows property', () => { + expect(rowManager).to.have.property('rows'); + expect(rowManager.rows).to.have.length(values.length); + }); + + it('should return proper row values', () => { + expect(rowManager.getRow(0).values[0]).to.equal(2); + expect(rowManager.getRow(1).values[1]).to.equal(7); + }); + + it('should sort rows', () => { + rowManager.sortRows(0, SORT_ORDER.DESC); + + expect(rowManager.getRow(0).values[0]).to.equal(6); + expect(rowManager.getRow(1).values[0]).to.equal(2); + }); + + it('should filter rows', () => { + const column = dataGrid.columnManager.getColumnByName('test1'); + column.filter(column.columnFilter['createFilterExpression']('$>2')); + + expect(rowManager.getRow(0).values[0]).to.equal(6); + expect(rowManager.rows).to.have.length(1); + }); + + it('should search rows', () => { + const column = dataGrid.columnManager.getColumnByName('test1'); + column.search(column.columnFilter['createSearchExpression']('6')); + + expect(rowManager.getRow(0).values[0]).to.equal(6); + expect(rowManager.rows).to.have.length(1); + }); + }); +}); diff --git a/js/notebook/test/src/tableDisplay/dataGrid/style/DataGridStyle.spec.ts b/js/notebook/test/src/tableDisplay/dataGrid/style/DataGridStyle.spec.ts new file mode 100644 index 0000000000..d1d7414dce --- /dev/null +++ b/js/notebook/test/src/tableDisplay/dataGrid/style/DataGridStyle.spec.ts @@ -0,0 +1,37 @@ +/* + * 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 { expect } from 'chai'; +import { DataGrid } from '@phosphor/datagrid'; +import { silverStripeStyle } from '@beakerx/tableDisplay/dataGrid/style/dataGridStyle'; + +describe('dataGridStyle', () => { + it('should be an object', () => { + expect(silverStripeStyle).to.be.an('object'); + }); + + it('should have same properties as DataGrid.defaultStyle', () => { + expect(silverStripeStyle).to.have.any.keys(Object.keys(DataGrid.defaultStyle)); + }); + + it('should have the voidColor set to #ffffff', () => { + expect(silverStripeStyle.voidColor).to.equal('#ffffff'); + }); + + it('should have the rowBackgroundColor method', () => { + expect(silverStripeStyle.rowBackgroundColor).to.be.a('function'); + }); +}); diff --git a/js/notebook/test/tsconfig.json b/js/notebook/test/tsconfig.json new file mode 100644 index 0000000000..25ca3b55cf --- /dev/null +++ b/js/notebook/test/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "module": "commonjs", + "target": "es5", + "allowJs": true, + "moduleResolution": "node", + "lib": ["dom", "es6", "es5"], + "types": ["mocha", "chai", "node"], + "paths": { + "@beakerx/*": ["../../src/*"] + } + }, + "compileOnSave": false +} diff --git a/js/notebook/yarn.lock b/js/notebook/yarn.lock index 16204002d9..f893bb83ce 100644 --- a/js/notebook/yarn.lock +++ b/js/notebook/yarn.lock @@ -27,6 +27,25 @@ version "1.3.0" resolved "https://registry.yarnpkg.com/@phosphor/coreutils/-/coreutils-1.3.0.tgz#63292d381c012c5ab0d0196e83ced829b7e04a42" +"@phosphor/datagrid@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@phosphor/datagrid/-/datagrid-0.1.5.tgz#a4ff39ced37e3797b75e9c2c5d2e3c5c0a7914b6" + dependencies: + "@phosphor/algorithm" "^1.1.2" + "@phosphor/coreutils" "^1.3.0" + "@phosphor/disposable" "^1.1.2" + "@phosphor/domutils" "^1.1.2" + "@phosphor/dragdrop" "^1.3.0" + "@phosphor/messaging" "^1.2.2" + "@phosphor/signaling" "^1.2.2" + "@phosphor/widgets" "^1.5.0" + +"@phosphor/datastore@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@phosphor/datastore/-/datastore-0.7.0.tgz#3c7e8c27d43fd469ae4a98e1fd5cb6b0443ca58a" + dependencies: + "@phosphor/signaling" "^1.2.2" + "@phosphor/disposable@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@phosphor/disposable/-/disposable-1.1.2.tgz#a192dd6a2e6c69d5d09d39ecf334dab93778060e" @@ -87,9 +106,35 @@ "@phosphor/signaling" "^1.2.2" "@phosphor/virtualdom" "^1.1.2" +"@sinonjs/formatio@^2.0.0": + version "2.0.0" + resolved "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" + dependencies: + samsam "1.3.0" + +"@types/chai@^4.1.1": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.2.tgz#f1af664769cfb50af805431c407425ed619daa21" + "@types/jquery@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.0.tgz#6316ac20a1a13c5d521a2dc661befc7184f73f5b" + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.1.tgz#55758d44d422756d6329cbf54e6d41931d7ba28f" + +"@types/mocha@^2.2.46": + version "2.2.48" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.48.tgz#3523b126a0b049482e1c3c11877460f76622ffab" + +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + +abab@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" abbrev@1: version "1.1.1" @@ -101,17 +146,23 @@ acorn-dynamic-import@^2.0.0: dependencies: acorn "^4.0.3" +acorn-globals@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" + dependencies: + acorn "^5.0.0" + acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" +acorn@^5.0.0, acorn@^5.3.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" -ajv-keywords@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" +ajv-keywords@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" ajv@^4.9.1: version "4.11.8" @@ -120,15 +171,23 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5: - version "5.5.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.1.tgz#b38bb8876d9e86bee994956a04e721e88b248eb2" +ajv@^5.0.0, ajv@^5.1.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.1.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.3.0.tgz#1650a41114ef00574cac10b8032d8f4c14812da7" + dependencies: + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -157,9 +216,9 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" @@ -170,10 +229,27 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" @@ -182,8 +258,8 @@ are-we-there-yet@~1.1.2: readable-stream "^2.0.6" argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" dependencies: sprintf-js "~1.0.2" @@ -193,10 +269,22 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -205,9 +293,17 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arrify@^1.0.0, arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + asn1.js@^4.0.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.2.tgz#8117ef4f7ed87cd8f89044b5bff97ac243a16c9a" + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" dependencies: bn.js "^4.0.0" inherits "^2.0.1" @@ -231,6 +327,14 @@ assert@^1.1.1: dependencies: util "0.10.3" +assertion-error@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + ast-types@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" @@ -243,7 +347,15 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" -async@^2.1.2, async@^2.1.5, async@^2.5.0: +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + +async@^1.4.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.1.2, async@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" dependencies: @@ -253,6 +365,10 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +atob@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + autoprefixer@^6.3.1: version "6.7.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" @@ -276,7 +392,7 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-code-frame@^6.11.0, babel-code-frame@^6.22.0: +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -284,6 +400,69 @@ babel-code-frame@^6.11.0, babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.2" +babel-generator@^6.18.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.16.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.18.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.18.0, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" @@ -293,13 +472,25 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" base64-js@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" + version "1.2.3" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.3.tgz#fb13668233d9614cf5fb4bce95a9ba4096cdf801" base64-loader@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/base64-loader/-/base64-loader-1.0.0.tgz#e530bad88e906dd2a1fad0af2d9e683fa8bd92a8" +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -320,7 +511,7 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.0.0, bluebird@^3.5.0: +bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -351,8 +542,8 @@ bootstrap-sass@^3.3.7: resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.7.tgz#6596c7ab40f6637393323ab0bc80d064fc630498" brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -365,10 +556,35 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.0, braces@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + define-property "^1.0.0" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + kind-of "^6.0.2" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" +browser-process-hrtime@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.1.1" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" @@ -448,23 +664,45 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" -cacache@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.1.tgz#3e05f6e616117d9b54665b1b20c8aeb93ea5d36f" +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" dependencies: - bluebird "^3.5.0" + bluebird "^3.5.1" chownr "^1.0.1" glob "^7.1.2" graceful-fs "^4.1.11" lru-cache "^4.1.1" - mississippi "^1.3.0" + mississippi "^2.0.0" mkdirp "^0.5.1" move-concurrently "^1.0.1" promise-inflight "^1.0.1" - rimraf "^2.6.1" - ssri "^5.0.0" + rimraf "^2.6.2" + ssri "^5.2.4" unique-filename "^1.1.0" - y18n "^3.2.1" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +caching-transform@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-1.0.1.tgz#6dbdb2f20f8d8fbce79f3e94e9d1742dcdf5c0a1" + dependencies: + md5-hex "^1.2.0" + mkdirp "^0.5.1" + write-file-atomic "^1.1.4" camel-case@3.0.x: version "3.0.0" @@ -506,8 +744,8 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000782" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000782.tgz#d8815bce1578c350aced1132507301205e0fab53" + version "1.0.30000817" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000817.tgz#c9f8e236887cf60ae623d1fb1e5ec92877ab1229" caseless@~0.11.0: version "0.11.0" @@ -524,6 +762,17 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +chai@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" + dependencies: + assertion-error "^1.0.1" + check-error "^1.0.1" + deep-eql "^3.0.0" + get-func-name "^2.0.0" + pathval "^1.0.0" + type-detect "^4.0.0" + chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -534,13 +783,17 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" +chalk@^2.3.0, chalk@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" dependencies: - ansi-styles "^3.1.0" + ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" - supports-color "^4.0.0" + supports-color "^5.3.0" + +check-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" chokidar@^1.7.0: version "1.7.0" @@ -557,6 +810,24 @@ chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +chokidar@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.0" + optionalDependencies: + fsevents "^1.0.0" + chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" @@ -574,9 +845,18 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + clean-css@4.1.x: - version "4.1.9" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301" + version "4.1.11" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a" dependencies: source-map "0.5.x" @@ -596,14 +876,22 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -clone-deep@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" +cliui@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc" + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" dependencies: for-own "^1.0.0" - is-plain-object "^2.0.1" - kind-of "^3.2.2" - shallow-clone "^0.1.2" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" clone@^1.0.2: version "1.0.3" @@ -623,6 +911,13 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" @@ -659,27 +954,39 @@ colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" +combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" dependencies: delayed-stream "~1.0.0" -commander@2, commander@2.12.x, commander@^2.9.0, commander@~2.12.1: - version "2.12.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" +commander@2, commander@2.15.x, commander@^2.9.0, commander@~2.15.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + +commander@2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" concat-stream@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + version "1.6.1" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26" dependencies: inherits "^2.0.3" readable-stream "^2.2.2" @@ -699,6 +1006,14 @@ constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" +content-type-parser@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" + +convert-source-map@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -710,6 +1025,14 @@ copy-concurrently@^1.0.0: rimraf "^2.5.4" run-queue "^1.0.0" +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-js@^2.4.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -748,6 +1071,13 @@ cross-spawn@^3.0.0: lru-cache "^4.0.1" which "^1.2.9" +cross-spawn@^4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -789,21 +1119,21 @@ css-color-names@0.0.4: resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" css-loader@^0.28.4: - version "0.28.7" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.7.tgz#5f2ee989dd32edd907717f953317656160999c1b" + version "0.28.11" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7" dependencies: - babel-code-frame "^6.11.0" + babel-code-frame "^6.26.0" css-selector-tokenizer "^0.7.0" - cssnano ">=2.6.1 <4" + cssnano "^3.10.0" icss-utils "^2.1.0" loader-utils "^1.0.2" lodash.camelcase "^4.3.0" - object-assign "^4.0.1" + object-assign "^4.1.1" postcss "^5.0.6" - postcss-modules-extract-imports "^1.0.0" - postcss-modules-local-by-default "^1.0.1" - postcss-modules-scope "^1.0.0" - postcss-modules-values "^1.1.0" + postcss-modules-extract-imports "^1.2.0" + postcss-modules-local-by-default "^1.2.0" + postcss-modules-scope "^1.1.0" + postcss-modules-values "^1.3.0" postcss-value-parser "^3.3.0" source-list-map "^2.0.0" @@ -819,7 +1149,7 @@ cssesc@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" -"cssnano@>=2.6.1 <4": +cssnano@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" dependencies: @@ -863,6 +1193,16 @@ csso@~2.3.1: clap "^1.0.9" source-map "^0.5.3" +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" + +"cssstyle@>= 0.2.37 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + dependencies: + cssom "0.3.x" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -938,13 +1278,13 @@ d3-force@1.1.0: d3-quadtree "1" d3-timer "1" -d3-format@1, d3-format@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f" +d3-format@1, d3-format@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a" -d3-geo@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.0.tgz#15c7d7a8ea9346e59ed150dc7b1f7f95479056e9" +d3-geo@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356" dependencies: d3-array "1" @@ -999,9 +1339,9 @@ d3-scale@1.0.7: d3-time "1" d3-time-format "2" -d3-selection@1, d3-selection@1.2.0, d3-selection@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88" +d3-selection@1, d3-selection@1.3.0, d3-selection@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.0.tgz#d53772382d3dc4f7507bfb28bcd2d6aed2a0ad6d" d3-shape@1.2.0: version "1.2.0" @@ -1049,8 +1389,8 @@ d3-zoom@1.7.1: d3-transition "1" d3@^4.9.1: - version "4.12.0" - resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.0.tgz#75eccb39ea40f6018de8cfa2752905bee7daa46f" + version "4.13.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-4.13.0.tgz#ab236ff8cf0cfc27a81e69bf2fb7518bc9b4f33d" dependencies: d3-array "1.2.1" d3-axis "1.0.8" @@ -1063,8 +1403,8 @@ d3@^4.9.1: d3-dsv "1.0.8" d3-ease "1.0.3" d3-force "1.1.0" - d3-format "1.2.1" - d3-geo "1.9.0" + d3-format "1.2.2" + d3-geo "1.9.1" d3-hierarchy "1.1.5" d3-interpolate "1.1.6" d3-path "1.0.5" @@ -1074,7 +1414,7 @@ d3@^4.9.1: d3-random "1.1.0" d3-request "1.0.6" d3-scale "1.0.7" - d3-selection "1.2.0" + d3-selection "1.3.0" d3-shape "1.2.0" d3-time "1.0.8" d3-time-format "2.1.1" @@ -1096,8 +1436,8 @@ dashdash@^1.12.0: assert-plus "^1.0.0" datatables.net-buttons@^1.3.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/datatables.net-buttons/-/datatables.net-buttons-1.5.0.tgz#cfe15ad9e1d943e70179a8b85d01a0b109bd46ce" + version "1.5.1" + resolved "https://registry.yarnpkg.com/datatables.net-buttons/-/datatables.net-buttons-1.5.1.tgz#cdb35c0c8b0260cd45b0df6b39f6da815357febb" dependencies: datatables.net "^1.10.15" jquery ">=1.7" @@ -1155,8 +1495,8 @@ datatables.net-keytable@2.3.2, datatables.net-keytable@^2.3.2: jquery ">=1.7" datatables.net-select@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/datatables.net-select/-/datatables.net-select-1.2.4.tgz#799eed5d8cfcf21e4502b49c61e84fc4b5f457f7" + version "1.2.5" + resolved "https://registry.yarnpkg.com/datatables.net-select/-/datatables.net-select-1.2.5.tgz#4f764669b464d5576a59c576c1250a5d069aa2e9" dependencies: datatables.net "^1.10.15" jquery ">=1.7" @@ -1171,7 +1511,17 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@^2.2.0: +debug-log@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" + +debug@3.1.0, debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1181,13 +1531,52 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-eql@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + deepmerge@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312" + version "2.1.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102" + +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" defined@^1.0.0: version "1.0.0" @@ -1208,6 +1597,12 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" @@ -1216,6 +1611,10 @@ detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" +diff@3.5.0, diff@^3.1.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" @@ -1225,12 +1624,18 @@ diffie-hellman@^5.0.0: randombytes "^2.0.0" domain-browser@^1.1.1: - version "1.1.7" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" -duplexify@^3.1.2, duplexify@^3.4.2: - version "3.5.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd" +domexception@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + dependencies: + webidl-conversions "^4.0.2" + +duplexify@^3.4.2, duplexify@^3.5.3: + version "3.5.4" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.4.tgz#4bb46c1796eabebeec4ca9a2e66b808cb7a3d8b4" dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -1244,8 +1649,8 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" electron-to-chromium@^1.2.7: - version "1.3.28" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.28.tgz#8dd4e6458086644e9f9f0a1cf32e2a1f9dffd9ee" + version "1.3.40" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.40.tgz#1fbd6d97befd72b8a6f921dc38d22413d2f6fddf" elliptic@^6.0.0: version "6.4.0" @@ -1264,8 +1669,8 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" dependencies: once "^1.4.0" @@ -1278,9 +1683,9 @@ enhanced-resolve@^3.0.0, enhanced-resolve@^3.4.0: object-assign "^4.0.1" tapable "^0.2.7" -errno@^0.1.3, errno@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.5.tgz#a563781a6052bc2c9ccd89e8cef0eb9506e0c321" +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" dependencies: prr "~1.0.1" @@ -1291,13 +1696,14 @@ error-ex@^1.2.0: is-arrayish "^0.2.1" es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.37" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.37.tgz#0ee741d148b80069ba27d020393756af257defc3" + version "0.10.41" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.41.tgz#bab3e982d750f0112f0cb9e6abed72c59eb33eb2" dependencies: - es6-iterator "~2.0.1" + es6-iterator "~2.0.3" es6-symbol "~3.1.1" + next-tick "1" -es6-iterator@^2.0.1, es6-iterator@~2.0.1: +es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" dependencies: @@ -1349,10 +1755,21 @@ es6-weak-map@^2.0.1: es6-iterator "^2.0.1" es6-symbol "^3.1.1" -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escodegen@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" @@ -1366,18 +1783,17 @@ esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" -esprima@~3.1.0: +esprima@^3.1.3, esprima@~3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" esrecurse@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" dependencies: estraverse "^4.1.0" - object-assign "^4.0.1" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" @@ -1421,12 +1837,37 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" dependencies: fill-range "^2.1.0" +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -1437,6 +1878,19 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -1446,13 +1900,17 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" fast-deep-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" @@ -1477,6 +1935,23 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -1507,8 +1982,8 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" flush-write-stream@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" dependencies: inherits "^2.0.1" readable-stream "^2.0.4" @@ -1517,7 +1992,7 @@ for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" -for-in@^1.0.1: +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1533,13 +2008,20 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" +foreground-child@^1.5.3, foreground-child@^1.5.6: + version "1.5.6" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" + dependencies: + cross-spawn "^4" + signal-exit "^3.0.0" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" fork-ts-checker-webpack-plugin@^0.2.8: - version "0.2.9" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.2.9.tgz#997ec3bfadb9adbf47efcd47cee7549c644fb01b" + version "0.2.10" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.2.10.tgz#d0a4080e77e9f5d6e3b43cdce7d26658f9d250c6" dependencies: babel-code-frame "^6.22.0" chalk "^1.1.3" @@ -1559,13 +2041,19 @@ form-data@~2.1.1: mime-types "^2.1.12" form-data@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" dependencies: asynckit "^0.4.0" - combined-stream "^1.0.5" + combined-stream "1.0.6" mime-types "^2.1.12" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -1647,6 +2135,10 @@ get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" @@ -1655,6 +2147,10 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -1674,17 +2170,14 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" + is-glob "^3.1.0" + path-dirname "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@~7.1.1: +glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.2, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -1695,6 +2188,20 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@~7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + globule@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" @@ -1707,6 +2214,20 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" +growl@1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" + +handlebars@^4.0.3: + version "4.0.11" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" @@ -1739,10 +2260,9 @@ har-validator@~5.0.3: har-schema "^2.0.0" hard-source-webpack-plugin@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/hard-source-webpack-plugin/-/hard-source-webpack-plugin-0.5.1.tgz#e2d73d2e5d977658bc8cd1bfaa30981c40758ee7" + version "0.5.18" + resolved "https://registry.yarnpkg.com/hard-source-webpack-plugin/-/hard-source-webpack-plugin-0.5.18.tgz#4f328e344ad5305227c7db526d5365e8d7786712" dependencies: - bluebird "^3.0.0" lodash "^4.15.0" mkdirp "^0.5.1" node-object-hash "^1.2.0" @@ -1767,10 +2287,41 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + has@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" @@ -1815,7 +2366,7 @@ hawk@~6.0.2: hoek "4.x.x" sntp "2.x.x" -he@1.1.x: +he@1.1.1, he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -1832,17 +2383,29 @@ hoek@2.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" hoek@4.x.x: - version "4.2.0" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + +homedir-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + dependencies: + parse-passwd "^1.0.0" hosted-git-info@^2.1.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + version "2.6.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222" html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + dependencies: + whatwg-encoding "^1.0.1" + html-loader@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.4.5.tgz#5fbcd87cd63a5c49a7fce2fe56f425e05729c68c" @@ -1854,17 +2417,17 @@ html-loader@^0.4.5: object-assign "^4.1.0" html-minifier@^3.0.1: - version "3.5.7" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.7.tgz#511e69bb5a8e7677d1012ebe03819aa02ca06208" + version "3.5.12" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.12.tgz#6bfad4d0327f5b8d2b62f5854654ac3703b9b031" dependencies: camel-case "3.0.x" clean-css "4.1.x" - commander "2.12.x" + commander "2.15.x" he "1.1.x" ncname "1.0.x" param-case "2.1.x" relateurl "0.2.x" - uglify-js "3.2.x" + uglify-js "3.3.x" http-signature@~1.1.0: version "1.1.1" @@ -1886,7 +2449,7 @@ https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" -iconv-lite@0.4: +iconv-lite@0.4, iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -1901,13 +2464,17 @@ icss-utils@^2.1.0: postcss "^6.0.1" ieee754@^1.1.4: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + version "1.1.10" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.10.tgz#719a6f7b026831e64bdb838b0de1bb0029bbf716" iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" +ignore-styles@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ignore-styles/-/ignore-styles-5.0.1.tgz#b49ef2274bdafcd8a4880a966bfe38d1a0bf4671" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -1953,6 +2520,12 @@ interpret@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + dependencies: + loose-envify "^1.0.0" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -1961,6 +2534,18 @@ is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -1971,7 +2556,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -1981,6 +2566,34 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -1991,14 +2604,24 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" @@ -2021,12 +2644,29 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + is-my-json-valid@^2.12.4: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" jsonpointer "^4.0.0" xtend "^4.0.0" @@ -2042,11 +2682,21 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + dependencies: + is-number "^4.0.0" + is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-object@^2.0.1: +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: @@ -2082,6 +2732,14 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2096,7 +2754,7 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.1: +isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -2104,19 +2762,66 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" +istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" + +istanbul-lib-hook@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b" + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.2.0" + semver "^5.3.0" + +istanbul-lib-report@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz#2df12188c0fa77990c0d2176d2d0ba3394188259" + dependencies: + istanbul-lib-coverage "^1.1.2" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6" + dependencies: + debug "^3.1.0" + istanbul-lib-coverage "^1.1.2" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-reports@^1.1.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.3.0.tgz#2f322e81e1d9520767597dca3c20a0cce89a3554" + dependencies: + handlebars "^4.0.3" + jquery-ui@^1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51" jquery@>=1.7: - version "3.2.1" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787" + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" js-base64@^2.1.8, js-base64@^2.1.9: - version "2.4.0" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.0.tgz#9e566fee624751a1d720c966cd6226d29d4025aa" + version "2.4.3" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582" -js-tokens@^3.0.2: +js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -2131,6 +2836,41 @@ jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" +jsdom@^11.5.1: + version "11.6.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.6.2.tgz#25d1ef332d48adf77fc5221fe2619967923f16bb" + dependencies: + abab "^1.0.4" + acorn "^5.3.0" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + browser-process-hrtime "^0.1.2" + content-type-parser "^1.0.2" + cssom ">= 0.3.2 < 0.4.0" + cssstyle ">= 0.2.37 < 0.3.0" + domexception "^1.0.0" + escodegen "^1.9.0" + html-encoding-sniffer "^1.0.2" + left-pad "^1.2.0" + nwmatcher "^1.4.3" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.83.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.3" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-url "^6.4.0" + ws "^4.0.0" + xml-name-validator "^3.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" @@ -2178,13 +2918,11 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -kind-of@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" - dependencies: - is-buffer "^1.0.2" +just-extend@^1.1.27: + version "1.1.27" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905" -kind-of@^3.0.2, kind-of@^3.2.2: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: @@ -2196,9 +2934,13 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" lazy-cache@^1.0.3: version "1.0.4" @@ -2210,6 +2952,17 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +left-pad@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -2273,9 +3026,13 @@ lodash.endswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + lodash.isfunction@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.8.tgz#4db709fc81bc4a8fd7127a458a5346c5cdce2c6b" + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" lodash.isstring@^4.0.1: version "4.0.1" @@ -2286,8 +3043,12 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" lodash.mergewith@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" lodash.startswith@^4.2.1: version "4.2.1" @@ -2301,14 +3062,24 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@~4.17.4: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.4: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + +lolex@^2.2.0, lolex@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.2.tgz#85f9450425103bf9e7a60668ea25dc43274ca807" longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -2321,8 +3092,8 @@ lower-case@^1.1.1: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" lru-cache@^4.0.1, lru-cache@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + version "4.1.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f" dependencies: pseudomap "^1.0.2" yallist "^2.1.2" @@ -2332,19 +3103,43 @@ macaddress@^0.2.8: resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" make-dir@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" + version "1.2.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" dependencies: pify "^3.0.0" +make-error@^1.1.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.4.tgz#19978ed575f9e9545d2ff8c13e33b5d18a67d535" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" +md5-hex@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-1.3.0.tgz#d2c4afe983c4370662179b8cad145219135046c4" + dependencies: + md5-o-matic "^0.1.1" + +md5-o-matic@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" + md5.js@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" @@ -2380,7 +3175,13 @@ meow@^3.7.0: redent "^1.0.0" trim-newlines "^1.0.0" -micromatch@^2.1.5: +merge-source-map@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + dependencies: + source-map "^0.6.1" + +micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -2398,6 +3199,24 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.4, micromatch@^3.1.8: + version "3.1.9" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -2405,23 +3224,23 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@~1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: - version "2.1.17" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: - mime-db "~1.30.0" + mime-db "~1.33.0" mime@1.3.x: version "1.3.6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" mimic-fn@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" minimalistic-assert@^1.0.0: version "1.0.0" @@ -2445,9 +3264,13 @@ minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -mississippi@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-1.3.0.tgz#d201583eb12327e3c5c1642a404a9cacf94e34f5" +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" dependencies: concat-stream "^1.5.0" duplexify "^3.4.2" @@ -2455,11 +3278,18 @@ mississippi@^1.3.0: flush-write-stream "^1.0.0" from2 "^2.1.0" parallel-transform "^1.1.0" - pump "^1.0.0" + pump "^2.0.1" pumpify "^1.3.3" stream-each "^1.1.0" through2 "^2.0.0" +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mixin-object@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" @@ -2467,12 +3297,27 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" +mocha@^5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.0.4.tgz#6b7aa328472da1088e69d47e75925fd3a3bb63c6" + dependencies: + browser-stdout "1.3.1" + commander "2.11.0" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.3" + he "1.1.1" + mkdirp "0.5.1" + supports-color "4.4.0" + moment-timezone@^0.5.13: version "0.5.14" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.14.tgz#4eb38ff9538b80108ba467a458f3ed4268ccfcb1" @@ -2480,8 +3325,8 @@ moment-timezone@^0.5.13: moment ">= 2.9.0" "moment@>= 2.9.0", moment@^2.17.1: - version "2.19.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.4.tgz#17e5e2c6ead8819c8ecfad83a0acccb312e94682" + version "2.21.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" move-concurrently@^1.0.1: version "1.0.1" @@ -2499,8 +3344,25 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" nan@^2.3.0, nan@^2.3.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" ncname@1.0.x: version "1.0.0" @@ -2508,6 +3370,24 @@ ncname@1.0.x: dependencies: xml-char-classes "^1.0.0" +neo-async@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" + +next-tick@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + +nise@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.3.2.tgz#fd6fd8dc040dfb3c0a45252feb6ff21832309b14" + dependencies: + "@sinonjs/formatio" "^2.0.0" + just-extend "^1.1.27" + lolex "^2.3.2" + path-to-regexp "^1.7.0" + text-encoding "^0.6.4" + no-case@^2.2.0: version "2.3.2" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" @@ -2626,7 +3506,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1: +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: @@ -2668,14 +3548,64 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" +nwmatcher@^1.4.3: + version "1.4.4" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" + +nyc@^11.4.1: + version "11.6.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.6.0.tgz#d9c7b51ffceb6bba099a4683a6adc1b331b98853" + dependencies: + archy "^1.0.0" + arrify "^1.0.1" + caching-transform "^1.0.0" + convert-source-map "^1.5.1" + debug-log "^1.0.1" + default-require-extensions "^1.0.0" + find-cache-dir "^0.1.1" + find-up "^2.1.0" + foreground-child "^1.5.3" + glob "^7.0.6" + istanbul-lib-coverage "^1.1.2" + istanbul-lib-hook "^1.1.0" + istanbul-lib-instrument "^1.10.0" + istanbul-lib-report "^1.1.3" + istanbul-lib-source-maps "^1.2.3" + istanbul-reports "^1.1.4" + md5-hex "^1.2.0" + merge-source-map "^1.0.2" + micromatch "^2.3.11" + mkdirp "^0.5.0" + resolve-from "^2.0.0" + rimraf "^2.5.4" + signal-exit "^3.0.1" + spawn-wrap "^1.4.2" + test-exclude "^4.2.0" + yargs "11.1.0" + yargs-parser "^8.0.0" + oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -2683,17 +3613,41 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -2716,8 +3670,8 @@ os-tmpdir@^1.0.0: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" osenv@0, osenv@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" @@ -2727,8 +3681,10 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" p-limit@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" + dependencies: + p-try "^1.0.0" p-locate@^2.0.0: version "2.0.0" @@ -2736,6 +3692,10 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" @@ -2779,10 +3739,26 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + path-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -2801,6 +3777,16 @@ path-key@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -2815,6 +3801,10 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" +pathval@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + pbkdf2@^3.0.3: version "3.0.14" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade" @@ -2851,12 +3841,26 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" dependencies: find-up "^2.1.0" +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + postcss-calc@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" @@ -2979,27 +3983,27 @@ postcss-minify-selectors@^2.0.4: postcss "^5.0.14" postcss-selector-parser "^2.0.0" -postcss-modules-extract-imports@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb" +postcss-modules-extract-imports@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" dependencies: postcss "^6.0.1" -postcss-modules-local-by-default@^1.0.1: +postcss-modules-local-by-default@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" dependencies: css-selector-tokenizer "^0.7.0" postcss "^6.0.1" -postcss-modules-scope@^1.0.0: +postcss-modules-scope@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" dependencies: css-selector-tokenizer "^0.7.0" postcss "^6.0.1" -postcss-modules-values@^1.1.0: +postcss-modules-values@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" dependencies: @@ -3096,12 +4100,16 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 supports-color "^3.2.3" postcss@^6.0.1: - version "6.0.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885" + version "6.0.20" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.20.tgz#686107e743a12d5530cb68438c590d5b2bf72c3c" dependencies: - chalk "^2.3.0" + chalk "^2.3.2" source-map "^0.6.1" - supports-color "^4.4.0" + supports-color "^5.3.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" prepend-http@^1.0.0: version "1.0.4" @@ -3115,9 +4123,9 @@ private@~0.1.5: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" process@^0.11.10: version "0.11.10" @@ -3145,20 +4153,20 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -pump@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" dependencies: end-of-stream "^1.1.0" once "^1.3.1" pumpify@^1.3.3: - version "1.3.5" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.5.tgz#1b671c619940abcaeac0ad0e3a3c164be760993b" + version "1.4.0" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb" dependencies: - duplexify "^3.1.2" - inherits "^2.0.1" - pump "^1.0.0" + duplexify "^3.5.3" + inherits "^2.0.3" + pump "^2.0.0" punycode@1.3.2: version "1.3.2" @@ -3168,6 +4176,10 @@ punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +punycode@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -3207,21 +4219,21 @@ randomatic@^1.1.3: kind-of "^4.0.0" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79" + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62" + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" rc@^1.1.7: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" + version "1.2.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" dependencies: deep-extend "~0.4.0" ini "~1.3.0" @@ -3258,14 +4270,14 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3: + version "2.3.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" dependencies: core-util-is "~1.0.0" inherits "~2.0.3" isarray "~1.0.0" - process-nextick-args "~1.0.6" + process-nextick-args "~2.0.0" safe-buffer "~5.1.1" string_decoder "~1.0.3" util-deprecate "~1.0.1" @@ -3313,12 +4325,23 @@ regenerate@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" dependencies: is-equal-shallow "^0.1.3" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -3349,7 +4372,7 @@ repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -3359,9 +4382,23 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2: - version "2.83.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" +request-promise-core@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" + dependencies: + lodash "^4.13.1" + +request-promise-native@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" + dependencies: + request-promise-core "1.1.1" + stealthy-require "^1.1.0" + tough-cookie ">=2.3.3" + +request@2, request@^2.83.0: + version "2.85.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" dependencies: aws-sign2 "~0.7.0" aws4 "^1.6.0" @@ -3446,6 +4483,22 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" +reselect@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" + +resolve-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -3479,6 +4532,16 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +samsam@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" + sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" @@ -3489,16 +4552,16 @@ sass-graph@^2.2.4: yargs "^7.0.0" sass-loader@^6.0.5: - version "6.0.6" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" + version "6.0.7" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.7.tgz#dd2fdb3e7eeff4a53f35ba6ac408715488353d00" dependencies: - async "^2.1.5" - clone-deep "^0.3.0" + clone-deep "^2.0.1" loader-utils "^1.0.1" lodash.tail "^4.1.1" + neo-async "^2.5.0" pify "^3.0.0" -sax@~1.2.1: +sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -3508,6 +4571,13 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" +schema-utils@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -3516,13 +4586,17 @@ scss-tokenizer@^0.2.3: source-map "^0.4.2" "semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +serialize-javascript@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3531,24 +4605,41 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.9" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d" + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" +shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" dependencies: is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" + kind-of "^5.0.0" mixin-object "^2.0.1" shebang-command@^1.2.0: @@ -3561,10 +4652,53 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +sinon@^4.2.0: + version "4.4.6" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.4.6.tgz#0b21ce56f1b11015749a82a3bbde2f46b78ec0e1" + dependencies: + "@sinonjs/formatio" "^2.0.0" + diff "^3.1.0" + lodash.get "^4.4.2" + lolex "^2.2.0" + nise "^1.2.0" + supports-color "^5.1.0" + type-detect "^4.0.5" + +slide@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -3605,41 +4739,86 @@ source-map-loader@^0.2.1: loader-utils "~0.2.2" source-map "~0.6.1" -source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1: +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" + dependencies: + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.0: + version "0.5.4" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.4.tgz#54456efa89caa9270af7cd624cc2f123e51fbae8" + dependencies: + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" -source-map@^0.4.2, source-map@~0.4.1: +source-map@^0.4.2, source-map@^0.4.4, source-map@~0.4.1: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" dependencies: amdefine ">=0.0.4" -source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" -spdx-correct@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" +spawn-wrap@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" dependencies: - spdx-license-ids "^1.0.2" + foreground-child "^1.5.6" + mkdirp "^0.5.0" + os-homedir "^1.0.1" + rimraf "^2.6.2" + signal-exit "^3.0.2" + which "^1.3.0" -spdx-expression-parse@~1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" -spdx-license-ids@^1.0.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + version "1.14.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -3651,11 +4830,18 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -ssri@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.0.0.tgz#13c19390b606c821f2a10d02b351c1729b94d8cf" +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" dependencies: - safe-buffer "^5.1.0" + safe-buffer "^5.1.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" stdout-stream@^1.4.0: version "1.4.0" @@ -3663,6 +4849,10 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" +stealthy-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" @@ -3678,12 +4868,12 @@ stream-each@^1.1.0: stream-shift "^1.0.0" stream-http@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" + version "2.8.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.1.tgz#d0441be1a457a73a733a8a7b53570bebd9ef66a4" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" - readable-stream "^2.2.6" + readable-stream "^2.3.3" to-arraybuffer "^1.0.0" xtend "^4.0.0" @@ -3703,14 +4893,20 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: +string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string_decoder@^1.0.0, string_decoder@~1.0.3: +string_decoder@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.0.tgz#384f322ee8a848e500effde99901bba849c5d403" + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: @@ -3752,7 +4948,7 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -3763,22 +4959,34 @@ style-loader@^0.18.1: loader-utils "^1.0.2" schema-utils "^0.3.0" +supports-color@4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + dependencies: + has-flag "^2.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.2.3: +supports-color@^3.1.2, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" -supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0: +supports-color@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" dependencies: has-flag "^2.0.0" +supports-color@^5.1.0, supports-color@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" + dependencies: + has-flag "^3.0.0" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -3791,6 +4999,10 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" +symbol-tree@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + tapable@^0.2.7: version "0.2.8" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" @@ -3816,6 +5028,20 @@ tar@^2.0.0, tar@^2.2.1: fstream "^1.0.2" inherits "2" +test-exclude@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa" + dependencies: + arrify "^1.0.1" + micromatch "^3.1.8" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + +text-encoding@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" + through2@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" @@ -3828,8 +5054,8 @@ through@~2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" timers-browserify@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6" + version "2.0.6" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.6.tgz#241e76927d9ca05f4d959819022f5b3664b64bae" dependencies: setimmediate "^1.0.4" @@ -3837,16 +5063,52 @@ to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" -tough-cookie@~2.3.0, tough-cookie@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: punycode "^1.4.1" +tr46@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + dependencies: + punycode "^2.1.0" + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + "true-case-path@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" @@ -3854,14 +5116,30 @@ trim-newlines@^1.0.0: glob "^6.0.4" ts-loader@^3.0.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-3.2.0.tgz#23211922179b81f7448754b7fdfca45b8374a15a" + version "3.5.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-3.5.0.tgz#151d004dcddb4cf8e381a3bf9d6b74c2d957a9c0" dependencies: chalk "^2.3.0" enhanced-resolve "^3.0.0" loader-utils "^1.0.2" + micromatch "^3.1.4" semver "^5.0.1" +ts-node@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-4.1.0.tgz#36d9529c7b90bb993306c408cd07f7743de20712" + dependencies: + arrify "^1.0.0" + chalk "^2.3.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.0" + tsconfig "^7.0.0" + v8flags "^3.0.0" + yn "^2.0.0" + tsconfig-paths-webpack-plugin@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-2.0.0.tgz#7652dc684bb3206c8e7e446831ca01cbf4d11772" @@ -3870,13 +5148,22 @@ tsconfig-paths-webpack-plugin@^2.0.0: tsconfig-paths "^3.1.1" tsconfig-paths@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.1.1.tgz#368478faed54a247f2c8e59e44cb81418b2ae515" + version "3.1.3" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.1.3.tgz#21783c11f36eeb1192197b70cfdf21552918a040" dependencies: deepmerge "^2.0.1" strip-bom "^3.0.0" strip-json-comments "^2.0.1" +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -3895,29 +5182,39 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" typescript@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.1.tgz#bb3682c2c791ac90e7c6210b26478a8da085c359" + version "2.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" -uglify-es@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.2.2.tgz#15c62b7775002c81b7987a1c49ecd3f126cace73" +uglify-es@^3.3.4: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" dependencies: - commander "~2.12.1" + commander "~2.13.0" source-map "~0.6.1" -uglify-js@3.2.x: - version "3.2.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.2.2.tgz#870e4b34ed733d179284f9998efd3293f7fd73f6" +uglify-js@3.3.x: + version "3.3.16" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.16.tgz#23ba13efa27aa00885be7417819e8a9787f94028" dependencies: - commander "~2.12.1" + commander "~2.15.0" source-map "~0.6.1" -uglify-js@^2.8.29: +uglify-js@^2.6, uglify-js@^2.8.29: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" dependencies: @@ -3939,16 +5236,17 @@ uglifyjs-webpack-plugin@^0.4.6: webpack-sources "^1.0.1" uglifyjs-webpack-plugin@^1.0.0-rc.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.2.tgz#8a9abc238d01a33daaf86fa9a84c7ebc1e67b0f9" + version "1.2.4" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.4.tgz#5eec941b2e9b8538be0a20fc6eda25b14c7c1043" dependencies: - cacache "^10.0.0" + cacache "^10.0.4" find-cache-dir "^1.0.0" - schema-utils "^0.3.0" + schema-utils "^0.4.5" + serialize-javascript "^1.4.0" source-map "^0.6.1" - uglify-es "^3.2.0" - webpack-sources "^1.0.1" - worker-farm "^1.4.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" uid-number@^0.0.6: version "0.0.6" @@ -3958,6 +5256,15 @@ underscore@^1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -3984,10 +5291,25 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" + upper-case@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + url-loader@^0.5.9: version "0.5.9" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.9.tgz#cc8fea82c7b906e7777019250869e569e995c295" @@ -4002,6 +5324,12 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +use@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" + dependencies: + kind-of "^6.0.2" + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -4013,15 +5341,21 @@ util@0.10.3, util@^0.10.3: inherits "2.0.1" uuid@^3.0.0, uuid@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + +v8flags@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.2.tgz#ad6a78a20a6b23d03a8debc11211e3cc23149477" + dependencies: + homedir-polyfill "^1.0.1" validate-npm-package-license@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + version "3.0.3" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" dependencies: - spdx-correct "~1.0.0" - spdx-expression-parse "~1.0.0" + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" vendors@^1.0.0: version "1.0.1" @@ -4041,13 +5375,23 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + dependencies: + browser-process-hrtime "^0.1.2" + watchpack@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" + version "1.5.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.5.0.tgz#231e783af830a22f8966f65c4c4bacc814072eed" dependencies: - async "^2.1.2" - chokidar "^1.7.0" + chokidar "^2.0.2" graceful-fs "^4.1.2" + neo-async "^2.5.0" + +webidl-conversions@^4.0.1, webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" webpack-core@~0.6.0: version "0.6.9" @@ -4057,12 +5401,12 @@ webpack-core@~0.6.0: source-map "~0.4.1" webpack-merge@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.1.tgz#f1197a0a973e69c6fbeeb6d658219aa8c0c13555" + version "4.1.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.2.tgz#5d372dddd3e1e5f8874f5bf5a8e929db09feb216" dependencies: - lodash "^4.17.4" + lodash "^4.17.5" -webpack-sources@^1.0.1: +webpack-sources@^1.0.1, webpack-sources@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" dependencies: @@ -4070,13 +5414,13 @@ webpack-sources@^1.0.1: source-map "~0.6.1" webpack@^3.5.6: - version "3.10.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725" + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.11.0.tgz#77da451b1d7b4b117adaf41a1a93b5742f24d894" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" - ajv "^5.1.5" - ajv-keywords "^2.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" async "^2.1.2" enhanced-resolve "^3.4.0" escope "^3.6.0" @@ -4096,6 +5440,20 @@ webpack@^3.5.6: webpack-sources "^1.0.1" yargs "^8.0.2" +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3" + dependencies: + iconv-lite "0.4.19" + +whatwg-url@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08" + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.0" + webidl-conversions "^4.0.1" + whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" @@ -4108,7 +5466,7 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@^1.2.9: +which@1, which@^1.2.9, which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: @@ -4128,12 +5486,19 @@ wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" -worker-farm@^1.4.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.2.tgz#32b312e5dc3d5d45d79ef44acc2587491cd729ae" +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" dependencies: - errno "^0.1.4" - xtend "^4.0.1" + errno "~0.1.7" wrap-ansi@^2.0.0: version "2.1.0" @@ -4146,6 +5511,14 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" +write-file-atomic@^1.1.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + slide "^1.1.5" + write-file-atomic@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" @@ -4165,15 +5538,26 @@ write-json-file@^2.3.0: sort-keys "^2.0.0" write-file-atomic "^2.0.0" +ws@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + xml-char-classes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + xmlhttprequest@1: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -4181,6 +5565,10 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -4197,6 +5585,35 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" +yargs-parser@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" + dependencies: + camelcase "^4.1.0" + +yargs-parser@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + dependencies: + camelcase "^4.1.0" + +yargs@11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^9.0.2" + yargs@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" @@ -4241,3 +5658,7 @@ yargs@~3.10.0: cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" diff --git a/kernel/base/build.gradle b/kernel/base/build.gradle index ddeee2b557..ffc7ef136b 100644 --- a/kernel/base/build.gradle +++ b/kernel/base/build.gradle @@ -30,7 +30,7 @@ compileJava { dependencies { compile group: 'com.opencsv', name: 'opencsv', version: '3.10' - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.6.5' + compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.3' compile group: 'com.github.jupyter', name: 'jvm-repr', version: '0.3.1' compile group: 'com.google.guava', name: 'guava', version: '22.0' compile group: 'com.google.inject', name: 'guice', version: '3.0' diff --git a/kernel/base/src/main/java/com/twosigma/beakerx/table/serializer/TableDisplaySerializer.java b/kernel/base/src/main/java/com/twosigma/beakerx/table/serializer/TableDisplaySerializer.java index 408a252e8c..e4048e40b4 100644 --- a/kernel/base/src/main/java/com/twosigma/beakerx/table/serializer/TableDisplaySerializer.java +++ b/kernel/base/src/main/java/com/twosigma/beakerx/table/serializer/TableDisplaySerializer.java @@ -48,7 +48,7 @@ public class TableDisplaySerializer extends ObservableTableDisplaySerializer> values = value.getValues(); if (values.size() > ROWS_LIMIT) { - jgen.writeObjectField(VALUES, values.subList(0, 1000)); + jgen.writeObjectField(VALUES, values.subList(0, 10000)); jgen.writeBooleanField("tooManyRows", true); jgen.writeObjectField("rowLength", values.size()); jgen.writeObjectField("rowLimit", ROWS_LIMIT); diff --git a/kernel/base/src/test/java/com/twosigma/beakerx/table/TableDisplayTest.java b/kernel/base/src/test/java/com/twosigma/beakerx/table/TableDisplayTest.java index f18bd9968e..16be74da0c 100644 --- a/kernel/base/src/test/java/com/twosigma/beakerx/table/TableDisplayTest.java +++ b/kernel/base/src/test/java/com/twosigma/beakerx/table/TableDisplayTest.java @@ -504,7 +504,7 @@ public void shouldSendCommMsgWhenStringFormatForTimesChange() throws Exception { assertThat(tableDisplay.getStringFormatForTimes()).isEqualTo(days); LinkedHashMap model = getModelUpdate(); assertThat(model.size()).isEqualTo(1); - assertThat(model.get(STRING_FORMAT_FOR_TIMES)).isEqualTo(days.toString()); + assertThat(model.get(STRING_FORMAT_FOR_TIMES)).isEqualTo(days); } @Test diff --git a/test/ipynb/groovy/PlotActionsTest.ipynb b/test/ipynb/groovy/PlotActionsTest.ipynb deleted file mode 100644 index 110d85a670..0000000000 --- a/test/ipynb/groovy/PlotActionsTest.ipynb +++ /dev/null @@ -1,277 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "barsPlot = new Plot(useToolTip: false);\n", - "barsPlot << new Bars(x: (1..5), y: [5, 2, 4, 3, 7], )\n", - " .onKey(KeyboardCodes.SHIFT, {info -> info.graphics.y[info.index]++})\n", - " .onKey(\"T\", \"tag1\")\n", - " .onKey(\"U\", {info -> info.graphics.y[info.index]--;\n", - " beakerx.runByTag(\"tag1\")\n", - " })" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag1" - ] - }, - "outputs": [], - "source": [ - "def details = barsPlot.details\n", - "def item = details.graphics\n", - "def index = details.index\n", - "println (item.x[index] + \":\" + item.y[index])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "barsPlot2 = new Plot(useToolTip: false);\n", - "barsPlot2 << new Bars(x: (1..5), y: [5, 2, 4, 3, 7], )\n", - " .onClick({info -> info.graphics.y[info.index]++;\n", - " beakerx.runByTag(\"tag2\")\n", - " })" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag2" - ] - }, - "outputs": [], - "source": [ - "def details2 = barsPlot2.details\n", - "def item2 = details2.graphics\n", - "def index2 = details2.index\n", - "println (item2.x[index2] + \":\" + item2.y[index2])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "barsPlot3 = new Plot(useToolTip: false);\n", - "barsPlot3 << new Bars(x: (1..5), y: [5, 2, 4, 3, 7], ).onClick(\"tag3\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag3" - ] - }, - "outputs": [], - "source": [ - "def details3 = barsPlot3.details\n", - "def item3 = details3.graphics\n", - "def index3 = details3.index\n", - "println (item3.x[index3] + \":\" + item3.y[index3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "barsPlot4 = new Plot(useToolTip: false);\n", - "barsPlot4 << new Bars(x: (1..5), y: [12, 6, 8, 4, 8], )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cplot5 = new CombinedPlot()\n", - "def plot5_1 = new Plot(useToolTip: false);\n", - "plot5_1 << new Bars(x: (1..5), y: [5, 2, 4, 3, 7]).onClick({info -> info.graphics.y[info.index]++;}) \n", - "cplot5.add(plot5_1)\n", - "\n", - "def plot5_2 = new Plot(useToolTip: false);\n", - "plot5_2 << new Bars(x: (1..5), y: [12, 6, 8, 4, 8], color: new Color(200, 55, 55)).onClick(\"tag52\")\n", - "cplot5.add(plot5_2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag52" - ] - }, - "outputs": [], - "source": [ - "println (cplot5.subplots[1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cplot6 = new CombinedPlot()\n", - "def plot6_1 = new Plot(useToolTip: false);\n", - "plot6_1 << new Bars(x: (1..5), y: [12, 6, 8, 4, 8])\n", - " .onClick({info -> info.graphics.y[info.index]--; beakerx.runByTag(\"tag61\"); }) \n", - "cplot6.add(plot6_1)\n", - "\n", - "def plot6_2 = new Plot(useToolTip: false);\n", - "plot6_2 << new Bars(x: (1..5), y: [5, 2, 4, 3, 7], color: new Color(55, 200, 55))\n", - " .onClick({ beakerx.runByTag(\"tag62\"); })\n", - "cplot6.add(plot6_2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag61" - ] - }, - "outputs": [], - "source": [ - "def details61 = cplot6.subplots[0].details\n", - "def item61 = details61.graphics\n", - "def index61 = details61.index\n", - "println (item61.x[index61] + \":\" + item61.y[index61])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag62" - ] - }, - "outputs": [], - "source": [ - "println (cplot6.subplots[1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cplot7 = new CombinedPlot()\n", - "def plot7_1 = new Plot(useToolTip: false);\n", - "plot7_1 << new Bars(x: (1..5), y: [5, 2, 4, 3, 7]).onKey(KeyboardCodes.SHIFT, { info -> info.graphics.y[info.index]++; }) \n", - "cplot7.add(plot7_1)\n", - "\n", - "def plot7_2 = new Plot(useToolTip: false);\n", - "plot7_2 << new Bars(x: (1..5), y: [12, 6, 8, 4, 8], color: new Color(200, 55, 55)).onKey(\"T\", \"tag72\")\n", - "cplot7.add(plot7_2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag72" - ] - }, - "outputs": [], - "source": [ - "println (cplot7.subplots[1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cplot8 = new CombinedPlot()\n", - "def plot8_1 = new Plot(useToolTip: false);\n", - "plot8_1 << new Bars(x: (1..5), y: [12, 6, 8, 4, 8])\n", - " .onKey(KeyboardCodes.SHIFT,{info -> info.graphics.y[info.index]--; beakerx.runByTag(\"tag81\"); }) \n", - "cplot8.add(plot8_1)\n", - "\n", - "def plot8_2 = new Plot(useToolTip: false);\n", - "plot8_2 << new Bars(x: (1..5), y: [5, 2, 4, 3, 7], color: new Color(55, 200, 55))\n", - " .onKey(\"U\", { beakerx.runByTag(\"tag82\"); })\n", - "cplot8.add(plot8_2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag81" - ] - }, - "outputs": [], - "source": [ - "def details81 = cplot8.subplots[0].details\n", - "def item81 = details81.graphics\n", - "def index81 = details81.index\n", - "println (item81.x[index81] + \":\" + item81.y[index81])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "tag82" - ] - }, - "outputs": [], - "source": [ - "println (cplot8.subplots[1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Groovy", - "language": "groovy", - "name": "groovy" - }, - "language_info": { - "codemirror_mode": "groovy", - "file_extension": ".groovy", - "mimetype": "", - "name": "Groovy", - "nbconverter_exporter": "", - "version": "2.4.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/test/js/groovy/plotActionsTest.js b/test/js/groovy/plotActionsTest.js deleted file mode 100644 index 71944cdf34..0000000000 --- a/test/js/groovy/plotActionsTest.js +++ /dev/null @@ -1,224 +0,0 @@ -/* - * 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 BeakerXPageObject = require('../beakerx.po.js'); -var beakerxPO; - -describe('Testing of plot Actions ', function () { - - beforeAll(function () { - beakerxPO = new BeakerXPageObject(); - beakerxPO.runNotebookByUrl('/test/ipynb/groovy/PlotActionsTest.ipynb'); - }, 2); - - afterAll(function () { - beakerxPO.closeAndHaltNotebook(); - }); - - var cellIndex; - - describe('(Groovy) onKey action for Plot ', function(){ - var svgElement1; - - it('onKey "SHIFT" should change bar value ', function () { - cellIndex = 0; - svgElement1 = beakerxPO.runCellToGetSvgElement(cellIndex); - var height1 = Math.round(svgElement1.$('rect#i0_0').getAttribute('height')); - svgElement1.$('rect#i0_0').click(); - browser.keys("Shift"); - browser.keys('\uE000'); - beakerxPO.kernelIdleIcon.waitForEnabled(); - var height2 = Math.round(svgElement1.$('rect#i0_0').getAttribute('height')); - expect(height2).toBeGreaterThan(height1); - }); - - it('onKey "T" should run the tag (by string name) ', function () { - cellIndex += 1; - svgElement1.$('rect#i0_0').click(); - browser.keys("t"); - beakerxPO.kernelIdleIcon.waitForEnabled(); - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /1:6/); - }); - - it('onKey "U" should run the tag (by closure) ', function () { - svgElement1.$('rect#i0_2').click(); - browser.keys("u"); - beakerxPO.kernelIdleIcon.waitForEnabled(); - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /3:3/); - }); - }); - - describe('(Groovy) onClick action for Plot ', function(){ - var svgElement2; - - it('Click on the bar should change its value ', function () { - cellIndex += 1; - svgElement2 = beakerxPO.runCellToGetSvgElement(cellIndex); - var height1 = Math.round(svgElement2.$('rect#i0_0').getAttribute('height')); - svgElement2.$('rect#i0_0').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - var height2 = Math.round(svgElement2.$('rect#i0_0').getAttribute('height')); - expect(height2).toBeGreaterThan(height1); - }); - - it('Click on the bar should run the tag (by closure) ', function () { - cellIndex += 1; - svgElement2.$('rect#i0_1').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /2:3/); - }); - - it('Click on the bar should run the tag (by string name) ', function () { - cellIndex += 1; - var svgElement3 = beakerxPO.runCellToGetSvgElement(cellIndex); - svgElement3.$('rect#i0_0').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - cellIndex += 1; - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /1:5/); - }); - }); - - describe('(Groovy) RightClickAndDrag action for Plot ', function(){ - - it('RightClickAndDrag action should zoom the plot ', function () { - cellIndex += 1; - var svgElement4 = beakerxPO.runCellToGetSvgElement(cellIndex); - var height1 = Math.round(svgElement4.$('rect#i0_0').getAttribute('height')); - var width1 = Math.round(svgElement4.$('rect#i0_0').getAttribute('width')); - svgElement4.$('rect#i0_0').moveToObject(0, 0); - browser.buttonDown(2); - svgElement4.$('rect#i0_2').moveToObject(0, 0); - browser.pause(1000); - browser.buttonUp(2); - var height2 = Math.round(svgElement4.$('rect#i0_0').getAttribute('height')); - var width2 = Math.round(svgElement4.$('rect#i0_0').getAttribute('width')); - expect(height2).toBeGreaterThan(height1); - expect(width2).toBeGreaterThan(width1); - }); - - it('RightClickAndDrag should not display the menu ', function () { - expect(browser.$('div.p-Menu-itemLabel=Save as SVG').isVisible()).toBeFalsy(); - expect(browser.$('div.p-Menu-itemLabel=Save as PNG').isVisible()).toBeFalsy(); - }); - }); - - describe('(Groovy) RightClick action for Plot ', function(){ - - it('RightClick action should not zoom the plot ', function () { - beakerxPO.runCodeCellByIndex(cellIndex - 1); - var svgElement5 = beakerxPO.runCellToGetSvgElement(cellIndex); - var height1 = Math.round(svgElement5.$('rect#i0_0').getAttribute('height')); - var width1 = Math.round(svgElement5.$('rect#i0_0').getAttribute('width')); - svgElement5.$('g#maing').rightClick(); - var height2 = Math.round(svgElement5.$('rect#i0_0').getAttribute('height')); - var width2 = Math.round(svgElement5.$('rect#i0_0').getAttribute('width')); - expect(height2).toEqual(height1); - expect(width2).toEqual(width1); - }); - - it('RightClick action should display the menu ', function () { - browser.$('ul.p-Menu-content').waitForEnabled(); - expect(browser.$('div.p-Menu-itemLabel=Save as SVG').isVisible()).toBeTruthy(); - expect(browser.$('div.p-Menu-itemLabel=Save as PNG').isVisible()).toBeTruthy(); - }); - }); - - describe('(Groovy) onClick action for Combined Plot ', function(){ - var svgElements5, svgElements6; - - it('Click on the bar should change its value ', function () { - cellIndex += 1; - svgElements5 = beakerxPO.runCellToGetSvgElements(cellIndex); - var height1 = Math.round(svgElements5[0].$('rect#i0_0').getAttribute('height')); - svgElements5[0].$('rect#i0_0').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - svgElements5 = beakerxPO.getSvgElementsByIndex(cellIndex); - var height2 = Math.round(svgElements5[0].$('rect#i0_0').getAttribute('height')); - expect(height2).toBeGreaterThan(height1); - }); - - it('Click on the bar should run the tag (by string name) ', function () { - svgElements5[1].$('rect#i0_0').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - cellIndex += 1; - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /com.twosigma.beakerx.chart.xychart.Plot/); - }); - - it('The action details are available after click on the bar ', function () { - cellIndex += 1; - svgElements6 = beakerxPO.runCellToGetSvgElements(cellIndex); - svgElements6[0].$('rect#i0_1').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - svgElements6 = beakerxPO.getSvgElementsByIndex(cellIndex); - cellIndex += 1; - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /2:5/); - }); - - it('Click on the bar should run the tag (by closure) ', function () { - cellIndex += 1; - svgElements6[1].$('rect#i0_1').click(); - beakerxPO.kernelIdleIcon.waitForEnabled(); - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /com.twosigma.beakerx.chart.xychart.Plot/); - }); - }); - - describe('(Groovy) onKey action for Combined Plot ', function(){ - var svgElements7, svgElements8; - - it('onKey "SHIFT" should change bar value ', function () { - cellIndex += 1; - svgElements7 = beakerxPO.runCellToGetSvgElements(cellIndex); - var height1 = Math.round(svgElements7[0].$('rect#i0_0').getAttribute('height')); - svgElements7[0].$('rect#i0_0').click(); - browser.keys("Shift"); - browser.keys('\uE000'); - beakerxPO.kernelIdleIcon.waitForEnabled(); - svgElements7 = beakerxPO.getSvgElementsByIndex(cellIndex); - var height2 = Math.round(svgElements7[0].$('rect#i0_0').getAttribute('height')); - expect(height2).toBeGreaterThan(height1); - }); - - it('onKey "T" should run the tag (by string name) ', function () { - svgElements7[1].$('rect#i0_0').click(); - browser.keys("t"); - beakerxPO.kernelIdleIcon.waitForEnabled(); - svgElements7 = beakerxPO.getSvgElementsByIndex(cellIndex); - cellIndex += 1; - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /com.twosigma.beakerx.chart.xychart.Plot/); - }); - - it('The action details are available after onKey ', function () { - cellIndex += 1; - svgElements8 = beakerxPO.runCellToGetSvgElements(cellIndex); - svgElements8[0].$('rect#i0_1').click(); - browser.keys("Shift"); - browser.keys('\uE000'); - beakerxPO.kernelIdleIcon.waitForEnabled(); - svgElements8 = beakerxPO.getSvgElementsByIndex(cellIndex); - cellIndex += 1; - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /2:5/); - }); - - it('onKey "U" should run the tag (by closure) ', function () { - cellIndex += 1; - svgElements8[1].$('rect#i0_1').click(); - browser.keys("u"); - beakerxPO.kernelIdleIcon.waitForEnabled(); - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, /com.twosigma.beakerx.chart.xychart.Plot/); - }); - }); - -}); \ No newline at end of file diff --git a/test/js/python/initCellsPythonTest.js b/test/js/python/initCellsPythonTest.js deleted file mode 100644 index 640457a54c..0000000000 --- a/test/js/python/initCellsPythonTest.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 BeakerXPageObject = require('../beakerx.po.js'); -var beakerxPO; - -describe('(Python) Testing of init cells', function() { - beforeAll(function () { - beakerxPO = new BeakerXPageObject(); - beakerxPO.runNotebookByUrl('/test/ipynb/python/InitCellsTest.ipynb'); - }, 2); - - afterAll(function () { - beakerxPO.closeAndHaltNotebook(); - }); - - var cellIndex; - - describe('(Python) Init cells ', function() { - it('Init cells display correct output ', function(){ - cellIndex = 0; - beakerxPO.runCodeCellByIndex(cellIndex); - var currentDate = Date().split(' ').slice(0, 4).join(' '); - beakerxPO.waitAndCheckOutputTextOfStdout(cellIndex, new RegExp(currentDate)); - }); - }); - - describe('(Python) Press "Tab" to autocomplete code ', function(){ - it('Autocomplete list is not empty ', function(){ - cellIndex += 1; - var codeCell = beakerxPO.getCodeCellByIndex(cellIndex); - var completeList = beakerxPO.callAutocompleteAndGetItsList(codeCell, 'im'); - expect(completeList.length).toBeGreaterThan(0); - }); - }); - - describe('(Python) Press "Shift + Tab" to display doc ', function(){ - it('doc tooltip is not empty ', function(){ - cellIndex += 1; - var codeCell = beakerxPO.getCodeCellByIndex(cellIndex); - var tooltip = beakerxPO.callDocAndGetItsTooltip(codeCell, 'time'); - expect(tooltip.getText().length).toBeGreaterThan(0); - }); - }); - -}); diff --git a/test/notebooks/groovy/AutoTranslationGroovyTest.ipynb b/test/notebooks/groovy/AutoTranslationGroovyTest.ipynb new file mode 100644 index 0000000000..838c2c73a7 --- /dev/null +++ b/test/notebooks/groovy/AutoTranslationGroovyTest.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "92c91f77-7bdc-4ed8-be5e-f114281fa4ab", + "version_major": 2, + "version_minor": 0 + }, + "method": "display_data" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def nodes = []\n", + "\n", + "for (x in (0..10)){\n", + " nodes.add(radius: x*5 + 5, colorB:(x*20))\n", + "}\n", + "\n", + "beakerx.testData = [nodes: nodes]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "require.config({\r\n", + " paths: {\r\n", + " d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'\r\n", + " }});" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%javascript\n", + "require.config({\n", + " paths: {\n", + " d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'\n", + " }});" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%html\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "\r\n", + "beakerx.displayHTML(this, '
');\r\n", + "\r\n", + "var testData = beakerx.testData\r\n", + "\r\n", + "var d3 = require(['d3'], function (d3) {\r\n", + " \r\n", + " var width = 600,\r\n", + " height = 200;\r\n", + "\r\n", + " var svg = d3.select(\"#bkrx\")\r\n", + " .append(\"svg\")\r\n", + " .attr(\"width\", width)\r\n", + " .attr(\"height\", height)\r\n", + " .attr(\"transform\", \"translate(\"+[100, 0]+\")\");\r\n", + "\r\n", + " var node = svg.selectAll()\r\n", + " .data(testData.nodes)\r\n", + " .enter().append(\"circle\")\r\n", + " .attr(\"class\", \"moon\")\r\n", + " .attr(\"r\", function(d) { return d.radius; })\r\n", + " .attr(\"cx\", function(d, i) { return i*40 + d.radius; })\r\n", + " .attr(\"cy\", function(d, i) { return 50 + d.radius; })\r\n", + " .style(\"fill\", function(d) { return d3.rgb(100, 100 , d.colorB); }); \r\n", + "});" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%javascript\n", + "\n", + "beakerx.displayHTML(this, '
');\n", + "\n", + "var testData = beakerx.testData\n", + "\n", + "var d3 = require(['d3'], function (d3) {\n", + " \n", + " var width = 600,\n", + " height = 200;\n", + "\n", + " var svg = d3.select(\"#bkrx\")\n", + " .append(\"svg\")\n", + " .attr(\"width\", width)\n", + " .attr(\"height\", height)\n", + " .attr(\"transform\", \"translate(\"+[100, 0]+\")\");\n", + "\n", + " var node = svg.selectAll()\n", + " .data(testData.nodes)\n", + " .enter().append(\"circle\")\n", + " .attr(\"class\", \"moon\")\n", + " .attr(\"r\", function(d) { return d.radius; })\n", + " .attr(\"cx\", function(d, i) { return i*40 + d.radius; })\n", + " .attr(\"cy\", function(d, i) { return 50 + d.radius; })\n", + " .style(\"fill\", function(d) { return d3.rgb(100, 100 , d.colorB); }); \n", + "});" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Groovy", + "language": "groovy", + "name": "groovy" + }, + "language_info": { + "codemirror_mode": "groovy", + "file_extension": ".groovy", + "mimetype": "", + "name": "Groovy", + "nbconverter_exporter": "", + "version": "2.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/notebooks/groovy/BigIntsTest.ipynb b/test/notebooks/groovy/BigIntsTest.ipynb new file mode 100644 index 0000000000..de1c8b4998 --- /dev/null +++ b/test/notebooks/groovy/BigIntsTest.ipynb @@ -0,0 +1,98 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Large Integers in Tables and Autotranslation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "JSON has a problem when integers get larger than 1016\n", + "because JavaScript uses floating point for all its numbers.\n", + "Most languages, including Python, R, and the JVM languages like Groovy can easily handle integers up to 1019,\n", + "and sometimes larger.\n", + "\n", + "Numbers this large are useful for measuring time in nanoseconds, for example.\n", + "\n", + "BeakerX table and plot widgets have special support 64-bit and arbitrary precision values. Run these cells, then over over the columns to see their types." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 64-bit Longs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def millis = 1234567890L\n", + "def nanos = millis * 1000 * 1000L\n", + "\n", + "table = [[time: nanos + 7 * 1, next_time:(nanos + 77) * 1, temp:14.6],\n", + " [time: nanos + 7 * 2, next_time:(nanos + 88) * 2, temp:18.1],\n", + " [time: nanos + 7 * 3, next_time:(nanos + 99) * 3, temp:23.6]]\n", + "\n", + "beakerx.table_with_longs = table\n", + "table" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## BigNums (arbitrary precision)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def millis = 1987654321g\n", + "// the \"g\" makes this a bignum, with as many bits as needed\n", + "def nanos = millis * 1000 * 1000g\n", + "\n", + "table = [[time: nanos + 7 * 1, next_time:(nanos + 77) * 777, temp:3.351],\n", + " [time: nanos + 7 * 2, next_time:(nanos + 88) * 888, temp:2.355],\n", + " [time: nanos + 7 * 3, next_time:(nanos + 99) * 999, temp:2.728]]\n", + "\n", + "beakerx.table_with_big_integers = table\n", + "table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Groovy", + "language": "groovy", + "name": "groovy" + }, + "language_info": { + "codemirror_mode": "groovy", + "file_extension": ".groovy", + "mimetype": "", + "name": "Groovy", + "nbconverter_exporter": "", + "version": "2.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/notebooks/groovy/handlingCombinationOfCodeAndMagics.ipynb b/test/notebooks/groovy/handlingCombinationOfCodeAndMagics.ipynb new file mode 100644 index 0000000000..00a4cd6d16 --- /dev/null +++ b/test/notebooks/groovy/handlingCombinationOfCodeAndMagics.ipynb @@ -0,0 +1,107 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "println('test1')\n", + "%time println(221)\n", + "println('test2')\n", + " %time 1+2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "println('test1')\n", + "if(true){\n", + " %time println(221)\n", + "}\n", + "println('test2')\n", + " %time 1+2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%classpath add jar ../../resources/jar/testdemo.jar\n", + "\n", + "%import com.twosigma.beakerx.widgets.integers.IntSlider" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "w = new IntSlider()\n", + "w.value = 60\n", + "w" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import com.example.TestDemo\n", + "TestDemo testDemo = new TestDemo();\n", + "println testDemo.getObjectTest()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%time println(\"x y\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%classpath add jar \"../../resources/jar/ with space.jar\" " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Groovy", + "language": "groovy", + "name": "groovy" + }, + "language_info": { + "codemirror_mode": "groovy", + "file_extension": ".groovy", + "mimetype": "", + "name": "Groovy", + "nbconverter_exporter": "", + "version": "2.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/notebooks/python/AutoTranslationPythonTest.ipynb b/test/notebooks/python/AutoTranslationPythonTest.ipynb new file mode 100644 index 0000000000..1cd6325849 --- /dev/null +++ b/test/notebooks/python/AutoTranslationPythonTest.ipynb @@ -0,0 +1,195 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0425e93c990544fcbcbbf0d3895af7b2", + "version_major": 2, + "version_minor": 0 + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from beakerx import *\n", + "\n", + "nodes = []\n", + "\n", + "for i in range(0, 10):\n", + " nodes.append({\"radius\": int(i*5 + 5), \"colorB\": int(i*20)})\n", + "\n", + "beakerx.testData = {\"nodes\": nodes}\n", + "\n", + "TableDisplay(nodes)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "require.config({\n", + " paths: {\n", + " d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'\n", + " }});" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%javascript\n", + "require.config({\n", + " paths: {\n", + " d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'\n", + " }});" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%html\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "\n", + "beakerx.displayHTML(this, '
');\n", + "\n", + "var testData = beakerx.testData\n", + "\n", + "var d3 = require(['d3'], function (d3) {\n", + " \n", + " var width = 600,\n", + " height = 200;\n", + "\n", + " var svg = d3.select(\"#bkrx\")\n", + " .append(\"svg\")\n", + " .attr(\"width\", width)\n", + " .attr(\"height\", height)\n", + " .attr(\"transform\", \"translate(\"+[100, 0]+\")\");\n", + "\n", + " var node = svg.selectAll()\n", + " .data(testData.nodes)\n", + " .enter().append(\"circle\")\n", + " .attr(\"class\", \"moon\")\n", + " .attr(\"r\", function(d) { return d.radius; })\n", + " .attr(\"cx\", function(d, i) { return i*40 + d.radius; })\n", + " .attr(\"cy\", function(d, i) { return 50 + d.radius; })\n", + " .style(\"fill\", function(d) { return d3.rgb(100, 100 , d.colorB); }); \n", + "});" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%javascript\n", + "\n", + "beakerx.displayHTML(this, '
');\n", + "\n", + "var testData = beakerx.testData\n", + "\n", + "var d3 = require(['d3'], function (d3) {\n", + " \n", + " var width = 600,\n", + " height = 200;\n", + "\n", + " var svg = d3.select(\"#bkrx\")\n", + " .append(\"svg\")\n", + " .attr(\"width\", width)\n", + " .attr(\"height\", height)\n", + " .attr(\"transform\", \"translate(\"+[100, 0]+\")\");\n", + "\n", + " var node = svg.selectAll()\n", + " .data(testData.nodes)\n", + " .enter().append(\"circle\")\n", + " .attr(\"class\", \"moon\")\n", + " .attr(\"r\", function(d) { return d.radius; })\n", + " .attr(\"cx\", function(d, i) { return i*40 + d.radius; })\n", + " .attr(\"cy\", function(d, i) { return 50 + d.radius; })\n", + " .style(\"fill\", function(d) { return d3.rgb(100, 100 , d.colorB); }); \n", + "});" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/tests/groovy/autotranslationGroovyTest.js b/test/tests/groovy/autotranslationGroovyTest.js new file mode 100644 index 0000000000..ce0be872d3 --- /dev/null +++ b/test/tests/groovy/autotranslationGroovyTest.js @@ -0,0 +1,93 @@ +/* + * Copyright 2018 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 BeakerXPageObject = require('../beakerx.po.js'); +var beakerxPO; + +describe('Autotranslation Groovy to JavaScript and D3 tests', function () { + + beforeAll(function () { + beakerxPO = new BeakerXPageObject(); + beakerxPO.runNotebookByUrl('/notebooks/test/notebooks/groovy/AutoTranslationGroovyTest.ipynb'); + }); + + afterAll(function () { + beakerxPO.closeAndHaltNotebook(); + }); + + describe('Groovy code', function(){ + it('Output contains data table', function(){ + var codeCell = beakerxPO.runCodeCellByIndex(0); + var bkoTable = codeCell.$('div.bko-table'); + expect(bkoTable.isEnabled()).toBeTruthy(); + var tblText = bkoTable.$('tbody > tr').getText(); + expect(tblText).toMatch('nodes'); + expect(tblText).toMatch('"radius":10'); + expect(tblText).toMatch('"colorB":20'); + }); + }); + + describe('JavaScript and D3 code', function(){ + var svgElement; + + it('Output contains svg tag', function(){ + beakerxPO.runCodeCellByIndex(1); + beakerxPO.runCodeCellByIndex(2); + var codeCell = beakerxPO.runCodeCellByIndex(3); + browser.pause(2000); + svgElement = codeCell.$('div#bkrx > svg'); + expect(svgElement.isEnabled()).toBeTruthy(); + }); + + it('Should set width, height and transform attributes to svg', function(){ + expect(Math.round(svgElement.getAttribute('width'))).toEqual(600); + expect(Math.round(svgElement.getAttribute('height'))).toEqual(200); + expect(svgElement.getAttribute('transform')).toMatch('translate'); + }); + + it('svg has 11 circles', function(){ + expect(svgElement.$$('circle').length).toEqual(11); + }); + + it('First and last circles has "class"= "moon"', function(){ + expect(svgElement.$$('circle')[0].getAttribute('class')).toEqual('moon'); + expect(svgElement.$$('circle')[10].getAttribute('class')).toEqual('moon'); + }); + + it('First circle has "r"=5, "cx"=5, "cy"=55', function(){ + var fCircle = svgElement.$$('circle')[0]; + expect(Math.round(fCircle.getAttribute('r'))).toEqual(5); + expect(Math.round(fCircle.getAttribute('cx'))).toEqual(5); + expect(Math.round(fCircle.getAttribute('cy'))).toEqual(55); + }); + + it('Last circle has "r"=55, "cx"=455, "cy"=105', function(){ + var lCircle = svgElement.$$('circle')[10]; + expect(Math.round(lCircle.getAttribute('r'))).toEqual(55); + expect(Math.round(lCircle.getAttribute('cx'))).toEqual(455); + expect(Math.round(lCircle.getAttribute('cy'))).toEqual(105); + }); + + it('First circle has color rgb(100,100,0)', function(){ + expect(svgElement.$$('circle')[0].getCssProperty('fill').value).toEqual('rgb(100,100,0)'); + }); + + it('Last circle has color rgb(100,100,200)', function(){ + expect(svgElement.$$('circle')[10].getCssProperty('fill').value).toEqual('rgb(100,100,200)'); + }); + }); + +}); diff --git a/test/tests/groovy/bigIntsTest.js b/test/tests/groovy/bigIntsTest.js new file mode 100644 index 0000000000..9b58ddcea5 --- /dev/null +++ b/test/tests/groovy/bigIntsTest.js @@ -0,0 +1,155 @@ +/* + * 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 BeakerXPageObject = require('../beakerx.po.js'); +var beakerxPO; + +describe('big ints tests', function () { + + beforeAll(function () { + beakerxPO = new BeakerXPageObject(); + beakerxPO.runNotebookByUrl('/notebooks/test/notebooks/groovy/BigIntsTest.ipynb'); + }, 2); + + afterAll(function () { + beakerxPO.closeAndHaltNotebook(); + }); + + describe('64-bit Longs labels and values', function () { + + it('Table has proper column labels', function () { + var tableColumnLabel; + var cellText; + var codeCell = beakerxPO.runCodeCellByIndex(0); + beakerxPO.kernelIdleIcon.waitForEnabled(); + + tableColumnLabel = beakerxPO.getTableColumnLabel(0, 0); + cellText = tableColumnLabel.getText(); + expect(cellText).toMatch('time'); + + tableColumnLabel = beakerxPO.getTableColumnLabel(0, 1); + cellText = tableColumnLabel.getText(); + expect(cellText).toMatch('next_time'); + + tableColumnLabel = beakerxPO.getTableColumnLabel(0, 2); + cellText = tableColumnLabel.getText(); + expect(cellText).toMatch('temp'); + }); + + it('Table has proper cell values', function () { + var tableCell; + var cellText; + tableCell = beakerxPO.getTableCell(0, 0, 1); + cellText = tableCell.getText(); + expect(cellText).toMatch('1234567890000007'); + + tableCell = beakerxPO.getTableCell(0, 0, 2); + cellText = tableCell.getText(); + expect(cellText).toMatch('1234567890000077'); + + tableCell = beakerxPO.getTableCell(0, 0, 3); + cellText = tableCell.getText(); + expect(cellText).toMatch('14.600'); + + tableCell = beakerxPO.getTableCell(0, 1, 1); + cellText = tableCell.getText(); + expect(cellText).toMatch('1234567890000014'); + + tableCell = beakerxPO.getTableCell(0, 1, 2); + cellText = tableCell.getText(); + expect(cellText).toMatch('2469135780000176'); + + tableCell = beakerxPO.getTableCell(0, 1, 3); + cellText = tableCell.getText(); + expect(cellText).toMatch('18.100'); + + tableCell = beakerxPO.getTableCell(0, 2, 1); + cellText = tableCell.getText(); + expect(cellText).toMatch('1234567890000021'); + + tableCell = beakerxPO.getTableCell(0, 2, 2); + cellText = tableCell.getText(); + expect(cellText).toMatch('3703703670000297'); + + tableCell = beakerxPO.getTableCell(0, 2, 3); + cellText = tableCell.getText(); + expect(cellText).toMatch('23.600'); + }); + }); + + describe('BigNums (arbitrary precision) labels and values', function () { + + it('Table has proper column labels', function () { + var tableColumnLabel; + var cellText; + beakerxPO.runCodeCellByIndex(1); + beakerxPO.kernelIdleIcon.waitForEnabled(); + + tableColumnLabel = beakerxPO.getTableColumnLabel(1, 0); + cellText = tableColumnLabel.getText(); + expect(cellText).toMatch('time'); + + tableColumnLabel = beakerxPO.getTableColumnLabel(1, 1); + cellText = tableColumnLabel.getText(); + expect(cellText).toMatch('next_time'); + + tableColumnLabel = beakerxPO.getTableColumnLabel(1, 2); + cellText = tableColumnLabel.getText(); + expect(cellText).toMatch('temp'); + }); + + it('Table has proper cell values', function () { + var tableCell; + var cellText; + tableCell = beakerxPO.getTableCell(1, 0, 1); + cellText = tableCell.getText(); + expect(cellText).toMatch('1987654321000007'); + + tableCell = beakerxPO.getTableCell(1, 0, 2); + cellText = tableCell.getText(); + expect(cellText).toMatch('1544407407417059829'); + + tableCell = beakerxPO.getTableCell(1, 0, 3); + cellText = tableCell.getText(); + expect(cellText).toMatch('3.351'); + + tableCell = beakerxPO.getTableCell(1, 1, 1); + cellText = tableCell.getText(); + expect(cellText).toMatch('1987654321000014'); + + tableCell = beakerxPO.getTableCell(1, 1, 2); + cellText = tableCell.getText(); + expect(cellText).toMatch('1765037037048078144'); + + tableCell = beakerxPO.getTableCell(1, 1, 3); + cellText = tableCell.getText(); + expect(cellText).toMatch('2.355'); + + tableCell = beakerxPO.getTableCell(1, 2, 1); + cellText = tableCell.getText(); + expect(cellText).toMatch('1987654321000021'); + + tableCell = beakerxPO.getTableCell(1, 2, 2); + cellText = tableCell.getText(); + expect(cellText).toMatch('1985666666679098901'); + + tableCell = beakerxPO.getTableCell(1, 2, 3); + cellText = tableCell.getText(); + expect(cellText).toMatch('2.728'); + }); + }); + +}); \ No newline at end of file diff --git a/test/tests/groovy/handlingCombinationOfCodeAndMagics.js b/test/tests/groovy/handlingCombinationOfCodeAndMagics.js new file mode 100644 index 0000000000..e98f289330 --- /dev/null +++ b/test/tests/groovy/handlingCombinationOfCodeAndMagics.js @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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 BeakerXPageObject = require('../beakerx.po.js'); +var beakerxPO; + +describe('Tests for combination of code and magics', function () { + + beforeAll(function () { + beakerxPO = new BeakerXPageObject(); + beakerxPO.runNotebookByUrl('/notebooks/test/notebooks/groovy/handlingCombinationOfCodeAndMagics.ipynb'); + }); + + afterAll(function () { + beakerxPO.closeAndHaltNotebook(); + }); + + var cellIndex = 0; + var timeExp = /CPU times: user \d+.+, sys: \d+.+, total: \d+.+\n+Wall Time: \d+/; + var errorExp = /org.codehaus.groovy.control.MultipleCompilationErrorsException:/; + + describe('Combination of code and magics', function () { + it('mixing of println code and %time magic', function () { + var outputs = beakerxPO.runCodeCellByIndex(cellIndex).$$('div.output_subarea.output_text'); + browser.pause(2000); + expect(outputs[0].getText()).toMatch(new RegExp('test1\n221\n' + timeExp.source)); + expect(outputs[0].getText()).toMatch(new RegExp('test2\n' + timeExp.source)); + expect(outputs[1].getText()).toMatch(/3/); + }); + + it('%import magic inside code', function () { + cellIndex += 1; + var outputs = beakerxPO.runCodeCellByIndex(cellIndex).$$('div.output_subarea.output_text'); + browser.pause(2000); + expect(outputs[0].getText()).toMatch(errorExp); + expect(outputs[1].getText()).toMatch(new RegExp('221\n' + timeExp.source)); + expect(outputs[2].getText()).toMatch(errorExp); + expect(outputs[3].getText()).toMatch(timeExp); + expect(outputs[4].getText()).toMatch(/3/); + }); + + it('Using of the spaces in %classpath and %import magics', function () { + cellIndex += 1; + beakerxPO.runCellAndCheckOutputText(cellIndex, /Added jar:.+testdemo\.jar.+/); + }); + + it('Cell has IntSlider widget', function () { + cellIndex += 1; + var widget = beakerxPO.runCellToGetWidgetElement(cellIndex); + expect(widget.$('div.slider-container').isEnabled()).toBeTruthy(); + }); + + it('Output contains "Demo_test_123"', function () { + cellIndex += 1; + beakerxPO.kernelIdleIcon.waitForEnabled(); + beakerxPO.runCellAndCheckOutputText(cellIndex, 'Demo_test_123'); + }); + + it('Using of the spaces in %time magic and println code', function () { + cellIndex += 1; + beakerxPO.runCellAndCheckOutputText(cellIndex, new RegExp(/x\s{4}y\n/.source + timeExp.source)); + }); + + it('%classpath for jar which contains spaces in name', function () { + cellIndex += 1; + beakerxPO.runCellAndCheckOutputText(cellIndex, /Added jar:.+ with space\.jar.+/); + }); + }); + +}); \ No newline at end of file diff --git a/test/tests/python/autotranslationPythonTest.js b/test/tests/python/autotranslationPythonTest.js new file mode 100644 index 0000000000..4c121b2803 --- /dev/null +++ b/test/tests/python/autotranslationPythonTest.js @@ -0,0 +1,93 @@ +/* + * Copyright 2018 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 BeakerXPageObject = require('../beakerx.po.js'); +var beakerxPO; + +describe('Autotranslation Python to JavaScript and D3 tests', function () { + + beforeAll(function () { + beakerxPO = new BeakerXPageObject(); + beakerxPO.runNotebookByUrl('/notebooks/test/notebooks/python/AutoTranslationPythonTest.ipynb'); + }); + + afterAll(function () { + beakerxPO.closeAndHaltNotebook(); + }); + + describe('Python code', function(){ + it('Output contains data table', function(){ + var codeCell = beakerxPO.runCodeCellByIndex(0); + var bkoTable = codeCell.$('div.bko-table'); + expect(bkoTable.isEnabled()).toBeTruthy(); + var headTable = bkoTable.$('div.dataTables_scrollHead'); + expect(headTable.$('th[data-column-index="1"]').getText()).toMatch('radius'); + expect(headTable.$('th[data-column-index="2"]').getText()).toMatch('colorB'); + expect(bkoTable.$('div.dataTables_scrollBody').$$('tbody > tr').length).toEqual(10); + }); + }); + + describe('JavaScript and D3 code', function(){ + var svgElement; + + it('Output contains svg tag', function(){ + beakerxPO.runCodeCellByIndex(1); + beakerxPO.runCodeCellByIndex(2); + var codeCell = beakerxPO.runCodeCellByIndex(3); + browser.pause(2000); + svgElement = codeCell.$('div#bkrx > svg'); + expect(svgElement.isEnabled()).toBeTruthy(); + }); + + it('Should set width, height and transform attributes to svg', function(){ + expect(Math.round(svgElement.getAttribute('width'))).toEqual(600); + expect(Math.round(svgElement.getAttribute('height'))).toEqual(200); + expect(svgElement.getAttribute('transform')).toMatch('translate'); + }); + + it('svg has 10 circles', function(){ + expect(svgElement.$$('circle').length).toEqual(10); + }); + + it('First and last circles has "class"= "moon"', function(){ + expect(svgElement.$$('circle')[0].getAttribute('class')).toEqual('moon'); + expect(svgElement.$$('circle')[9].getAttribute('class')).toEqual('moon'); + }); + + it('First circle has "r"=5, "cx"=5, "cy"=55', function(){ + var fCircle = svgElement.$$('circle')[0]; + expect(Math.round(fCircle.getAttribute('r'))).toEqual(5); + expect(Math.round(fCircle.getAttribute('cx'))).toEqual(5); + expect(Math.round(fCircle.getAttribute('cy'))).toEqual(55); + }); + + it('Last circle has "r"=50, "cx"=410, "cy"=100', function(){ + var lCircle = svgElement.$$('circle')[9]; + expect(Math.round(lCircle.getAttribute('r'))).toEqual(50); + expect(Math.round(lCircle.getAttribute('cx'))).toEqual(410); + expect(Math.round(lCircle.getAttribute('cy'))).toEqual(100); + }); + + it('First circle has color rgb(100,100,0)', function(){ + expect(svgElement.$$('circle')[0].getCssProperty('fill').value).toEqual('rgb(100,100,0)'); + }); + + it('Last circle has color rgb(100,100,180)', function(){ + expect(svgElement.$$('circle')[9].getCssProperty('fill').value).toEqual('rgb(100,100,180)'); + }); + }); + +}); \ No newline at end of file diff --git a/test/tests/python/tableAPIPythonTest.js b/test/tests/python/tableAPIPythonTest.js new file mode 100644 index 0000000000..dffbedec3e --- /dev/null +++ b/test/tests/python/tableAPIPythonTest.js @@ -0,0 +1,176 @@ +/* + * Copyright 2018 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 BeakerXPageObject = require('../beakerx.po.js'); +var TableHelperObject = require('../table.helper.js'); +var beakerxPO; +var tableHelper; + +describe('Testing of table (python)', function () { + + beforeAll(function () { + beakerxPO = new BeakerXPageObject(); + tableHelper = new TableHelperObject(); + beakerxPO.runNotebookByUrl('/notebooks/test/notebooks/python/TableAPIPythonTest.ipynb'); + }); + + afterAll(function () { + beakerxPO.closeAndHaltNotebook(); + }); + + function checkRowValues(tblElement, rowIndex, cell1, cell2, cell3){ + expect(tableHelper.getTableBodyCell(tblElement, rowIndex, 0).getText()).toMatch(cell1); + expect(tableHelper.getTableBodyCell(tblElement, rowIndex, 1).getText()).toMatch(cell2); + expect(tableHelper.getTableBodyCell(tblElement, rowIndex, 2).getText()).toMatch(cell3); + } + + function checkHeaderValues(tblElement, value1, value2){ + expect(tableHelper.getTableHeaderCell(tblElement, 1).getText()).toMatch(value1); + expect(tableHelper.getTableHeaderCell(tblElement, 2).getText()).toMatch(value2); + } + + function checkTableRows(tblElement, lenght){ + expect(tableHelper.getTableAllRows(tblElement).length).toEqual(lenght); + } + + var cellIndex; + + describe("Data types for TableDisplay", function(){ + + it('Can use Array of Integers parameter', function () { + cellIndex = 0; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', 'a', '100'); + checkTableRows(tBody, 3); + checkHeaderValues(tHead, 'Key', 'Value'); + }); + + it('Can use 2D Array of Integers parameter', function () { + cellIndex += 1; + var tBody = beakerxPO.runCellToGetTableElement(cellIndex).$('tbody'); + checkRowValues(tBody, 0, '0', '1', ''); + checkTableRows(tBody, 2); + }); + + it('Can use Array of Decimals parameter', function () { + cellIndex += 1; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', 'a', '0\.1'); + checkTableRows(tBody, 3); + checkHeaderValues(tHead, 'Key', 'Value'); + }); + + it('Can use 2D Array of Decimals parameter', function () { + cellIndex += 1; + var tBody = beakerxPO.runCellToGetTableElement(cellIndex).$('tbody'); + checkRowValues(tBody, 0, '0', '0\.1', ''); + checkTableRows(tBody, 2); + }); + + it('Can use Array of Strings parameter', function () { + cellIndex += 1; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', 'a', 'a a a'); + checkTableRows(tBody, 3); + checkHeaderValues(tHead, 'Key', 'Value'); + }); + + it('Can use 2D Array of Strings parameter', function () { + cellIndex += 1; + var tBody = beakerxPO.runCellToGetTableElement(cellIndex).$('tbody'); + checkRowValues(tBody, 0, '0', 'a', ''); + checkTableRows(tBody, 2); + }); + + it('Can use Array of Integer Arrays parameter', function () { + cellIndex += 1; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', 'a', '[1, 2, 3]'); + checkTableRows(tBody, 3); + checkHeaderValues(tHead, 'Key', 'Value'); + }); + + it('Can use 2D Array of Integer Arrays parameter', function () { + cellIndex +=1 ; + var tBody = beakerxPO.runCellToGetTableElement(cellIndex).$('tbody'); + checkRowValues(tBody, 0, '0', '[1,2,3]', ''); + checkTableRows(tBody, 2); + }); + + it('Can use 2D Array of Integer,Decimal,String,Array Arrays parameter', function () { + cellIndex +=1 ; + var tBody = beakerxPO.runCellToGetTableElement(cellIndex).$('tbody'); + checkRowValues(tBody, 0, '0', '100', '200'); + checkRowValues(tBody, 1, '1', '0\.1', '0\.05'); + checkRowValues(tBody, 2, '2', 'a a a', 'b b b'); + checkRowValues(tBody, 3, '3', '[1,2,3]', '[10,20,30]'); + checkTableRows(tBody, 4); + }); + + it('Can use [Integer,Decimal,String,Array] parameter', function () { + cellIndex +=1 ; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', 'a', '100'); + checkRowValues(tBody, 1, '1', 'b', '0\.05'); + checkRowValues(tBody, 2, '2', 'c', 'c c c'); + checkRowValues(tBody, 3, '3', 'd', '[1,2,3]'); + checkTableRows(tBody, 4); + checkHeaderValues(tHead, 'Key', 'Value'); + }); + + it('Can use 2D Arrays of [Integer,Decimal,String,Array] parameter', function () { + cellIndex +=1 ; + var tBody = beakerxPO.runCellToGetTableElement(cellIndex).$('tbody'); + checkRowValues(tBody, 0, '0', '10', '0\.1'); + checkRowValues(tBody, 1, '1', '100', '0\.05'); + checkTableRows(tBody, 2); + }); + + it('Can use numbers as name of Array keys (Array parameter)', function () { + cellIndex +=1 ; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', '10', '20'); + checkRowValues(tBody, 1, '1', '0\.1', '0\.05'); + checkTableRows(tBody, 4); + checkHeaderValues(tHead, 'Key', 'Value'); + }); + + it('Can use numbers as name of Array keys (2D Array parameter)', function () { + cellIndex +=1 ; + var dtContainer = beakerxPO.runCellToGetDtContainer(cellIndex); + var tBody = tableHelper.getDataTableBody(dtContainer); + var tHead = tableHelper.getDataTablesScrollHead(dtContainer); + checkRowValues(tBody, 0, '0', '10', '0\.1'); + checkRowValues(tBody, 1, '1', '20', '0\.05'); + checkTableRows(tBody, 2); + checkHeaderValues(tHead, '10', '0\.1'); + }); + + }); + +}); \ No newline at end of file diff --git a/test/tests/table.helper.js b/test/tests/table.helper.js new file mode 100644 index 0000000000..46bdec5981 --- /dev/null +++ b/test/tests/table.helper.js @@ -0,0 +1,61 @@ +/* + * Copyright 2018 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 TableHelperObject = function () { + + this.getDataTablesScrollBody = function(dtcontainer){ + return dtcontainer.$('.dataTables_scrollBody'); + } + + this.getDataTablesScrollHead = function(dtcontainer){ + return dtcontainer.$('.dataTables_scrollHead'); + } + + this.getDataTableBody = function(dtcontainer){ + return this.getDataTablesScrollBody(dtcontainer).$('tbody'); + } + + this.dataTablesIsEnabled = function(dtcontainer){ + var dataTables = dtcontainer.$('.dataTables_scroll'); + dataTables.waitForEnabled(); + } + + this.getTableRow = function(tblElement, rowIndex){ + return this.getTableAllRows(tblElement)[rowIndex]; + } + + this.getTableAllRows = function(tblElement){ + return tblElement.$$('tr[role="row"]'); + } + + this.getTableBodyCell = function(tblElement, rowIndex, colIndex){ + return this.getTableBodyRowAllCells(tblElement, rowIndex)[colIndex]; + } + + this.getTableBodyRowAllCells = function(tblElement, rowIndex){ + return this.getTableRow(tblElement, rowIndex).$$('td.ui-selectee'); + } + + this.getTableHeaderAllCells = function(tblElement){ + return this.getTableRow(tblElement, 0).$$('th'); + } + + this.getTableHeaderCell = function(tblElement, rowIndex){ + return this.getTableHeaderAllCells(tblElement, 0)[rowIndex]; + } + +}; +module.exports = TableHelperObject; \ No newline at end of file