feat: PlotlyLineChart component API and docs improvements
This commit is contained in:
parent
0aed7dce77
commit
059ffe4e34
@ -1,7 +1,8 @@
|
|||||||
import { QueryClient, QueryClientProvider, useQuery } from "react-query";
|
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
|
||||||
import { Plotly } from "./Plotly";
|
import { Plotly } from './Plotly';
|
||||||
import Papa, { ParseConfig } from "papaparse";
|
import Papa, { ParseConfig } from 'papaparse';
|
||||||
import LoadingSpinner from "./LoadingSpinner";
|
import LoadingSpinner from './LoadingSpinner';
|
||||||
|
import { Data } from '../types/properties';
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ async function getCsv(url: string, bytes: number) {
|
|||||||
|
|
||||||
async function parseCsv(
|
async function parseCsv(
|
||||||
file: string,
|
file: string,
|
||||||
parsingConfig: ParseConfig,
|
parsingConfig: ParseConfig
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
Papa.parse(file, {
|
Papa.parse(file, {
|
||||||
@ -39,38 +40,33 @@ async function parseCsv(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PlotlyLineChartProps {
|
export interface PlotlyLineChartProps {
|
||||||
url?: string;
|
data: Data;
|
||||||
data?: { [key: string]: number | string }[];
|
|
||||||
rawCsv?: string;
|
|
||||||
randomId?: number;
|
|
||||||
bytes?: number;
|
bytes?: number;
|
||||||
parsingConfig?: ParseConfig;
|
parsingConfig?: ParseConfig;
|
||||||
xAxis: string;
|
xAxis: string;
|
||||||
yAxis: string;
|
yAxis: string;
|
||||||
lineLabel?: string;
|
lineLabel?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
uniqueId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlotlyLineChart: React.FC<PlotlyLineChartProps> = ({
|
export const PlotlyLineChart: React.FC<PlotlyLineChartProps> = ({
|
||||||
url,
|
|
||||||
data,
|
data,
|
||||||
rawCsv,
|
|
||||||
bytes = 5132288,
|
bytes = 5132288,
|
||||||
parsingConfig = {},
|
parsingConfig = {},
|
||||||
xAxis,
|
xAxis,
|
||||||
yAxis,
|
yAxis,
|
||||||
lineLabel,
|
lineLabel,
|
||||||
title = "",
|
title = '',
|
||||||
|
uniqueId,
|
||||||
}) => {
|
}) => {
|
||||||
const randomId = Math.random();
|
uniqueId = uniqueId ?? Math.random();
|
||||||
return (
|
return (
|
||||||
// Provide the client to your App
|
// Provide the client to your App
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<LineChartInner
|
<LineChartInner
|
||||||
url={url}
|
|
||||||
data={data}
|
data={data}
|
||||||
rawCsv={rawCsv}
|
uniqueId={uniqueId}
|
||||||
randomId={randomId}
|
|
||||||
bytes={bytes}
|
bytes={bytes}
|
||||||
parsingConfig={parsingConfig}
|
parsingConfig={parsingConfig}
|
||||||
xAxis={xAxis}
|
xAxis={xAxis}
|
||||||
@ -83,10 +79,8 @@ export const PlotlyLineChart: React.FC<PlotlyLineChartProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LineChartInner: React.FC<PlotlyLineChartProps> = ({
|
const LineChartInner: React.FC<PlotlyLineChartProps> = ({
|
||||||
url,
|
|
||||||
data,
|
data,
|
||||||
rawCsv,
|
uniqueId,
|
||||||
randomId,
|
|
||||||
bytes,
|
bytes,
|
||||||
parsingConfig,
|
parsingConfig,
|
||||||
xAxis,
|
xAxis,
|
||||||
@ -94,18 +88,22 @@ const LineChartInner: React.FC<PlotlyLineChartProps> = ({
|
|||||||
lineLabel,
|
lineLabel,
|
||||||
title,
|
title,
|
||||||
}) => {
|
}) => {
|
||||||
if (data) {
|
const values = data.values;
|
||||||
|
const url = data.url;
|
||||||
|
const csv = data.csv;
|
||||||
|
|
||||||
|
if (values) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full" style={{ height: "500px" }}>
|
<div className="w-full" style={{ height: '500px' }}>
|
||||||
<Plotly
|
<Plotly
|
||||||
layout={{
|
layout={{
|
||||||
title,
|
title,
|
||||||
}}
|
}}
|
||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
x: data.map((d) => d[xAxis]),
|
x: values.map((d) => d[xAxis]),
|
||||||
y: data.map((d) => d[yAxis]),
|
y: values.map((d) => d[yAxis]),
|
||||||
mode: "lines",
|
mode: 'lines',
|
||||||
name: lineLabel,
|
name: lineLabel,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
@ -114,18 +112,18 @@ const LineChartInner: React.FC<PlotlyLineChartProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
|
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
|
||||||
["dataCsv", url, randomId],
|
['dataCsv', url, uniqueId],
|
||||||
() => getCsv(url as string, bytes ?? 5132288),
|
() => getCsv(url as string, bytes ?? 5132288),
|
||||||
{ enabled: !!url },
|
{ enabled: !!url }
|
||||||
);
|
);
|
||||||
const { data: parsedData, isLoading: isParsing } = useQuery(
|
const { data: parsedData, isLoading: isParsing } = useQuery(
|
||||||
["dataPreview", csvString, randomId],
|
['dataPreview', csvString, uniqueId],
|
||||||
() =>
|
() =>
|
||||||
parseCsv(
|
parseCsv(
|
||||||
rawCsv ? (rawCsv as string) : (csvString as string),
|
csv ? (csv as string) : (csvString as string),
|
||||||
parsingConfig ?? {},
|
parsingConfig ?? {}
|
||||||
),
|
),
|
||||||
{ enabled: rawCsv ? true : !!csvString },
|
{ enabled: csv ? true : !!csvString }
|
||||||
);
|
);
|
||||||
if (isParsing || isDownloadingCSV)
|
if (isParsing || isDownloadingCSV)
|
||||||
<div className="w-full flex justify-center items-center h-[500px]">
|
<div className="w-full flex justify-center items-center h-[500px]">
|
||||||
@ -133,7 +131,7 @@ const LineChartInner: React.FC<PlotlyLineChartProps> = ({
|
|||||||
</div>;
|
</div>;
|
||||||
if (parsedData)
|
if (parsedData)
|
||||||
return (
|
return (
|
||||||
<div className="w-full" style={{ height: "500px" }}>
|
<div className="w-full" style={{ height: '500px' }}>
|
||||||
<Plotly
|
<Plotly
|
||||||
layout={{
|
layout={{
|
||||||
title,
|
title,
|
||||||
@ -142,7 +140,7 @@ const LineChartInner: React.FC<PlotlyLineChartProps> = ({
|
|||||||
{
|
{
|
||||||
x: parsedData.data.map((d: any) => d[xAxis]),
|
x: parsedData.data.map((d: any) => d[xAxis]),
|
||||||
y: parsedData.data.map((d: any) => d[yAxis]),
|
y: parsedData.data.map((d: any) => d[yAxis]),
|
||||||
mode: "lines",
|
mode: 'lines',
|
||||||
name: lineLabel,
|
name: lineLabel,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const meta: Meta = {
|
|||||||
'Data to be displayed.\n\n GeoJSON Object \n\nOR\n\n URL to GeoJSON Object',
|
'Data to be displayed.\n\n GeoJSON Object \n\nOR\n\n URL to GeoJSON Object',
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
description: 'Title to display on the map. Optional.',
|
description: 'Title to display on the map.',
|
||||||
},
|
},
|
||||||
center: {
|
center: {
|
||||||
description: 'Initial coordinates of the center of the map',
|
description: 'Initial coordinates of the center of the map',
|
||||||
|
|||||||
@ -28,10 +28,10 @@ Must be an object with one of the following properties: `url`, `values` or `csv`
|
|||||||
},
|
},
|
||||||
parsingConfig: {
|
parsingConfig: {
|
||||||
description:
|
description:
|
||||||
'If using URL or CSV, this parsing config will be used to parse the data. Optional, check https://www.papaparse.com/ for more info.',
|
'If using URL or CSV, this parsing config will be used to parse the data. Check https://www.papaparse.com/ for more info.',
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
description: 'Title to display on the chart. Optional.',
|
description: 'Title to display on the chart.',
|
||||||
},
|
},
|
||||||
// TODO: commented out because this doesn't work
|
// TODO: commented out because this doesn't work
|
||||||
// lineLabel: {
|
// lineLabel: {
|
||||||
|
|||||||
@ -1,45 +1,52 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { PlotlyLineChart, PlotlyLineChartProps } from '../src/components/PlotlyLineChart';
|
import {
|
||||||
|
PlotlyLineChart,
|
||||||
|
PlotlyLineChartProps,
|
||||||
|
} from '../src/components/PlotlyLineChart';
|
||||||
|
|
||||||
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
|
|
||||||
const meta: Meta = {
|
const meta: Meta = {
|
||||||
title: 'Components/Charts/PlotlyLineChart',
|
title: 'Components/Charts/PlotlyLineChart',
|
||||||
component: PlotlyLineChart,
|
component: PlotlyLineChart,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
url: {
|
|
||||||
description:
|
|
||||||
'CSV Url to be parsed and used as data source',
|
|
||||||
},
|
|
||||||
data: {
|
data: {
|
||||||
description:
|
description:
|
||||||
'Data to be displayed. as an array of key value pairs \n\n E.g.: [{ year: 1850, temperature: -0.41765878 }, { year: 1851, temperature: -0.2333498 }, ...]',
|
'Data to be displayed. \n\n \
|
||||||
},
|
Must be an object with one of the following properties: `url`, `values` or `csv` \n\n \
|
||||||
rawCsv: {
|
`url`: URL pointing to a CSV file. \n\n \
|
||||||
description:
|
`values`: array of objects. \n\n \
|
||||||
'Raw csv data to be parsed and used as data source',
|
`csv`: string with valid CSV. \n\n \
|
||||||
|
',
|
||||||
},
|
},
|
||||||
bytes: {
|
bytes: {
|
||||||
|
// TODO: likely this should be an extra option on the data parameter,
|
||||||
|
// specific to URLs
|
||||||
description:
|
description:
|
||||||
'How many bytes to read from the url',
|
"How many bytes to read from the url so that the entire file doesn's have to be fetched.",
|
||||||
},
|
},
|
||||||
parsingConfig: {
|
parsingConfig: {
|
||||||
description: 'If using url or rawCsv, this parsing config will be used to parse the data. Optional, check https://www.papaparse.com/ for more info',
|
description:
|
||||||
|
'If using URL or CSV, this parsing config will be used to parse the data. Check https://www.papaparse.com/ for more info',
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
description: 'Title to display on the chart. Optional.',
|
description: 'Title to display on the chart.',
|
||||||
},
|
},
|
||||||
lineLabel: {
|
lineLabel: {
|
||||||
description: 'Label to display on the line, Optional, will use yAxis if not provided',
|
description:
|
||||||
|
'Label to display on the line, will use yAxis if not provided',
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
description:
|
description:
|
||||||
'Name of the X axis on the data. Required when the "data" parameter is an URL.',
|
'Name of the column header or object property that represents the X-axis on the data.',
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
description:
|
description:
|
||||||
'Name of the Y axis on the data. Required when the "data" parameter is an URL.',
|
'Name of the column header or object property that represents the Y-axis on the data.',
|
||||||
|
},
|
||||||
|
uniqueId: {
|
||||||
|
description:
|
||||||
|
'Provide a unique ID to help with cache revalidation of the fetched data.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -51,13 +58,15 @@ type Story = StoryObj<PlotlyLineChartProps>;
|
|||||||
export const FromDataPoints: Story = {
|
export const FromDataPoints: Story = {
|
||||||
name: 'Line chart from array of data points',
|
name: 'Line chart from array of data points',
|
||||||
args: {
|
args: {
|
||||||
data: [
|
data: {
|
||||||
{year: '1850', temperature: -0.41765878},
|
values: [
|
||||||
{year: '1851', temperature: -0.2333498},
|
{ year: '1850', temperature: -0.41765878 },
|
||||||
{year: '1852', temperature: -0.22939907},
|
{ year: '1851', temperature: -0.2333498 },
|
||||||
{year: '1853', temperature: -0.27035445},
|
{ year: '1852', temperature: -0.22939907 },
|
||||||
{year: '1854', temperature: -0.29163003},
|
{ year: '1853', temperature: -0.27035445 },
|
||||||
],
|
{ year: '1854', temperature: -0.29163003 },
|
||||||
|
],
|
||||||
|
},
|
||||||
xAxis: 'year',
|
xAxis: 'year',
|
||||||
yAxis: 'temperature',
|
yAxis: 'temperature',
|
||||||
},
|
},
|
||||||
@ -67,8 +76,26 @@ export const FromURL: Story = {
|
|||||||
name: 'Line chart from URL',
|
name: 'Line chart from URL',
|
||||||
args: {
|
args: {
|
||||||
title: 'Oil Price x Year',
|
title: 'Oil Price x Year',
|
||||||
url: 'https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv',
|
data: {
|
||||||
|
url: 'https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv',
|
||||||
|
},
|
||||||
xAxis: 'Date',
|
xAxis: 'Date',
|
||||||
yAxis: 'Price',
|
yAxis: 'Price',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FromInlineCSV: Story = {
|
||||||
|
name: 'Bar chart from inline CSV',
|
||||||
|
args: {
|
||||||
|
title: 'Apple Stock Prices',
|
||||||
|
data: {
|
||||||
|
csv: `Date,AAPL.Open,AAPL.High,AAPL.Low,AAPL.Close,AAPL.Volume,AAPL.Adjusted,dn,mavg,up,direction
|
||||||
|
2015-02-17,127.489998,128.880005,126.919998,127.830002,63152400,122.905254,106.7410523,117.9276669,129.1142814,Increasing
|
||||||
|
2015-02-18,127.629997,128.779999,127.449997,128.720001,44891700,123.760965,107.842423,118.9403335,130.0382439,Increasing
|
||||||
|
2015-02-19,128.479996,129.029999,128.330002,128.449997,37362400,123.501363,108.8942449,119.8891668,130.8840887,Decreasing
|
||||||
|
2015-02-20,128.619995,129.5,128.050003,129.5,48948400,124.510914,109.7854494,120.7635001,131.7415509,Increasing`,
|
||||||
|
},
|
||||||
|
xAxis: 'Date',
|
||||||
|
yAxis: 'AAPL.Open',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user