Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add pan/zoom to mo.ui.altair_chart #1855

Merged
merged 3 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions frontend/src/plugins/impl/vega/VegaPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { VegaComponentState, Data } from "./vega-component";

import "./vega.css";
import React from "react";
import { TooltipProvider } from "@/components/ui/tooltip";

const LazyVegaComponent = React.lazy(() => import("./vega-component"));

Expand All @@ -27,11 +28,13 @@ export class VegaPlugin implements IPlugin<VegaComponentState, Data> {

render(props: IPluginProps<VegaComponentState, Data>): JSX.Element {
return (
<LazyVegaComponent
value={props.value}
setValue={props.setValue}
{...props.data}
/>
<TooltipProvider>
<LazyVegaComponent
value={props.value}
setValue={props.setValue}
{...props.data}
/>
</TooltipProvider>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,20 @@ exports[`makeSelectable > should return correctly if existing legend selection 1
"encodings": [
"color",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
}
`;
Expand Down Expand Up @@ -127,6 +138,7 @@ exports[`makeSelectable > should return correctly if mark is a string 1`] = `
"x",
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -143,9 +155,21 @@ exports[`makeSelectable > should return correctly if mark is a string 1`] = `
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
}
`;
Expand Down Expand Up @@ -183,6 +207,7 @@ exports[`makeSelectable > should return correctly if mark is not string 1`] = `
"x",
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -199,9 +224,21 @@ exports[`makeSelectable > should return correctly if mark is not string 1`] = `
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
}
`;
Expand Down Expand Up @@ -272,6 +309,7 @@ exports[`makeSelectable > should return correctly if overlapping encodings 1`] =
"x",
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -288,7 +326,19 @@ exports[`makeSelectable > should return correctly if overlapping encodings 1`] =
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
Expand Down Expand Up @@ -362,6 +412,7 @@ exports[`makeSelectable > should return correctly with multiple encodings 1`] =
"x",
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -378,7 +429,19 @@ exports[`makeSelectable > should return correctly with multiple encodings 1`] =
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
Expand Down Expand Up @@ -438,6 +501,7 @@ exports[`makeSelectable > should skip field selection if empty or false 1`] = `
"x",
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -454,9 +518,21 @@ exports[`makeSelectable > should skip field selection if empty or false 1`] = `
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
}
`;
Expand Down Expand Up @@ -509,6 +585,18 @@ exports[`makeSelectable > should work for multi-layered charts 1`] = `
"ticks": true,
"type": "errorbar",
},
"params": [
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
},
{
"encoding": {
Expand Down Expand Up @@ -548,6 +636,7 @@ exports[`makeSelectable > should work for multi-layered charts 1`] = `
"x",
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -564,6 +653,8 @@ exports[`makeSelectable > should work for multi-layered charts 1`] = `
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
Expand Down Expand Up @@ -632,6 +723,7 @@ exports[`makeSelectable > should work for multi-layered charts with different se
"encodings": [
"y",
],
"on": "click[!event.metaKey]",
"type": "point",
},
},
Expand All @@ -647,7 +739,19 @@ exports[`makeSelectable > should work for multi-layered charts with different se
"stroke": "#669EFF",
"strokeOpacity": 0.4,
},
"on": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"translate": "[mousedown[!event.metaKey], mouseup] > mousemove[!event.metaKey]",
"type": "interval",
},
},
{
"bind": "scales",
"name": "pan_zoom",
"select": {
"on": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"translate": "[mousedown[event.metaKey], window:mouseup] > window:mousemove!",
"type": "interval",
"zoom": "wheel![event.metaKey]",
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ describe("makeSelectable", () => {
"legend_selection_Origin",
"select_point",
"select_interval",
"pan_zoom",
]);
});

Expand Down Expand Up @@ -130,6 +131,7 @@ describe("makeSelectable", () => {
expect(getSelectionParamNames(newSpec)).toEqual([
"select_point",
"select_interval",
"pan_zoom",
]);

// These are the same
Expand Down Expand Up @@ -162,6 +164,7 @@ describe("makeSelectable", () => {
"legend_selection_sizeField",
"select_point",
"select_interval",
"pan_zoom",
]);
});

Expand Down Expand Up @@ -214,6 +217,7 @@ describe("makeSelectable", () => {
"param_1",
"legend_selection_series",
"select_point",
"pan_zoom",
]);
});

Expand Down Expand Up @@ -287,6 +291,7 @@ describe("makeSelectable", () => {
expect(newSpec).toMatchSnapshot();

expect(getSelectionParamNames(newSpec)).toEqual([
"pan_zoom",
"select_point_1",
"select_interval_1",
]);
Expand Down Expand Up @@ -349,6 +354,7 @@ describe("makeSelectable", () => {
expect(getSelectionParamNames(newSpec)).toEqual([
"select_point_0",
"select_interval_0",
"pan_zoom",
]);
});
});
25 changes: 25 additions & 0 deletions frontend/src/plugins/impl/vega/__tests__/vega.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,31 @@ yield_error,yield_center
]
`);
});

it("should handle when there is no format given", async () => {
const csvData = `id\n912312851340981241284`;
vi.spyOn(vegaLoader, "load").mockReturnValue(Promise.resolve(csvData));
const format = undefined;
const data = await vegaLoadData(csvData, format, { handleBigInt: true });
expect(data).toMatchInlineSnapshot(`
[
{
"id": 912312851340981241284n,
},
]
`);

const jsonData = `[{"id": "912312851340981241284"}]`;
vi.spyOn(vegaLoader, "load").mockReturnValue(Promise.resolve(jsonData));
const data2 = await vegaLoadData(jsonData, format);
expect(data2).toMatchInlineSnapshot(`
[
{
"id": "912312851340981241284",
},
]
`);
});
});

describe("uniquifyColumnNames", () => {
Expand Down
20 changes: 17 additions & 3 deletions frontend/src/plugins/impl/vega/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,23 @@ export function vegaLoadData<T = object>(
): Promise<T[]> {
const { handleBigInt = false, replacePeriod = false } = opts;

const isCsv = format?.type === "csv";

return vegaLoader.load(url).then((csvOrJsonData) => {
if (!format) {
// Infer by trying to parse
if (typeof csvOrJsonData === "string") {
try {
JSON.parse(csvOrJsonData);
format = { type: "json" };
} catch {
format = { type: "csv", parse: "auto" };
}
}
if (typeof csvOrJsonData === "object") {
format = { type: "json" };
}
}

const isCsv = format?.type === "csv";
// CSV data comes columnar and may have duplicate column names.
// We need to uniquify the column names before parsing since vega-loader
// returns an array of objects which drops duplicate keys.
Expand Down Expand Up @@ -126,7 +140,7 @@ export function vegaLoadData<T = object>(
? // csv -> json
read(csvOrJsonData, {
...format,
parse: (format.parse as FieldTypes) || "auto",
parse: (format?.parse as FieldTypes) || "auto",
})
: read(csvOrJsonData, format);

Expand Down
Loading
Loading