Skip to content
Open
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
22 changes: 22 additions & 0 deletions packages/data-viz/src/core/BarChart/__storybook__/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const BARCHART_DATA: number[][] = [];

for (let i = 1; i < 20; i++) {
BARCHART_DATA.push([i, Math.round(Math.random() * 100)]);
}

export const BARCHART_TOOLTIP_OPTIONS = [
{ show: false },
{
enterable: true,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formatter: function (param: any) {
return param.data
? [
`X-Axis: <strong>${param.data[0]}</strong><br/>`,
`Y-Axis: <strong>${param.data[1]} ${param.marker}</strong>`,
].join("")
: [];
},
show: true,
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Meta } from "@storybook/react";
import { BARCHART_TOOLTIP_OPTIONS } from "./constants";
import { BADGE } from "@geometricpanda/storybook-addon-badges";
import { BarChart } from "./stories/default";

export default {
argTypes: {
echartsRendererMode: {
control: {
labels: ["Canvas", "SVG"],
type: "select",
},
options: ["canvas", "svg"],
},
tooltip: {
control: {
labels: ["No tooltip", "Show Tooltip"],
type: "select",
},
mapping: BARCHART_TOOLTIP_OPTIONS,
options: Object.keys(BARCHART_TOOLTIP_OPTIONS),
},
},
component: BarChart,
parameters: {
badges: [BADGE.BETA],
chromatic: {
disableSnapshot: true,
},
snapshot: {
skip: true,
},
},
title: "Data Viz/BarChart [beta]",
} as Meta;

// Default

export const Default = {
args: {
echartsRendererMode: "svg",
tooltip: BARCHART_TOOLTIP_OPTIONS[1],
},
parameters: {},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Args } from "@storybook/react";
import RawBarChart from "src/core/BarChart";
import { BARCHART_DATA } from "../constants";

export const BarChart = (props: Args): JSX.Element => {
const { tooltip, ...rest } = props;

return (
<>
<RawBarChart
width={600}
height={400}
options={{
grid: {
backgroundColor: "rgba(0, 0, 0, 0)",
borderColor: "#ccc",
borderWidth: 1,
bottom: 70,
containLabel: false,
left: "10%",
right: "10%",
show: false,
top: 60,
z: 0,
},
series: [
{
data: BARCHART_DATA,
type: "bar",
},
],
tooltip: tooltip,
xAxis: {
type: "value",
},
yAxis: {
type: "value",
},
}}
{...rest}
/>
</>
);
};
76 changes: 76 additions & 0 deletions packages/data-viz/src/core/BarChart/hooks/useUpdateChart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { throttle } from "lodash";
import { useEffect, useMemo } from "react";
import { CreateChartOptionsProps, createChartOptions } from "./utils";

const UPDATE_THROTTLE_MS = 1 * 100;

export interface UpdateChartProps extends CreateChartOptionsProps {
chart: echarts.ECharts | null;
}

export function useUpdateChart({
chart,
width,
height,
options,
onEvents,
}: UpdateChartProps): void {
const throttledUpdateChart = useMemo(() => {
return throttle(
() => {
if (!chart) {
return;
}

// (thuang): resize() needs to be called before setOption() to prevent
// TypeError: Cannot read properties of undefined (reading 'shouldBePainted')
chart.resize();

const chartOptions = createChartOptions({
height,
options,
width,
});

chart.setOption(chartOptions, {
replaceMerge: ["tooltip"],
});

/**
* We need to remove old event listeners and bind new ones to
* make sure that the event listeners are updated when the props change.
*/
if (onEvents) {
for (const eventName in onEvents) {
if (
Object.prototype.hasOwnProperty.call(onEvents, eventName) &&
typeof eventName === "string" &&
typeof onEvents[eventName] === "function"
) {
// Remove old event listener
chart.off(eventName);

// Add new event listener
chart.on(eventName, (event) => {
onEvents[eventName](event, chart);
});
}
}
}
},
UPDATE_THROTTLE_MS,
// (thuang): Trailing guarantees that the last call to the function will
// be executed
{ trailing: true }
);
}, [chart, width, height, options, onEvents]);

useEffect(() => {
return () => throttledUpdateChart.cancel();
}, [throttledUpdateChart]);

// Update the charts
useEffect(() => {
throttledUpdateChart();
}, [chart, throttledUpdateChart, width, height, options, onEvents]);
}
75 changes: 75 additions & 0 deletions packages/data-viz/src/core/BarChart/hooks/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { ECharts, EChartsOption } from "echarts";

export interface CreateChartOptionsProps {
/**
* The width of the chart in pixels
*/
width: number;
/**
* The height of the chart in pixels
*/
height: number;
/**
* https://echarts.apache.org/en/option.html#grid
*/
grid?:
| EChartsOption["grid"]
| ((defaultOption: EChartsOption["grid"]) => EChartsOption["grid"]);
/**
* The options object to be passed to echarts.setOption()
* https://echarts.apache.org/en/option.html
*/
options?: EChartsOption;
/**
* Event listeners for the chart
* https://echarts.apache.org/en/api.html#events
*/
onEvents?: Record<string, (event: unknown, chart: ECharts) => void>;
}

export function createChartOptions(
props: CreateChartOptionsProps
): EChartsOption {
const { grid: gridProp, options } = props;

const { defaultGrid } = generateDefaultValues(props);

const customGrid =
typeof gridProp === "function" ? gridProp(defaultGrid) : gridProp;

const { series: optionsSeries, ...optionsRest } = options || {};

return {
animation: false,
grid: customGrid || defaultGrid,
series: [
Object.assign(
optionsSeries
? Array.isArray(optionsSeries)
? optionsSeries[0]
: optionsSeries
: [],
{ type: "bar" }
),
] as EChartsOption["series"],
...optionsRest,
};
}

function generateDefaultValues(props: CreateChartOptionsProps) {
const { height, width } = props;

const defaultGrid = {
containLabel: true,
height: `${height}px`,
left: 0,
top: 0,
// (atarashansky): this is the key change to align x and y axis
// labels to fixed spaces
width: `${width}px`,
};

return {
defaultGrid,
};
}
Loading
Loading