Skip to content

Commit

Permalink
Charts: Adding multi series support for line charts (#40605)
Browse files Browse the repository at this point in the history
* Charts - Adding basic pie chart

* Charts - Updating charts and adding a tooltip hook

* Charts - Adding doughts to pies

* Charts - Fixing dependency

* changelog

* Charts - Adding chart TS interface

* Charts - Adding support for multiple data sets

* Charts - Adding shared chart TS interface

* Charts - Cleanup

* Charts - Cleanup 2

* Charts - Fixing tooltips for multiple data sets, adding className prop

* Charts - Updating examples

* Charts - updating tooltip prop and simplifying event handlers

* changelog
  • Loading branch information
grzegorz-cp authored Dec 17, 2024
1 parent 058df59 commit 9582f25
Show file tree
Hide file tree
Showing 11 changed files with 494 additions and 221 deletions.
4 changes: 4 additions & 0 deletions projects/js-packages/charts/changelog/add-charts-multi-series
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Adding support for mutliple data series for the line charts.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const BarChart: FC< BarChartProps > = ( {
height,
margin = { top: 20, right: 20, bottom: 40, left: 40 },
withTooltips = false,
className,
} ) => {
const theme = useChartTheme();
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } =
Expand Down Expand Up @@ -63,7 +64,7 @@ const BarChart: FC< BarChartProps > = ( {
}, [ hideTooltip ] );

return (
<div className={ clsx( 'bar-chart', styles[ 'bar-chart' ] ) }>
<div className={ clsx( 'bar-chart', className, styles[ 'bar-chart' ] ) }>
<svg width={ width } height={ height }>
<Group left={ margins.left } top={ margins.top }>
{ data.map( d => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@
position: relative;

&__tooltip {
background: #fff;
padding: 0.5rem;
}

&__tooltip-date {
font-weight: bold;
padding-bottom: 10px;
}

&-row {
margin-bottom: 0.25rem;
&__tooltip-row {
display: flex;
align-items: center;
padding: 4px 0;
justify-content: space-between;
}

&:last-child {
margin-bottom: 0;
}
}
&__tooltip-label {
font-weight: 500;
padding-right: 1rem;
}
}
109 changes: 71 additions & 38 deletions projects/js-packages/charts/src/components/line-chart/line-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,54 @@ import clsx from 'clsx';
import { FC } from 'react';
import { useChartTheme } from '../../providers/theme/theme-provider';
import styles from './line-chart.module.scss';
import type { BaseChartProps, DataPointDate } from '../shared/types';
import type { BaseChartProps, DataPointDate, SeriesData } from '../shared/types';

// TODO: revisit grid and axis options - accept as props for frid lines, axis, values: x, y, all, none

interface LineChartProps extends BaseChartProps< DataPointDate[] > {}
interface LineChartProps extends BaseChartProps< SeriesData[] > {}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderTooltip: any = ( { tooltipData } ) => {
// TODO: fix any
const datum = tooltipData?.nearestDatum?.datum;
if ( ! datum ) {
return null;
}
type TooltipData = {
date: Date;
[ key: string ]: number | Date;
};

type TooltipDatum = {
key: string;
value: number;
};

const renderTooltip = ( {
tooltipData,
}: {
tooltipData?: {
nearestDatum?: {
datum: TooltipData;
key: string;
};
datumByKey?: { [ key: string ]: { datum: TooltipData } };
};
} ) => {
const nearestDatum = tooltipData?.nearestDatum?.datum;
if ( ! nearestDatum ) return null;

const tooltipPoints: TooltipDatum[] = Object.entries( tooltipData?.datumByKey || {} )
.map( ( [ key, { datum } ] ) => ( {
key,
value: datum.value as number,
} ) )
.sort( ( a, b ) => b.value - a.value );

return (
<div className={ styles[ 'line-chart__tooltip' ] }>
<div className={ styles[ 'line-chart__tooltip-row' ] }>
<strong>Date:</strong> { datum.date.toLocaleDateString() }
</div>
<div className={ styles[ 'line-chart__tooltip-row' ] }>
<strong>Value:</strong> { datum.value }
<div className={ styles[ 'line-chart__tooltip-date' ] }>
{ nearestDatum.date.toLocaleDateString() }
</div>
{ tooltipPoints.map( point => (
<div key={ point.key } className={ styles[ 'line-chart__tooltip-row' ] }>
<span className={ styles[ 'line-chart__tooltip-label' ] }>{ point.key }:</span>
<span className={ styles[ 'line-chart__tooltip-value' ] }>{ point.value }</span>
</div>
) ) }
</div>
);
};
Expand All @@ -44,35 +70,38 @@ const formatDateTick = ( value: number ) => {
} );
};

// TODO: add support for multiple data sets

const LineChart: FC< LineChartProps > = ( {
data,
width,
height,
margin = { top: 20, right: 20, bottom: 40, left: 40 },
className,
withTooltips = true,
} ) => {
const providerTheme = useChartTheme();

if ( ! data.length ) {
return (
<div className={ clsx( 'line-chart-empty', styles[ 'line-chart-empty' ] ) }>Empty...</div>
);
}

const accessors = {
xAccessor: ( d: DataPointDate ) => d.date,
yAccessor: ( d: DataPointDate ) => d.value,
};

// Use theme to construct XYChart theme
const chartTheme = {
const theme = buildChartTheme( {
backgroundColor: providerTheme.backgroundColor,
colors: providerTheme.colors,
gridStyles: providerTheme.gridStyles,
tickLength: providerTheme?.tickLength || 0,
gridColor: providerTheme?.gridColor || '',
gridColorDark: providerTheme?.gridColorDark || '',
};

const theme = buildChartTheme( chartTheme );
} );

//
return (
<div className={ clsx( 'line-chart', styles[ 'line-chart' ] ) }>
<div className={ clsx( 'line-chart', styles[ 'line-chart' ], className ) }>
<XYChart
theme={ theme }
width={ width }
Expand All @@ -82,24 +111,28 @@ const LineChart: FC< LineChartProps > = ( {
yScale={ { type: 'linear', nice: true } }
>
<AnimatedGrid columns={ false } numTicks={ 4 } />

<AnimatedAxis orientation="bottom" numTicks={ 5 } tickFormat={ formatDateTick } />
<AnimatedAxis orientation="left" numTicks={ 4 } />

<AnimatedLineSeries
dataKey="Line"
data={ data }
{ ...accessors }
stroke={ theme.colors[ 0 ] }
strokeWidth={ 2 }
/>

<Tooltip
snapTooltipToDatumX
snapTooltipToDatumY
showSeriesGlyphs
renderTooltip={ renderTooltip }
/>
{ data.map( ( seriesData, index ) => (
<AnimatedLineSeries
key={ seriesData?.label }
dataKey={ seriesData?.label }
data={ seriesData.data }
{ ...accessors }
stroke={ theme.colors[ index % theme.colors.length ] }
strokeWidth={ 2 }
/>
) ) }

{ withTooltips && (
<Tooltip
snapTooltipToDatumX
snapTooltipToDatumY
showSeriesGlyphs
renderTooltip={ renderTooltip }
/>
) }
</XYChart>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,39 @@ export default {

const Template = args => <LineChart { ...args } />;

// Default story with multiple series
export const Default = Template.bind( {} );
Default.args = {
width: 500,
height: 300,
margin: { top: 20, right: 20, bottom: 30, left: 40 },
data: sampleData.mars,
data: sampleData,
};

// Story with single data series
export const SingleSeries = Template.bind( {} );
SingleSeries.args = {
width: 500,
height: 300,
margin: { top: 20, right: 20, bottom: 30, left: 40 },
data: [ sampleData[ 0 ] ], // Only London temperature data
};

// Story without tooltip
export const WithoutTooltip = Template.bind( {} );
WithoutTooltip.args = {
width: 500,
height: 300,
margin: { top: 20, right: 20, bottom: 30, left: 40 },
data: sampleData,
withTooltips: false,
};

// Story with custom dimensions
export const CustomDimensions = Template.bind( {} );
CustomDimensions.args = {
width: 800,
height: 400,
margin: { top: 20, right: 20, bottom: 30, left: 40 },
data: sampleData,
};
Loading

0 comments on commit 9582f25

Please sign in to comment.