Skip to content

Commit

Permalink
Charts: Adding a theme provider (#40558)
Browse files Browse the repository at this point in the history
* Charts - Adding a Theme provider and themes

* Charts - Updating theme objects with colours

* Charts - Updating examples

* changelog

* Charts - Fixing double imports
  • Loading branch information
grzegorz-cp authored Dec 11, 2024
1 parent a217fd3 commit 6be299d
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 58 deletions.
4 changes: 4 additions & 0 deletions projects/js-packages/charts/changelog/add-charts-themes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Adding a theme provider to Automattic Charts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { scaleBand, scaleLinear } from '@visx/scale';
import { Bar } from '@visx/shape';
import { useTooltip } from '@visx/tooltip';
import React from 'react';
import { useChartTheme } from '../../providers/theme';
import { Tooltip } from '../tooltip';
import type { DataPoint } from '../shared/types';

Expand All @@ -28,6 +29,7 @@ function BarChart( { data, width, height, margin, showTooltips = false }: BarCha
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } =
useTooltip< DataPoint >();

const theme = useChartTheme();
const margins = { top: 20, right: 20, bottom: 40, left: 40, ...margin };
const xMax = width - margins.left - margins.right;
const yMax = height - margins.top - margins.bottom;
Expand Down Expand Up @@ -80,7 +82,7 @@ function BarChart( { data, width, height, margin, showTooltips = false }: BarCha
y={ yScale( d.value ) }
width={ xScale.bandwidth() }
height={ yMax - ( yScale( d.value ) ?? 0 ) }
fill="#0675C4"
fill={ theme.colors[ 0 ] }
onMouseMove={ getMouseMoveHandler( d ) }
onMouseLeave={ showTooltips ? handleMouseLeave : undefined }
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import BarChart from '../index';
import type { Meta } from '@storybook/react';

export default {
title: 'JS Packages/Charts/Bar Chart',
title: 'JS Packages/Charts/Types/Bar Chart',
component: BarChart,
parameters: {
layout: 'centered',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@visx/xychart';
import clsx from 'clsx';
import { FC } from 'react';
import { useChartTheme } from '../../providers/theme/theme-provider';
import styles from './line-chart.module.scss';
import type { DataPointDate } from '../shared/types';

Expand Down Expand Up @@ -36,52 +37,6 @@ type LineChartProps = {
lineColor?: string;
};

// TODO: move to a provider
// const customTheme = buildChartTheme( {
// // Customize colors
// colors: [ '#3182ce' ],
// // Customize typography
// // labelStyles: {
// // fill: '#666',
// // fontSize: 12,
// // },
// // Customize grid styles
// gridStyles: {
// stroke: '#e2e8f0',
// strokeWidth: 1,
// },
// } );

const customTheme = buildChartTheme( {
// colors
backgroundColor: 'lightblue', // used by Tooltip, Annotation
colors: [ '#3182ce' ], // categorical colors, mapped to series via `dataKey`s

// labels
// svgLabelBig?: SVGTextProps,
// svgLabelSmall?: SVGTextProps,
// htmlLabel?: HTMLTextStyles,

// lines
// xAxisLineStyles?: LineStyles,
// yAxisLineStyles?: LineStyles,
// xTickLineStyles?: LineStyles,
// yTickLineStyles?: LineStyles,
// tickLength: number,

// grid
// gridColor: string,
// gridColorDark: string, // used for axis baseline if x/yxAxisLineStyles not set
// gridStyles?: CSSProperties,
gridStyles: {
stroke: '#e2e8f0',
strokeWidth: 1,
},
tickLength: 0,
gridColor: '',
gridColorDark: '',
} );

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderTooltip: any = ( { tooltipData } ) => {
// TODO: fix any
Expand Down Expand Up @@ -117,18 +72,30 @@ const LineChart: FC< LineChartProps > = ( {
width,
height,
margin = { top: 20, right: 20, bottom: 40, left: 40 },
lineColor = '#3182ce',
} ) => {
const providerTheme = useChartTheme();
const accessors = {
xAccessor: ( d: DataPointDate ) => d.date,
yAccessor: ( d: DataPointDate ) => d.value,
};

// Use theme to construct XYChart theme
const chartTheme = {
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' ] ) }>
<XYChart
theme={ customTheme }
theme={ theme }
width={ width }
height={ height }
margin={ margin }
Expand All @@ -144,7 +111,7 @@ const LineChart: FC< LineChartProps > = ( {
dataKey="Line"
data={ data }
{ ...accessors }
stroke={ lineColor }
stroke={ theme.colors[ 0 ] }
strokeWidth={ 2 }
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const data = [
];

export default {
title: 'JS Packages/Charts/Line Chart',
title: 'JS Packages/Charts/Types/Line Chart',
component: LineChart,
parameters: {
layout: 'centered',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Pie } from '@visx/shape';
import { Text } from '@visx/text';
import clsx from 'clsx';
import { FC } from 'react';
import { useChartTheme } from '../../providers/theme/theme-provider';
import styles from './pie-semi-circle-chart.module.scss';
import type { DataPointPercentage } from '../shared/types';

Expand Down Expand Up @@ -38,13 +39,15 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( {
label,
note,
} ) => {
const providerTheme = useChartTheme();
const centerX = width / 2;
const centerY = height;

const accessors = {
value: d => d.value,
sort: ( a, b ) => a.value - b.value,
fill: d => d.data.color,
// Use the color property from the data object as a last resort. The theme provides colours by default.
fill: d => d.color || providerTheme.colors[ d.index ],
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,23 @@ const data = [
value: 80000,
valueDisplay: '$80K',
percentage: 2,
color: '#3858E9',
},
{
label: 'MacOS',
value: 30000,
valueDisplay: '$30K',
percentage: 5,
color: '#80C8FF',
},
{
label: 'Linux',
value: 22000,
valueDisplay: '$22K',
percentage: 1,
color: '#B999FF',
},
];

export default {
title: 'JS Packages/Charts/Pie Semi Circle Chart',
title: 'JS Packages/Charts/Types/Pie Semi Circle Chart',
component: PieSemiCircleChart,
parameters: {
layout: 'centered',
Expand Down
22 changes: 21 additions & 1 deletion projects/js-packages/charts/src/components/shared/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { CSSProperties } from 'react';

export type DataPoint = {
label: string;
value: number;
Expand Down Expand Up @@ -26,7 +28,25 @@ export type DataPointPercentage = {
*/
percentage: number;
/**
* Color code for the segment
* Color code for the segment, by default colours are taken from the theme but this property can overrides it
*/
color?: string;
};

/**
* Theme configuration for chart components
*/
export type ChartTheme = {
/** Background color for chart components */
backgroundColor: string;
/** Array of colors used for data visualization */
colors: string[];
/** Optional CSS styles for grid lines */
gridStyles?: CSSProperties;
/** Length of axis ticks in pixels */
tickLength: number;
/** Color of the grid lines */
gridColor: string;
/** Color of the grid lines in dark mode */
gridColorDark: string;
};
2 changes: 2 additions & 0 deletions projects/js-packages/charts/src/providers/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ThemeProvider, useChartTheme } from './theme-provider';
export { defaultTheme, jetpackTheme, wooTheme } from './themes';
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { Meta, StoryObj } from '@storybook/react';
import { ThemeProvider, jetpackTheme, wooTheme } from '../.';
import { LineChart, BarChart, PieSemiCircleChart } from '../../..';

const meta: Meta< typeof LineChart > = {
title: 'JS Packages/Charts/Themes',
component: ThemeProvider,
parameters: {
layout: 'centered',
},
};

export default meta;
type Story = StoryObj< typeof ThemeProvider >;

const sampleData = [
{ date: new Date( '2024-01-01' ), value: 10, label: 'Jan 1' },
{ date: new Date( '2024-01-02' ), value: 20, label: 'Jan 2' },
{ date: new Date( '2024-01-03' ), value: 15, label: 'Jan 3' },
{ date: new Date( '2024-01-04' ), value: 25, label: 'Jan 4' },
{ date: new Date( '2024-01-05' ), value: 30, label: 'Jan 5' },
];

const pieData = [
{
label: 'Windows',
value: 80000,
valueDisplay: '80K',
percentage: 2,
},
{
label: 'MacOS',
value: 30000,
valueDisplay: '30K',
percentage: 5,
},
{
label: 'Linux',
value: 22000,
valueDisplay: '22K',
percentage: 1,
},
];

const GridComponent = ( { children } ) => {
return (
<div style={ { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '1rem' } }>
{ children }
</div>
);
};

export const Default: Story = {
render: () => (
<ThemeProvider>
<GridComponent>
<LineChart data={ sampleData } width={ 400 } height={ 300 } />
<BarChart data={ sampleData } width={ 400 } height={ 300 } />
<PieSemiCircleChart
data={ pieData }
width={ 400 }
height={ 200 }
label="Pie Chart"
note="Default Theme"
/>
</GridComponent>
</ThemeProvider>
),
};

export const JetpackTheme: Story = {
render: () => (
<ThemeProvider theme={ jetpackTheme }>
<GridComponent>
<LineChart data={ sampleData } width={ 400 } height={ 300 } />
<BarChart data={ sampleData } width={ 400 } height={ 300 } />
<PieSemiCircleChart
data={ pieData }
width={ 400 }
height={ 200 }
label="Pie Chart"
note="Jetpack Theme"
/>
</GridComponent>
</ThemeProvider>
),
};

export const WooTheme: Story = {
render: () => (
<ThemeProvider theme={ wooTheme }>
<GridComponent>
<LineChart data={ sampleData } width={ 400 } height={ 300 } />
<BarChart data={ sampleData } width={ 400 } height={ 300 } />
<PieSemiCircleChart
data={ pieData }
width={ 400 }
height={ 200 }
label="Pie Chart"
note="Woo Theme"
/>
</GridComponent>
</ThemeProvider>
),
};

export const CustomColorTheme: Story = {
render: () => (
<ThemeProvider
theme={ {
colors: [ '#073B3A', '#0B6E4F', '#08A045', '#6BBF59', '#DDB771' ],
gridStyles: {
stroke: '#ffe3e3',
strokeWidth: 2,
},
} }
>
<GridComponent>
<LineChart data={ sampleData } width={ 400 } height={ 300 } />
<BarChart data={ sampleData } width={ 400 } height={ 300 } />
<PieSemiCircleChart
data={ pieData }
width={ 400 }
height={ 200 }
label="Pie Chart"
note="Custom Color Theme"
/>
</GridComponent>
</ThemeProvider>
),
};
Loading

0 comments on commit 6be299d

Please sign in to comment.