feat: PlotlyLineChart component API and docs improvements

This commit is contained in:
Demenech 2024-04-09 17:08:50 -03:00
parent 0aed7dce77
commit 059ffe4e34
4 changed files with 85 additions and 60 deletions

View File

@ -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,
}, },
]} ]}

View File

@ -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',

View File

@ -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: {

View File

@ -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',
},
};