Skip to content

Commit fabe4a5

Browse files
authored
Merge pull request #69 from bcdev/forman-x-fix_vega_signal_handlers
Fix vega signal handlers
2 parents 630282b + c12834f commit fabe4a5

File tree

2 files changed

+55
-18
lines changed

2 files changed

+55
-18
lines changed

chartlets.js/packages/lib/src/plugins/vega/hooks/useSignalListeners.test.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,27 @@ const chartWithSelect: TopLevelSpec = {
2424
...chart,
2525
params: [
2626
{
27-
name: "points",
28-
select: { type: "point", fields: ["x", "a"], on: "click" },
27+
name: "sel_point",
28+
select: "point",
29+
},
30+
{
31+
name: "sel_interval",
32+
select: "interval",
33+
},
34+
{
35+
name: "sel_point_a",
36+
select: { on: "click", type: "point", fields: ["x", "a"] },
37+
},
38+
// Event not supported yet
39+
{
40+
name: "sel_interval_b",
41+
select: { on: "wheel", type: "interval", fields: ["x", "b"] },
2942
},
3043
],
3144
};
3245

3346
describe("useSignalListeners", () => {
34-
it("should return a stable record", () => {
47+
it("should return a stable record wo signals", () => {
3548
const { result, rerender } = renderHook(() =>
3649
useSignalListeners(chart, "VegaChart", "my_chart", () => {}),
3750
);
@@ -43,23 +56,36 @@ describe("useSignalListeners", () => {
4356
expect(signalHandlers1).toBe(signalHandlers1);
4457
});
4558

59+
it("should support different signal types", () => {
60+
const { result } = renderHook(() =>
61+
useSignalListeners(chartWithSelect, "VegaChart", "my_chart", () => {}),
62+
);
63+
const signalHandlers = result.current;
64+
expect(signalHandlers).toBeDefined();
65+
expect(signalHandlers["sel_point"]).toBeTypeOf("function");
66+
expect(signalHandlers["sel_interval"]).toBeTypeOf("function");
67+
expect(signalHandlers["sel_point_a"]).toBeTypeOf("function");
68+
// "wheel" not supported
69+
expect(signalHandlers["sel_point_b"]).toBeUndefined();
70+
});
71+
4672
it("should call onChange", () => {
4773
const { recordedEvents, onChange } = createChangeHandler();
4874
const { result } = renderHook(() =>
4975
useSignalListeners(chartWithSelect, "VegaChart", "my_chart", onChange),
5076
);
5177
const signalHandlers = result.current;
5278
expect(signalHandlers).toBeDefined();
53-
const signalHandler = signalHandlers["points"];
79+
const signalHandler = signalHandlers["sel_point_a"];
5480
expect(signalHandler).toBeTypeOf("function");
5581
act(() => {
56-
signalHandler("points", [1, 2, 3]);
82+
signalHandler("sel_point_a", [1, 2, 3]);
5783
});
5884
expect(recordedEvents.length).toBe(1);
5985
expect(recordedEvents[0]).toEqual({
6086
componentType: "VegaChart",
6187
id: "my_chart",
62-
property: "points",
88+
property: "sel_point_a",
6389
value: [1, 2, 3],
6490
});
6591
});

chartlets.js/packages/lib/src/plugins/vega/hooks/useSignalListeners.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@ type SignalHandler = (signalName: string, signalValue: unknown) => void;
1313
* only interested in extracting the handlers, the following
1414
* properties are required.
1515
*/
16-
type SelectionParameter = { name: string; select: { on: string } };
16+
type SelectionParameter = {
17+
name: string;
18+
select: "point" | "interval" | { type: "point" | "interval"; on: string };
19+
};
1720

1821
const isSelectionParameter = (param: unknown): param is SelectionParameter =>
1922
isObject(param) &&
20-
"name" in param &&
21-
"select" in param &&
22-
isObject(param.select) &&
23-
param.select?.on !== null &&
24-
isString(param.select.on);
23+
(param.select === "point" ||
24+
param.select === "interval" ||
25+
(isObject(param.select) &&
26+
(param.select.type === "point" || param.select.type === "interval") &&
27+
isString(param.select.on)));
2528

2629
export function useSignalListeners(
2730
chart: TopLevelSpec | null | undefined,
@@ -36,16 +39,23 @@ export function useSignalListeners(
3639
* have so that we can create those listeners with the `name` specified in
3740
* the event-listener object.
3841
*/
39-
const signalNames = useMemo((): Record<string, string> => {
40-
const signalNames: Record<string, string> = {};
42+
const signalNames = useMemo(() => {
43+
const signalNames: [string, string][] = [];
4144
if (!chart || !chart.params) {
4245
return signalNames;
4346
}
4447
return chart.params
4548
.filter(isSelectionParameter)
46-
.reduce((paramNames, param) => {
47-
paramNames[param.select.on] = param.name;
48-
return paramNames;
49+
.reduce((signalNames, param) => {
50+
// https://vega.github.io/vega-lite/docs/parameter.html#select
51+
if (param.select === "point") {
52+
signalNames.push(["click", param.name]);
53+
} else if (param.select === "interval") {
54+
signalNames.push(["drag", param.name]);
55+
} else {
56+
signalNames.push([param.select.on, param.name]);
57+
}
58+
return signalNames;
4959
}, signalNames);
5060
}, [chart]);
5161

@@ -74,10 +84,11 @@ export function useSignalListeners(
7484
*/
7585
const signalHandlers: Record<string, SignalHandler> = {
7686
click: handleClickSignal,
87+
drag: handleClickSignal,
7788
};
7889

7990
const signalListeners: Record<string, SignalHandler> = {};
80-
Object.entries(signalNames).forEach(([event, signalName]) => {
91+
signalNames.forEach(([event, signalName]) => {
8192
if (signalHandlers[event]) {
8293
signalListeners[signalName] = signalHandlers[event];
8394
} else {

0 commit comments

Comments
 (0)