diff --git a/packages/perspective-viewer-hypergrid/src/js/PerspectiveDataModel.js b/packages/perspective-viewer-hypergrid/src/js/PerspectiveDataModel.js index fcad8e93e8..802f4d30af 100644 --- a/packages/perspective-viewer-hypergrid/src/js/PerspectiveDataModel.js +++ b/packages/perspective-viewer-hypergrid/src/js/PerspectiveDataModel.js @@ -34,6 +34,10 @@ module.exports = require("datasaur-local").extend("PerspectiveDataModel", { return this._nrows || 0; }, + getConfig: function() { + return this._view._config; + }, + setRowCount: function(count) { this._nrows = count || 0; }, diff --git a/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js b/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js index fe82ff932c..2b652f93ff 100644 --- a/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js +++ b/packages/perspective-viewer-hypergrid/src/js/perspective-plugin.js @@ -107,7 +107,55 @@ function setColumnPropsByType(column) { // `install` makes this a Hypergrid plug-in exports.install = function(grid) { Object.getPrototypeOf(grid.behavior).setPSP = setPSP; - Object.getPrototypeOf(grid.behavior).cellClicked = function(event) { + + Object.getPrototypeOf(grid.behavior).cellClicked = async function(event) { + event.primitiveEvent.preventDefault(); + event.handled = true; + const {x, y} = event.dataCell; + const config = this.dataModel.getConfig(); + const row_pivots = config.row_pivot; + const column_pivots = config.column_pivot; + const start_row = y >= 0 ? y : 0; + const end_row = start_row + 1; + this.dataModel._view.to_json({start_row, end_row}).then(r => { + const row_paths = r.map(x => x.__ROW_PATH__); + const row_pivot_values = row_paths[0] || []; + const row_filters = row_pivots + .map((pivot, index) => { + const pivot_value = row_pivot_values[index]; + return pivot_value ? [pivot, "==", pivot_value] : undefined; + }) + .filter(x => x); + const column_index = row_pivots.length > 0 ? x + 1 : x; + const column_paths = Object.keys(r[0])[column_index]; + let column_filters = []; + let column_name; + if (column_paths) { + const column_pivot_values = column_paths.split("|"); + column_name = column_pivot_values[column_pivot_values.length - 1]; + column_filters = column_pivots + .map((pivot, index) => { + const pivot_value = column_pivot_values[index]; + return pivot_value ? [pivot, "==", pivot_value] : undefined; + }) + .filter(x => x); + } + + const filters = config.filter.concat(row_filters).concat(column_filters); + + grid.canvas.dispatchEvent( + new CustomEvent("perspective-click", { + bubbles: true, + composed: true, + detail: { + config: {filters}, + column_name, + row: r[0] + } + }) + ); + }); + return this.dataModel.toggleRow(event.dataCell.y, event.dataCell.x, event); }; diff --git a/packages/perspective-viewer-hypergrid/test/html/hypergrid.html b/packages/perspective-viewer-hypergrid/test/html/hypergrid.html new file mode 100644 index 0000000000..8f05b25f97 --- /dev/null +++ b/packages/perspective-viewer-hypergrid/test/html/hypergrid.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/perspective-viewer-hypergrid/test/js/hypergrid.spec.js b/packages/perspective-viewer-hypergrid/test/js/hypergrid.spec.js new file mode 100644 index 0000000000..be8ad78773 --- /dev/null +++ b/packages/perspective-viewer-hypergrid/test/js/hypergrid.spec.js @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * Copyright (c) 2019, the Perspective Authors. + * + * This file is part of the Perspective library, distributed under the terms of + * the Apache License 2.0. The full license can be found in the LICENSE file. + * + */ + +const utils = require("@jpmorganchase/perspective-viewer/test/js/utils.js"); +const path = require("path"); + +const click_details = async page => { + const viewer = await page.$("perspective-viewer"); + + const click_event = page.evaluate(element => { + return new Promise(resolve => { + element.addEventListener("perspective-click", e => { + resolve(e.detail); + }); + }); + }, viewer); + + await page.mouse.click(300, 300); + return await click_event; +}; + +utils.with_server({}, () => { + describe.page( + "hypergrid.html", + () => { + describe("clicking on a cell in the grid", () => { + describe("when no filters are present", () => { + test.capture("perspective dispatches perspective-click event with correct properties.", async page => { + await page.waitFor(100); + const detail = await click_details(page); + expect(detail.row).toEqual({ + Category: "Technology", + City: "Los Angeles", + Country: "United States", + "Customer ID": "BH-11710", + Discount: 0.2, + "Order Date": 1307577600000, + "Order ID": "CA-2011-115812", + "Postal Code": 90032, + "Product ID": "TEC-PH-10002033", + Profit: 68.3568, + Quantity: 4, + Region: "West", + "Row ID": 12, + Sales: 911.424, + Segment: "Consumer", + "Ship Date": 1308009600000, + "Ship Mode": "Standard Class", + State: "California", + "Sub-Category": "Phones" + }); + expect(detail.column_name).toEqual("Order Date"); + expect(detail.config).toEqual({filters: []}); + }); + }); + + describe("when a filter is present", () => { + test.capture("perspective dispatches perspective-click event with one filter.", async page => { + await page.waitFor(100); + const viewer = await page.$("perspective-viewer"); + page.evaluate(element => element.setAttribute("filters", '[["Segment", "==", "Consumer"]]'), viewer); + await page.waitForSelector("perspective-viewer:not([updating])"); + + const detail = await click_details(page); + expect(detail.config).toEqual({filters: [["Segment", "==", "Consumer"]]}); + }); + + test.capture("perspective dispatches perspective-click event with filters.", async page => { + await page.waitFor(100); + const viewer = await page.$("perspective-viewer"); + page.evaluate(element => { + element.setAttribute("filters", '[["Segment", "==", "Consumer"]]'); + element.setAttribute("column-pivots", '["Region"]'); + element.setAttribute("row-pivots", '["Country", "City"]'); + }, viewer); + await page.waitForSelector("perspective-viewer:not([updating])"); + + const detail = await click_details(page); + expect(detail.config).toEqual({ + filters: [["Segment", "==", "Consumer"], ["Country", "==", "United States"], ["City", "==", "Madison"], ["Region", "==", "Central"]] + }); + }); + }); + }); + }, + {reload_page: true, root: path.join(__dirname, "..", "..")} + ); +}); diff --git a/packages/perspective-viewer-hypergrid/test/js/superstore.spec.js b/packages/perspective-viewer-hypergrid/test/js/superstore.spec.js index db9b0f9e68..a3c428191f 100644 --- a/packages/perspective-viewer-hypergrid/test/js/superstore.spec.js +++ b/packages/perspective-viewer-hypergrid/test/js/superstore.spec.js @@ -44,7 +44,7 @@ utils.with_server({}, () => { await page.waitForSelector("perspective-viewer:not([updating])"); }); - test.capture("handles flush().", async page => { + test.capture("handles flush", async page => { const viewer = await page.$("perspective-viewer"); await page.shadow_click("perspective-viewer", "#config_button"); await page.evaluate(element => { diff --git a/packages/perspective-viewer-hypergrid/test/results/results.json b/packages/perspective-viewer-hypergrid/test/results/results.json index 41e8b801c1..8398752ace 100644 --- a/packages/perspective-viewer-hypergrid/test/results/results.json +++ b/packages/perspective-viewer-hypergrid/test/results/results.json @@ -1,22 +1,25 @@ { + "empty.html/empty grids do not explode": "423ca653bbcbc21a28029c149a37b8ec", "regressions.html/regular updates": "14abd51c3cae1919119d9f88bfbc5cd3", + "regressions.html/saving a computed column does not interrupt update rendering": "da13afb4284b9c3da21ed57c6ba69301", "superstore.html/shows a grid without any settings applied.": "59ecbb591317976232b7dc078b79e164", "superstore.html/pivots by a row.": "0eab9174593f50e58b92ae047ea97068", "superstore.html/pivots by two rows.": "5552a85a31321fe2495fc8aec3d457d9", + "superstore.html/pivots by a column.": "523c39ab27ca610b5ed179cbe742ec5b", "superstore.html/pivots by a row and a column.": "404fd705b0145f4bbf31f7bd1bccebe6", "superstore.html/pivots by two rows and two columns.": "8527820a72137b6c459a0e13a11a765d", + "superstore.html/sorts by a hidden column.": "042756bbc39aeb729bc4108cde465ec8", "superstore.html/sorts by a numeric column.": "3838ecb375fe4450079c1f54969d2239", + "superstore.html/filters by a numeric column.": "a100080f91cae951339cf5b956b5c2cc", + "superstore.html/highlights invalid filter.": "e244cca8fc2278cb2477d0a46ab5331f", "superstore.html/sorts by an alpha column.": "0c65ce1fbb1035414fd98ca6bb26c493", "superstore.html/displays visible columns.": "5dd292347e4a969425d8c2b216e802ae", + "superstore.html/collapses to depth smaller than viewport": "bdd25c1c40781478bd040d5816b887a6", "superstore.html/resets viewable area when the logical size expands.": "a5d1bad309edf83ceef190dd19d867ec", "superstore.html/resets viewable area when the physical size expands.": "59ecbb591317976232b7dc078b79e164", - "superstore.html/sorts by a hidden column.": "042756bbc39aeb729bc4108cde465ec8", - "superstore.html/filters by a numeric column.": "a100080f91cae951339cf5b956b5c2cc", - "superstore.html/pivots by a column.": "523c39ab27ca610b5ed179cbe742ec5b", - "superstore.html/collapses to depth smaller than viewport": "bdd25c1c40781478bd040d5816b887a6", - "regressions.html/saving a computed column does not interrupt update rendering": "da13afb4284b9c3da21ed57c6ba69301", - "empty.html/empty grids do not explode": "423ca653bbcbc21a28029c149a37b8ec", - "superstore.html/highlights invalid filter.": "e244cca8fc2278cb2477d0a46ab5331f", - "superstore.html/handles flush().": "a5d1bad309edf83ceef190dd19d867ec", - "superstore.html/replaces all rows.": "2c41d623f03b2417da7b5dcf7f31e364" + "superstore.html/replaces all rows.": "2c41d623f03b2417da7b5dcf7f31e364", + "hypergrid.html/perspective dispatches perspective-click event with correct properties.": "f5d6bf82299b2e5f403cdb69b67645fe", + "hypergrid.html/perspective dispatches perspective-click event with one filter.": "b7a10ece1d5084742f9a2d88bb68984d", + "hypergrid.html/perspective dispatches perspective-click event with filters.": "3025f83c828d468207b04d8454e94fda", + "superstore.html/handles flush": "a5d1bad309edf83ceef190dd19d867ec" } \ No newline at end of file