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

Fix vega warnings 2 #49

Merged
merged 24 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7a18ffb
Fix vega OnClick warning
b-yogesh Nov 20, 2024
19b7aec
Fix Scale bindings warning
b-yogesh Nov 20, 2024
371755f
Fix infinite extent warning
b-yogesh Nov 20, 2024
caf2a4b
Added comments
b-yogesh Nov 20, 2024
98dfd91
Added optimizations
b-yogesh Nov 21, 2024
baef8ee
Changes based on reviewers comments
b-yogesh Nov 21, 2024
921c50f
Replace == "string" with isString type-guard
b-yogesh Nov 21, 2024
82c92eb
TODO: new callback onclick test
b-yogesh Nov 22, 2024
192783e
Merge branch 'main' into yogesh-xxx-fix-vega-warnings
b-yogesh Nov 22, 2024
efdc7d3
Access the click event in the callback function
b-yogesh Nov 22, 2024
6bce83b
Merge branch 'main' into yogesh-xxx-fix-vega-warnings
b-yogesh Nov 22, 2024
c346d19
fix merge conflict error
b-yogesh Nov 22, 2024
de26805
Merge branch 'main' into yogesh-xxx-fix-vega-warnings
b-yogesh Nov 22, 2024
30fa717
fix build error - remove src exports
b-yogesh Nov 22, 2024
b6ade49
Merge branch 'main' into yogesh-xxx-fix-vega-warnings2
b-yogesh Nov 22, 2024
5fce033
Resolved merge conflicts
b-yogesh Nov 25, 2024
51ba7e4
fix useCallback dep
b-yogesh Nov 25, 2024
a75a725
minor refactoring
b-yogesh Nov 25, 2024
3c4e7e3
Merge branch 'main' into yogesh-xxx-fix-vega-warnings2
b-yogesh Nov 26, 2024
617c737
TODO: improve callback fn
b-yogesh Nov 26, 2024
1b6b9e6
Merge branch 'main' into yogesh-xxx-fix-vega-warnings2
b-yogesh Nov 26, 2024
c9ab9fe
Update callback fn.
b-yogesh Nov 26, 2024
57466ad
Update CHANGES.md and bump up the version
b-yogesh Nov 26, 2024
3ffd4ff
Remove print
b-yogesh Nov 26, 2024
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
7,655 changes: 0 additions & 7,655 deletions chartlets.js/package-lock.json

This file was deleted.

20 changes: 6 additions & 14 deletions chartlets.js/src/lib/components/Plot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,24 @@ import { VegaLite } from "react-vega";

import { type PlotState } from "@/lib/types/state/component";
import { type ComponentChangeHandler } from "@/lib/types/state/event";
import { useSignalListeners } from "@/lib/hooks";

export interface PlotProps extends Omit<PlotState, "type"> {
onChange: ComponentChangeHandler;
}

export function Plot({ id, style, chart, onChange }: PlotProps) {
const signalListeners = useSignalListeners(chart, id, onChange);

if (!chart) {
return <div id={id} style={style} />;
}
const { datasets, ...spec } = chart;
const handleSignal = (_signalName: string, value: unknown) => {
if (id) {
return onChange({
componentType: "Plot",
id: id,
property: "points",
value: value,
});
}
};

return (
<VegaLite
spec={spec}
data={datasets}
spec={chart}
style={style}
signalListeners={{ onClick: handleSignal }}
signalListeners={signalListeners}
actions={false}
/>
);
Expand Down
3 changes: 2 additions & 1 deletion chartlets.js/src/lib/components/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type SelectState,
} from "@/lib/types/state/component";
import { type ComponentChangeHandler } from "@/lib/types/state/event";
import { isString } from "@/lib/utils/isString";

export interface SelectProps extends Omit<SelectState, "type"> {
onChange: ComponentChangeHandler;
Expand Down Expand Up @@ -64,7 +65,7 @@ export function Select({
function normalizeSelectOption(
option: SelectOption,
): [string | number, string] {
if (typeof option === "string") {
if (isString(option)) {
return [option, option];
} else if (typeof option === "number") {
return [option, option.toString()];
Expand Down
94 changes: 93 additions & 1 deletion chartlets.js/src/lib/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import type { StoreState } from "@/lib/types/state/store";
import { store } from "@/lib/store";
import { useMemo } from "react";
import { useCallback, useMemo } from "react";
import type { ContributionState } from "@/lib/types/state/contribution";
import { type SignalHandler } from "@/lib/types/state/vega";
import type { TopLevelSpec } from "vega-lite";
import type {
ComponentChangeEvent,
ComponentChangeHandler,
} from "@/lib/types/state/event";
import { handleComponentChange } from "@/lib/actions/handleComponentChange";
import { isString } from "@/lib/utils/isString";
import { isObject } from "@/lib/utils/isObject";

const selectConfiguration = (state: StoreState) => state.configuration;

Expand Down Expand Up @@ -38,6 +42,94 @@ export function makeContributionsHook<S extends object = object>(
};
}

export function useSignalListeners(
chart: TopLevelSpec | null,
id: string | undefined,
onChange: ComponentChangeHandler,
): { [key: string]: SignalHandler } {
/*
Here, we create map of signals which will be then used to create the
map of signal-listeners because not all params are event-listeners, and we
need to identify them. Later in the code, we then see which handlers do we
have so that we can create those listeners with the `name` specified in
the event-listener object.
*/
const signals: { [key: string]: string } = useMemo(() => {
if (!chart) return {};
if (!chart.params) return {};
return chart.params
.filter(
(param): param is { name: string; select: { on: string } } =>
isObject(param) &&
param !== null &&
"name" in param &&
"select" in param &&
isObject(param.select) &&
param.select?.on != null &&
isString(param.select.on),
)
.reduce(
(acc, param) => {
acc[param.select.on] = param.name;
return acc;
},
{} as { [key: string]: string },
);
}, [chart]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the part that was changed


const handleClickSignal = useCallback(
(signalName: string, signalValue: unknown) => {
if (id) {
return onChange({
componentType: "Plot",
id: id,
property: signalName,
value: signalValue,
});
}
},
[id, onChange],
);

/*
Currently, we only have click events support, but if more are required,
they can be implemented and added in the map below.
*/
const signalHandlerMap: { [key: string]: SignalHandler } = useMemo(
() => ({
click: handleClickSignal,
}),
[handleClickSignal],
);

/*
This function creates the map of signal listeners based on the `signals`
map computed above.
*/
const createSignalListeners = useCallback(
(signals: { [key: string]: string }) => {
const signalListeners: { [key: string]: SignalHandler } = {};
Object.entries(signals).forEach(([event, signalName]) => {
if (signalHandlerMap[event]) {
signalListeners[signalName] = signalHandlerMap[event];
} else {
console.warn(
"The signal " + event + " is not yet supported in chartlets.js",
);
}
});

return signalListeners;
},
[signalHandlerMap],
);

return useMemo(
() => createSignalListeners(signals),
[createSignalListeners, signals],
);
}

/**
* A hook that retrieves the contributions for the given contribution
* point given by `contribPoint`.
Expand Down
9 changes: 4 additions & 5 deletions chartlets.js/src/lib/types/state/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type CSSProperties } from "react";
import type { VisualizationSpec } from "react-vega";
import { isObject } from "@/lib/utils/isObject";
import type { TopLevelSpec } from "vega-lite";
import { isString } from "@/lib/utils/isString";

export type ComponentType =
| "Box"
Expand Down Expand Up @@ -62,9 +63,7 @@ export interface CheckboxState extends ComponentState {
export interface PlotState extends ComponentState {
type: "Plot";
chart:
| (VisualizationSpec & {
datasets?: Record<string, unknown>; // Add the datasets property
})
| TopLevelSpec // This is the vega-lite specification type
| null;
}

Expand All @@ -77,7 +76,7 @@ export interface TypographyState extends ContainerState {
}

export function isComponentState(object: unknown): object is ComponentState {
return isObject(object) && typeof object.type === "string";
return isObject(object) && isString(object.type);
}

export function isContainerState(object: unknown): object is ContainerState {
Expand Down
1 change: 1 addition & 0 deletions chartlets.js/src/lib/types/state/vega.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type SignalHandler = (signalName: string, value: unknown) => void;
3 changes: 3 additions & 0 deletions chartlets.js/src/lib/utils/isString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isString(signalName: unknown): signalName is string {
return typeof signalName === "string";
}
3 changes: 2 additions & 1 deletion chartlets.js/src/lib/utils/objPath.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isObject } from "@/lib/utils/isObject";
import { isString } from "@/lib/utils/isString";

export type ObjPath = (string | number)[];
export type ObjPathLike = ObjPath | string | number | undefined | null;
Expand Down Expand Up @@ -83,7 +84,7 @@ export function normalizeObjPath(pathLike: ObjPathLike): ObjPath {
}

export function formatObjPath(objPath: ObjPathLike): string {
if (typeof objPath === "string") {
if (isString(objPath)) {
return objPath;
} else if (Array.isArray(objPath)) {
return objPath
Expand Down
50 changes: 47 additions & 3 deletions chartlets.py/my_extension/my_panel_1.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import copy
from types import NoneType

import altair as alt
import pandas as pd

from chartlets import Component, Input, Output
from chartlets import Component, Input, Output, State
from chartlets.components import Plot, Box, Select
from chartlets.demo.contribs import Panel
from chartlets.demo.context import Context
Expand Down Expand Up @@ -60,7 +64,7 @@ def make_figure(ctx: Context, selected_dataset: int = 0) -> alt.Chart:
# Create another parameter to handle the click events and send the data as
# specified in the fields
click_param = alt.selection_point(
on="click", name="onClick", fields=["x", variable_name]
on="click", name="points", fields=["x", variable_name]
)
# Create a chart type using mark_* where * could be any kind of chart
# supported by Vega. We can add properties and parameters as shown below.
Expand All @@ -79,5 +83,45 @@ def make_figure(ctx: Context, selected_dataset: int = 0) -> alt.Chart:
.properties(width=290, height=300, title="Vega charts")
.add_params(corner_var, click_param)
)

return chart


@panel.callback(
Input("plot", property="points"), State("plot", "chart"), Output("plot", "chart")
)
def get_click_event_points(ctx: Context, points, plot) -> alt.Chart:
b-yogesh marked this conversation as resolved.
Show resolved Hide resolved
"""
This callback function shows how we can use the event handlers output
(property="points") which was defined in the `make_figure` callback
function as a `on='click'` handler. Here, we access the variables as
defined in the `fields` argument when creating the `click_param` parameter.

Based on the click event, the user can access the point that was clicked.
The function below extracts the points and changes the color of the bar
that was clicked.

"""
if points:
conditions = []
for field, values in points.items():
if field != "vlPoint":
for value in values:
field_type = plot["encoding"].get(field, {}).get("type", "")
if field_type == "nominal":
conditions.append(f"datum.{field} === '{value}'")
else:
conditions.append(f"datum.{field} === {value}")
conditions.append(f"datum.{field} === {repr(value)}")

condition_expr = " && ".join(conditions)

plot["encoding"]["color"] = {
"condition": {
"test": condition_expr,
# Highlight color when the condition is true
"value": "orange",
},
"value": "steelblue", # Default color
}

return alt.Chart.from_dict(plot)
8 changes: 7 additions & 1 deletion chartlets.py/my_extension/my_panel_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ def make_figure(
)
.properties(width=300, height=300, title="Vega charts using Shorthand syntax")
.add_params(selector)
.interactive()
# .interactive() # Using interactive mode will lead to warning
# `WARN Scale bindings are currently only supported for scales with
# unbinned, continuous domains.`
# because it expects both x and y to be continuous scales,
# but we have x as Nominal which leads to this warning.
# This still works where we can only zoom in on the y axis but
# with a warning.
)
return chart

Expand Down