Skip to content

Commit c54a4de

Browse files
authored
Merge pull request #331 from Point72/tkp/spaday-save-select-custom
Fix Spaday layout selection and chart restores
2 parents 7a3371f + f45babf commit c54a4de

7 files changed

Lines changed: 52 additions & 7 deletions

File tree

TODO_SPADAY.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
The `<perspective-panel>` theme path currently:
88

99
1. calls `restore({theme})` for the element chrome and active panel;
10-
2. calls `saveWorkspace()` to enumerate panels;
11-
3. loops over every panel and awaits `restore({theme}, {panel})` sequentially.
10+
1. calls `saveWorkspace()` to enumerate panels;
11+
1. loops over every panel and awaits `restore({theme}, {panel})` sequentially.
1212

1313
Each restore restyles a Perspective plugin, so total latency grows with the number of tabs. Browser profiling against Perspective 5.2 showed a visibly delayed transition with eight panels. In one run, sequential completion took about 300 ms; issuing the panel restores with `Promise.all()` reduced it to about 150 ms. Exact timings vary with panel contents and render state.
1414

@@ -18,3 +18,15 @@ Upstream action:
1818
- After `saveWorkspace()`, restore background panel themes concurrently with `Promise.all()` rather than a serial `for ... of` loop.
1919
- Verify concurrent restores remain safe while live tables update and while a layout replacement is queued.
2020
- Add a multi-panel browser test that checks all saved panel themes after toggling the global theme.
21+
22+
## Bundle Perspective chart plugins
23+
24+
`spaday-perspective` 0.4.3 registers only the Datagrid plugin. A whole-element layout containing chart plugins such as `X Bar` or `Treemap` restores its geometry, but Perspective falls back to Datagrid for those panels because the requested plugins are unavailable.
25+
26+
csp-gateway temporarily works around this by building `spaday-charts.js` from `@perspective-dev/viewer-charts` and loading it as an additional Spaday component package. Remove that bundle and package registration once `spaday-perspective` provides chart plugins itself.
27+
28+
Upstream action:
29+
30+
- Bundle and register `@perspective-dev/viewer-charts` with `<perspective-panel>`, or expose a documented package/option that does so.
31+
- Add a browser test that restores a multi-panel workspace containing Datagrid, `X Bar`, and `Treemap`, then verifies `saveWorkspace()` retains each requested plugin.
32+
- Keep chart and Perspective viewer versions aligned so plugin registration uses the same Perspective runtime.

csp_gateway/server/modules/web/perspective.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ def ui(self, app: "GatewayUI") -> None:
753753
),
754754
)
755755
default_view = self.default_layout or "__default__"
756-
app.seed_store(view=default_view)
756+
app.seed_store(view=default_view, layout_view=default_view)
757757
app.add(Region.HEADER_RIGHT, app.layout_selector(layouts, value=default_view), order=90)
758758
app.add(Region.HEADER_RIGHT, app.save_layout_button(), order=100)
759759
app.add(

csp_gateway/server/web/spaday_assets/actions.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { registerHandler } from "../../js/cdn/index.js";
22

3+
const CUSTOM_LAYOUT = "Custom Layout";
34
const CUSTOM_LAYOUT_STORAGE_KEY = "csp_gateway_demo_config";
5+
const LAYOUT_SELECTOR_ID = "gateway-layout-selector";
46
const WORKSPACE_ID = "gateway-workspace";
57

68
function stripTransientFields(layout) {
@@ -27,6 +29,11 @@ registerHandler("csp-gateway:save-layout", (_event, currentTarget) => {
2729
delete customLayout[key];
2830
}
2931
Object.assign(customLayout, layout);
32+
33+
const selector =
34+
currentTarget.ownerDocument.getElementById(LAYOUT_SELECTOR_ID);
35+
selector.value = CUSTOM_LAYOUT;
36+
selector.dispatchEvent(new Event("input", { bubbles: true }));
3037
})().catch((error) =>
3138
console.error("Failed to save Perspective layout:", error),
3239
);

csp_gateway/server/web/spaday_ui.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
concat,
4848
cond,
4949
eq,
50+
event_value,
5051
field,
5152
not_,
5253
obj,
@@ -90,6 +91,11 @@
9091
assets_dir=Path(__file__).with_name("spaday_assets"),
9192
assets=(("js", "actions.js"),),
9293
)
94+
_PERSPECTIVE_CHARTS_PACKAGE = ComponentPackage(
95+
name="csp-gateway-perspective-charts",
96+
assets_dir=Path(__file__).parents[1] / "build",
97+
assets=(("js", "spaday-charts.js"),),
98+
)
9399

94100

95101
# Page-level resets that spaday's document template does not ship. The palette is deliberately absent:
@@ -293,7 +299,7 @@ def perspective_panel(
293299
parsed = json.loads(layout_json)
294300
except (TypeError, ValueError):
295301
continue
296-
layout_expr = cond(eq(field("view"), name), parsed, layout_expr)
302+
layout_expr = cond(eq(field("layout_view"), name), parsed, layout_expr)
297303
fallback = json.dumps(default_layout).replace("<", "\\u003c")
298304
storage_key = json.dumps(_CUSTOM_LAYOUT_STORAGE_KEY)
299305
self._store_seeds["custom_layout"] = Js(
@@ -306,7 +312,7 @@ def perspective_panel(
306312
"catch { return fallback; } "
307313
"})()"
308314
)
309-
layout_expr = cond(eq(field("view"), _CUSTOM_LAYOUT_NAME), field("custom_layout"), layout_expr)
315+
layout_expr = cond(eq(field("layout_view"), _CUSTOM_LAYOUT_NAME), field("custom_layout"), layout_expr)
310316

311317
return (
312318
PerspectivePanel()
@@ -321,7 +327,13 @@ def layout_selector(self, layouts: dict[str, str], *, value: str | None = None)
321327
322328
Add it to `Region.HEADER_RIGHT` (and `seed_store(view=...)`).
323329
"""
324-
select = WaSelect(value=value, size="s").bind("value", "view", mode="two-way").style(width="220px")
330+
select = (
331+
WaSelect(value=value, size="s")
332+
.prop("id", "gateway-layout-selector")
333+
.bind("value", "view", mode="two-way")
334+
.on("change", SetField("layout_view", event_value()))
335+
.style(width="220px")
336+
)
325337
select = select.child(WaOption(value="__default__").text("All Tables"))
326338
for name in layouts:
327339
select = select.child(WaOption(value=name).text(name))
@@ -754,7 +766,7 @@ def mount(self) -> None:
754766
scratch,
755767
self.build_page,
756768
# Component libraries ship as their own distributions and are resolved by entry point.
757-
packages=["webawesome", "perspective", _GATEWAY_COMPONENT_PACKAGE],
769+
packages=["webawesome", "perspective", _GATEWAY_COMPONENT_PACKAGE, _PERSPECTIVE_CHARTS_PACKAGE],
758770
# spaday infers "source checkout" from a `js/` dir next to itself, which any distribution
759771
# shipping a top-level `js/` package (plotly does) satisfies -- serving assets we consume
760772
# from the wheel, never from a spaday checkout.

csp_gateway/tests/server/web/test_spaday_ui.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ def client(self, gateway):
271271
def test_layout_actions_are_available_without_server_layouts(self, client: TestClient):
272272
tree = client.get("/tree.json").text
273273
assert "Custom Layout" in tree
274+
assert "gateway-layout-selector" in tree
275+
assert "layout_view" in tree
274276
assert "Save current layout" in tree
275277
assert "csp-gateway:save-layout" in tree
276278
assert "Download layout" in tree
@@ -279,6 +281,7 @@ def test_layout_actions_are_available_without_server_layouts(self, client: TestC
279281
def test_layout_action_script_is_served(self, client: TestClient):
280282
page = client.get("/").text
281283
assert "/components/csp-gateway/actions.js" in page
284+
assert "/components/csp-gateway-perspective-charts/spaday-charts.js" in page
282285
assert "globalThis.cspGatewayCustomLayout" in page
283286
assert '"gateway-workspace"' in client.get("/tree.json").text
284287
script = client.get("/components/csp-gateway/actions.js")
@@ -287,8 +290,14 @@ def test_layout_action_script_is_served(self, client: TestClient):
287290
assert '"csp-gateway:save-layout"' in script.text
288291
assert '"csp-gateway:download-layout"' in script.text
289292
assert '"csp_gateway_demo_config"' in script.text
293+
assert '"gateway-layout-selector"' in script.text
294+
assert 'new Event("input", { bubbles: true })' in script.text
290295
assert '"gateway-workspace"' in script.text
291296
assert client.get("/js/cdn/index.js").status_code == 200
297+
charts = client.get("/components/csp-gateway-perspective-charts/spaday-charts.js")
298+
assert charts.status_code == 200
299+
assert "X Bar" in charts.text
300+
assert "Treemap" in charts.text
292301

293302
def test_layout_download_is_a_same_origin_attachment(self, client: TestClient):
294303
layout = {"layout": {"type": "tab-layout", "tabs": []}, "panels": {}}

js/build.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ const BUNDLES = [
3030
alias: REACT_ALIAS,
3131
publicPath: "static",
3232
},
33+
{
34+
entryPoints: ["./src/js/spaday_charts.js"],
35+
outfile: "../csp_gateway/server/build/spaday-charts.js",
36+
},
3337
];
3438

3539
const WASM_ASSETS = [

js/src/js/spaday_charts.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "@perspective-dev/viewer-charts";

0 commit comments

Comments
 (0)