Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Charts: Adding a theme provider #40558

Merged
merged 6 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading