diff --git a/README.md b/README.md index bad451b7..3760f426 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,12 @@ [![PyPI](https://img.shields.io/pypi/pyversions/dtale.svg)](https://pypi.python.org/pypi/dtale/) [![ReadTheDocs](https://readthedocs.org/projects/dtale/badge)](https://dtale.readthedocs.io) [![codecov](https://codecov.io/gh/manahl/dtale/branch/master/graph/badge.svg)](https://codecov.io/gh/manahl/dtale) +[![Downloads](https://pepy.tech/badge/dtale)](https://pepy.tech/project/dtale) ## Getting Started +![](https://raw.githubusercontent.com/manahl/dtale/master/docs/images/blog/dtale_demo_mini.gif) + Setup/Activate your environment and install the egg ```bash diff --git a/docs/images/blog/dtale_demo_mini.gif b/docs/images/blog/dtale_demo_mini.gif new file mode 100644 index 00000000..dce66644 Binary files /dev/null and b/docs/images/blog/dtale_demo_mini.gif differ diff --git a/static/__tests__/dtale/DataViewer-reload-test.jsx b/static/__tests__/dtale/DataViewer-reload-test.jsx new file mode 100644 index 00000000..a0e3eb94 --- /dev/null +++ b/static/__tests__/dtale/DataViewer-reload-test.jsx @@ -0,0 +1,101 @@ +import { mount } from "enzyme"; +import _ from "lodash"; +import React from "react"; +import { Provider } from "react-redux"; + +import { RemovableError } from "../../RemovableError"; +import mockPopsicle from "../MockPopsicle"; +import * as t from "../jest-assertions"; +import reduxUtils from "../redux-test-utils"; +import { withGlobalJquery } from "../test-utils"; + +const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "offsetHeight"); +const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "offsetWidth"); + +describe("DataViewer tests", () => { + beforeAll(() => { + Object.defineProperty(HTMLElement.prototype, "offsetHeight", { configurable: true, value: 500 }); + Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 500 }); + + const mockBuildLibs = withGlobalJquery(() => + mockPopsicle.mock(url => { + const { urlFetcher } = require("../redux-test-utils").default; + return urlFetcher(url); + }) + ); + + const mockChartUtils = withGlobalJquery(() => (ctx, cfg) => { + const chartCfg = { ctx, cfg, data: cfg.data, destroyed: false }; + chartCfg.destroy = () => (chartCfg.destroyed = true); + chartCfg.getElementsAtXAxis = _evt => [{ _index: 0 }]; + return chartCfg; + }); + + jest.mock("popsicle", () => mockBuildLibs); + jest.mock("chart.js", () => mockChartUtils); + jest.mock("chartjs-plugin-zoom", () => ({})); + }); + + afterAll(() => { + Object.defineProperty(HTMLElement.prototype, "offsetHeight", originalOffsetHeight); + Object.defineProperty(HTMLElement.prototype, "offsetWidth", originalOffsetWidth); + }); + + test("DataViewer: base operations (column selection, locking, sorting, moving to front, histograms,...", done => { + const { DataViewer, ReactDataViewer } = require("../../dtale/DataViewer"); + + const store = reduxUtils.createDtaleStore(); + const body = document.getElementsByTagName("body")[0]; + body.innerHTML += ''; + body.innerHTML += '
'; + const result = mount( + + + , + { + attachTo: document.getElementById("content"), + } + ); + + setTimeout(() => { + result.update(); + let dv = result.find(ReactDataViewer).instance(); + dv.getData(dv.state.ids, true); + dv.getData(dv.state.ids, true); + dv = result.find(ReactDataViewer).instance(); + t.deepEqual( + _.pick(dv.state, ["loading", "loadQueue"]), + { loading: true, loadQueue: [[0, 55]] }, + "should update state" + ); + dv.getData(dv.state.ids, true); + dv = result.find(ReactDataViewer).instance(); + t.deepEqual( + _.pick(dv.state, ["loading", "loadQueue"]), + { loading: true, loadQueue: [[0, 55], [0, 55]] }, + "should update state" + ); + setTimeout(() => { + result.update(); + dv = result.find(ReactDataViewer).instance(); + t.deepEqual( + _.pick(dv.state, ["loading", "loadQueue"]), + { loading: false, loadQueue: [] }, + "should clear state" + ); + dv.getData(dv.state.ids); + dv.getData([0, 1, 2, 3]); + dv = result.find(ReactDataViewer).instance(); + result.find(ReactDataViewer).setState({ query: "error", loadQueue: [], loading: false }); + result.update(); + dv = result.find(ReactDataViewer).instance(); + dv.getData(dv.state.ids, true); + setTimeout(() => { + result.update(); + t.equals(result.find(RemovableError).length, 1, "should display error"); + done(); + }, 400); + }, 400); + }, 600); + }); +}); diff --git a/static/dtale/DataViewer.jsx b/static/dtale/DataViewer.jsx index cae0c9b3..ac1dd5ae 100644 --- a/static/dtale/DataViewer.jsx +++ b/static/dtale/DataViewer.jsx @@ -280,4 +280,4 @@ ReactDataViewer.propTypes = { const ReduxDataViewer = connect()(ReactDataViewer); -export { ReduxDataViewer as DataViewer }; +export { ReduxDataViewer as DataViewer, ReactDataViewer }; diff --git a/static/filter_console.jsx b/static/filter_console.jsx deleted file mode 100644 index 9527c2fe..00000000 --- a/static/filter_console.jsx +++ /dev/null @@ -1,70 +0,0 @@ -const originalConsoleError = console.error; -let patterns = []; - -// Patch console.error to ignore all errors that match the pattern -// given. -// -// Usually, you will to call this only on dev builds: -// -// if (process.env.NODE_ENV == "dev") { -// ignoreConsoleErrors(/foo/); -// } -// -// This ensures that we don't filter any errors in production (helping -// debugging and performance). React doesn't throw any warnings in -// production builds anyway. -function ignoreConsoleErrors(pattern) { - patterns.push(pattern); - - // If we haven't patched console.error, patch it. This ensures that - // users can call this function multiple times. - if (originalConsoleError == console.error) { - console.error = function(...args) { - let message = null; - // eslint-disable-next-line lodash/prefer-lodash-typecheck - if (typeof args[0] === "string") { - message = args[0]; - } - - // Skip the message if it matches. - let shouldIgnore = false; - patterns.forEach(pattern => { - if (message && message.match(pattern)) { - shouldIgnore = true; - } - }); - - if (!shouldIgnore) { - // Otherwise, continue to console.error as normal. - originalConsoleError.apply(console, args); - } - }; - } -} - -// Work around https://github.com/adazzle/react-data-grid/issues/418 -function ignoreDatagridWarnings() { - const prefixes = [ - "Warning: Failed prop type: The prop `isScrolling` is marked as required ", - "Warning: Failed prop type: Required prop ", - "Warning: Failed prop type: Invalid prop `value` supplied to `Cell`.", - "Warning: getDefaultProps is only used on classic React.createClass definitions.", - "Warning: Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler.", - ]; - prefixes.forEach(prefix => { - const pattern = new RegExp(`^${prefix}[\\s\\S]*created by (ReactDataGrid|ReactNodeGrid|ReactAtlasNodeGrid)`); - ignoreConsoleErrors(pattern); - }); - ignoreConsoleErrors(/^Warning: Cell: `ref` is not a prop./); - ignoreConsoleErrors(/^Warning: Invalid argument supplied to oneOf, expected an instance of array./); - ignoreConsoleErrors(/^Warning: `NaN` is an invalid value for the `width` css style property./); - ignoreConsoleErrors(/Warning: getDefaultProps is only used on classic React.createClass definitions./); - ignoreConsoleErrors(/Warning: Failed prop type: You provided a `checked` prop to a form field/); -} - -function restoreConsoleError() { - console.error = originalConsoleError; - patterns = []; -} - -export default { ignoreDatagridWarnings, ignoreConsoleErrors, restoreConsoleError }; diff --git a/test_env.js b/test_env.js index 3cf6dc14..cdd2e0b1 100644 --- a/test_env.js +++ b/test_env.js @@ -3,12 +3,6 @@ import Adapter from "enzyme-adapter-react-16"; configure({ adapter: new Adapter() }); -// Provided that we're run *after* babel-register, we can load -// filter_console, even though it's ES6. -const filterConsole = require("./static/filter_console"); -filterConsole.default.ignoreDatagridWarnings(); -filterConsole.default.ignoreConsoleErrors(/This browser doesn't support the `onScroll` event/); - // required for react 16 global.requestAnimationFrame = function(callback) { setTimeout(callback, 0);