[feat,LineChart][s]: support for multiple series

This commit is contained in:
Ola Rubaj 2024-10-23 17:55:50 +02:00
parent f86f0541eb
commit 72f78387ce
3 changed files with 147 additions and 73 deletions

View File

@ -0,0 +1,5 @@
---
'@portaljs/components': minor
---
Support for plotting multiple series in LineChart component.

View File

@ -8,86 +8,106 @@ type AxisType = 'quantitative' | 'temporal';
type TimeUnit = 'year' | undefined; // or ... type TimeUnit = 'year' | undefined; // or ...
export type LineChartProps = { export type LineChartProps = {
data: Omit<Data, 'csv'>; data: Omit<Data, 'csv'>;
title?: string; title?: string;
xAxis: string; xAxis: string;
xAxisType?: AxisType; xAxisType?: AxisType;
xAxisTimeUnit?: TimeUnit; xAxisTimeUnit?: TimeUnit;
yAxis: string; yAxis: string | string[];
yAxisType?: AxisType; yAxisType?: AxisType;
fullWidth?: boolean; fullWidth?: boolean;
symbol?: string;
}; };
export function LineChart({ export function LineChart({
data, data,
title = '', title = '',
xAxis, xAxis,
xAxisType = 'temporal', xAxisType = 'temporal',
xAxisTimeUnit = 'year', // TODO: defaults to undefined would probably work better... keeping it as it's for compatibility purposes xAxisTimeUnit = 'year', // TODO: defaults to undefined would probably work better... keeping it as it's for compatibility purposes
yAxis, yAxis,
yAxisType = 'quantitative', yAxisType = 'quantitative',
symbol,
}: LineChartProps) { }: LineChartProps) {
const url = data.url; const url = data.url;
const values = data.values; const values = data.values;
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
// By default, assumes data is an Array... // By default, assumes data is an Array...
const [specData, setSpecData] = useState<any>({ name: 'table' }); const [specData, setSpecData] = useState<any>({ name: 'table' });
const isMultiYAxis = Array.isArray(yAxis);
const spec = { const spec = {
$schema: 'https://vega.github.io/schema/vega-lite/v5.json', $schema: 'https://vega.github.io/schema/vega-lite/v5.json',
title, title,
width: 'container', width: 'container',
height: 300, height: 300,
mark: { mark: {
type: 'line', type: 'line',
color: 'black', color: 'black',
strokeWidth: 1, strokeWidth: 1,
tooltip: true, tooltip: true,
}, },
data: specData, data: specData,
selection: { ...(isMultiYAxis ? {
grid: { transform: [
type: 'interval', { fold: yAxis, as: ['key', 'value'] }
bind: 'scales', ]
}, } : {}),
}, selection: {
encoding: { grid: {
x: { type: 'interval',
field: xAxis, bind: 'scales',
timeUnit: xAxisTimeUnit, },
type: xAxisType, },
}, encoding: {
y: { x: {
field: yAxis, field: xAxis,
type: yAxisType, timeUnit: xAxisTimeUnit,
}, type: xAxisType,
}, },
} as any; y: {
field: isMultiYAxis ? 'value' : yAxis,
type: yAxisType,
},
...(symbol ? {
color: {
field: symbol,
type: 'nominal'
}
} : {}),
...(isMultiYAxis ? {
color: {
field: 'key',
type: 'nominal'
}
} : {})
},
} as any;
useEffect(() => { useEffect(() => {
if (url) { if (url) {
setIsLoading(true); setIsLoading(true);
// Manualy loading the data allows us to do other kinds // Manualy loading the data allows us to do other kinds
// of stuff later e.g. load a file partially // of stuff later e.g. load a file partially
loadData(url).then((res: any) => { loadData(url).then((res: any) => {
setSpecData({ values: res, format: { type: 'csv' } }); setSpecData({ values: res, format: { type: 'csv' } });
setIsLoading(false); setIsLoading(false);
}); });
}
}, []);
var vegaData = {};
if (values) {
vegaData = { table: values };
} }
}, []);
var vegaData = {}; return isLoading ? (
if (values) { <div className="w-full flex items-center justify-center w-[600px] h-[300px]">
vegaData = { table: values }; <LoadingSpinner />
} </div>
) : (
return isLoading ? ( <VegaLite data={vegaData} spec={spec} />
<div className="w-full flex items-center justify-center w-[600px] h-[300px]"> );
<LoadingSpinner />
</div>
) : (
<VegaLite data={vegaData} spec={spec} />
);
} }

View File

@ -30,11 +30,14 @@ Must be an object with one of the following properties: `url` or `values` \n\n \
}, },
yAxis: { yAxis: {
description: description:
'Name of the column header or object property that represents the Y-axis on the data.', 'Name of the column headers or object properties that represent the Y-axis on the data.',
}, },
yAxisType: { yAxisType: {
description: 'Type of the Y-axis', description: 'Type of the Y-axis',
}, },
symbol: {
description: 'Name of the column header or object property that represents a series for multiple series.',
}
}, },
}; };
@ -60,6 +63,52 @@ export const FromDataPoints: Story = {
}, },
}; };
export const MultiSeries: Story = {
name: 'Line chart with multiple series (specifying symbol)',
args: {
data: {
values: [
{ year: '1850', value: -0.41765878, z: 'A' },
{ year: '1851', value: -0.2333498, z: 'A' },
{ year: '1852', value: -0.22939907, z: 'A' },
{ year: '1853', value: -0.27035445, z: 'A' },
{ year: '1854', value: -0.29163003, z: 'A' },
{ year: '1850', value: -0.42993882, z: 'B' },
{ year: '1851', value: -0.30365549, z: 'B' },
{ year: '1852', value: -0.27905189, z: 'B' },
{ year: '1853', value: -0.22939704, z: 'B' },
{ year: '1854', value: -0.25688013, z: 'B' },
{ year: '1850', value: -0.4757164, z: 'C' },
{ year: '1851', value: -0.41971018, z: 'C' },
{ year: '1852', value: -0.40724799, z: 'C' },
{ year: '1853', value: -0.45049156, z: 'C' },
{ year: '1854', value: -0.41896583, z: 'C' },
],
},
xAxis: 'year',
yAxis: 'value',
symbol: 'z',
},
};
export const MultiColumns: Story = {
name: 'Line chart with multiple series (with multiple columns)',
args: {
data: {
values: [
{ year: '1850', A: -0.41765878, B: -0.42993882, C: -0.4757164 },
{ year: '1851', A: -0.2333498, B: -0.30365549, C: -0.41971018 },
{ year: '1852', A: -0.22939907, B: -0.27905189, C: -0.40724799 },
{ year: '1853', A: -0.27035445, B: -0.22939704, C: -0.45049156 },
{ year: '1854', A: -0.29163003, B: -0.25688013, C: -0.41896583 },
],
},
xAxis: 'year',
yAxis: ['A', 'B', 'C']
},
};
export const FromURL: Story = { export const FromURL: Story = {
name: 'Line chart from URL', name: 'Line chart from URL',
args: { args: {