Skip to content
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
7 changes: 7 additions & 0 deletions .changeset/quick-needles-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@lg-charts/core': patch
'@lg-charts/drag-provider': patch
---

introduce `Series` abstraction as a superclass of `Line` this allows supporting more diverse series
types such as `Bar` in a follow up PR
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# IDE files
.idea
*.iml

# Locally-installed dependencies
node_modules/

Expand Down
2 changes: 1 addition & 1 deletion charts/core/src/Chart.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { StoryObj } from '@storybook/react';
import { ChartProps } from './Chart/Chart.types';
import { ChartHeaderProps } from './ChartHeader/ChartHeader.types';
import { ChartTooltipProps } from './ChartTooltip/ChartTooltip.types';
import { LineProps } from './Line';
import { LineProps } from './Series';
import { makeLineData } from './testUtils';
import { ThresholdLineProps } from './ThresholdLine';
import {
Expand Down
39 changes: 34 additions & 5 deletions charts/core/src/Echart/Echart.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { XAXisComponentOption, YAXisComponentOption } from 'echarts';
import type { LineSeriesOption } from 'echarts/charts';
import type {
DatasetComponentOption,
GridComponentOption,
Expand All @@ -9,16 +8,45 @@ import type {
TooltipComponentOption,
} from 'echarts/components';
import type { ComposeOption, EChartsType } from 'echarts/core';
import {
BarSeriesOption,
LineSeriesOption,
SeriesOption,
} from 'echarts/types/dist/shared';

import { Theme } from '@leafygreen-ui/lib';
import { Theme, ValuesOf } from '@leafygreen-ui/lib';

// Type not exported by echarts.
// reference: https://github.com/apache/echarts/blob/master/src/coord/axisCommonTypes.ts#L193
export type AxisLabelValueFormatter = (value: number, index?: number) => string;

type RequiredSeriesProps = 'type' | 'name' | 'data';
export type EChartSeriesOption = Pick<LineSeriesOption, RequiredSeriesProps> &
Partial<Omit<LineSeriesOption, RequiredSeriesProps>>;
export interface StylingContext {
seriesColor?: string;
}

// to convert an SeriesOption type of echarts into a structured form more aligned with LeafyGreen design standards,
// where the 'type', 'name', and 'data' fields are explicitly required and typed,
// and all additional series properties encapsulated within the 'options' object
interface DisciplinedSeriesOption<EChartType extends SeriesOption> {
type: NonNullable<EChartType['type']>;
name: string;
data: NonNullable<EChartType['data']>;
options: Omit<EChartType, 'type' | 'name' | 'data'>;
}

// all supported series options types disciplined and grouped into a single interface
export interface EChartSeriesOptions {
line: DisciplinedSeriesOption<LineSeriesOption>;
// TODO: to be leveraged in a follow-up PR to add Bar chart support
bar: DisciplinedSeriesOption<BarSeriesOption>;
}

// a disciplined substitute for SeriesOption type of echarts limited to the ones supported here
export type EChartSeriesOption = Omit<
ValuesOf<EChartSeriesOptions>,
'options'
> &
ValuesOf<EChartSeriesOptions>['options'];

/**
* TODO: This might need to be improved. `ComposeOption` appears to make most base option
Expand Down Expand Up @@ -108,6 +136,7 @@ interface EChartsEventHandlerType {
callback: (params: any) => void,
options?: Partial<{ useCanvasAsTrigger: boolean }>,
): void;

(
event: 'zoomselect',
callback: (params: EChartZoomSelectionEvent) => void,
Expand Down
23 changes: 17 additions & 6 deletions charts/core/src/Echart/utils/updateUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
import { EChartOptions } from '../Echart.types';
import { EChartOptions, EChartSeriesOption } from '../Echart.types';

import { addSeries, removeSeries, updateOptions } from './updateUtils';

describe('@lg-charts/core/Chart/hooks/updateUtils', () => {
test('addSeries should add a series to the chart options', () => {
const currentOptions: Partial<EChartOptions> = {
series: [{ name: 'series1' }],
series: [{ type: 'line', name: 'series1', data: [] }],
};
const newSeriesName = 'series2';
const data = { name: newSeriesName };
const data: EChartSeriesOption = {
type: 'line',
name: newSeriesName,
data: [],
};
const updatedOptions = addSeries(currentOptions, data);
expect(updatedOptions.series).toHaveLength(2);
expect(updatedOptions.series?.[1].name).toBe(newSeriesName);
});

test('addSeries should not add a series if a chart with the same name exists', () => {
const currentOptions: Partial<EChartOptions> = {
series: [{ name: 'series1' }],
series: [{ type: 'line', name: 'series1', data: [] }],
};
const newSeriesName = 'series1';
const data = { name: newSeriesName };
const data: EChartSeriesOption = {
type: 'line',
name: newSeriesName,
data: [],
};
const updatedOptions = addSeries(currentOptions, data);
expect(updatedOptions.series).toHaveLength(1);
expect(updatedOptions.series?.[0].name).toBe(newSeriesName);
});

test('removeSeries should remove a series from the chart options', () => {
const currentOptions: Partial<EChartOptions> = {
series: [{ name: 'series1' }, { name: 'series2' }],
series: [
{ type: 'line', name: 'series1', data: [] },
{ type: 'line', name: 'series2', data: [] },
],
};
const seriesName1 = 'series1';
const seriesName2 = 'series2';
Expand Down
2 changes: 2 additions & 0 deletions charts/core/src/EventMarkers/BaseEventMarker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export function getMarkConfig({
symbolSize: [16, 16],
symbolRotate: 360, // Icon shows upside down without this
},
data: [],
} as SeriesOption;
} else {
return {
Expand All @@ -130,6 +131,7 @@ export function getMarkConfig({
symbolSize: [16, 16],
symbol: generateSymbolDataUri(level, theme),
},
data: [],
} as SeriesOption;
}
}
54 changes: 0 additions & 54 deletions charts/core/src/Line/Line.tsx

This file was deleted.

20 changes: 0 additions & 20 deletions charts/core/src/Line/config/defaultLineOptions.ts

This file was deleted.

1 change: 0 additions & 1 deletion charts/core/src/Line/config/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions charts/core/src/Line/index.ts

This file was deleted.

44 changes: 44 additions & 0 deletions charts/core/src/Series/Line/Line.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';

import { EChartSeriesOptions, StylingContext } from '../../Echart/Echart.types';
import { Series } from '../Series';
import { SeriesProps } from '../Series.types';

export type LineProps = SeriesProps;

function getDefaultLineOptions(
stylingContext: StylingContext,
): EChartSeriesOptions['line']['options'] {
return {
showSymbol: false,
symbol: 'circle',
clip: false,
symbolSize: 7,
emphasis: {
focus: 'series',
},
blur: {
lineStyle: {
opacity: 0.5,
},
},
itemStyle: {
color: stylingContext.seriesColor,
},
lineStyle: {
color: stylingContext.seriesColor,
width: 1,
},
};
}

export const Line = (props: LineProps) => (
<Series
type={'line'}
name={props.name}
data={props.data}
options={getDefaultLineOptions}
/>
);

Line.displayName = 'Line';
2 changes: 2 additions & 0 deletions charts/core/src/Series/Line/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { LineProps } from './Line';
export { Line } from './Line';
66 changes: 66 additions & 0 deletions charts/core/src/Series/Series.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useEffect } from 'react';
import { useSeriesContext } from '@lg-charts/series-provider';

import { useDarkMode } from '@leafygreen-ui/leafygreen-provider';
import { ValuesOf } from '@leafygreen-ui/lib';

import { useChartContext } from '../ChartContext';
import { EChartSeriesOptions, StylingContext } from '../Echart/Echart.types';

export function Series<T extends ValuesOf<EChartSeriesOptions>>({
type,
name,
data,
options,
}: {
type: T['type'];
name: T['name'];
data: T['data'];
options: (ctx: StylingContext) => T['options'];
}) {
const {
chart: { addSeries, ready, removeSeries },
} = useChartContext();
const { theme } = useDarkMode();
const { isChecked, getColor } = useSeriesContext();
const seriesColor = getColor(name, theme) || undefined;
const isVisible = isChecked(name);

useEffect(() => {
if (!ready) return;

if (isVisible) {
const context = { seriesColor };
addSeries({
type,
name,
data,
...options(context),
});
} else {
removeSeries(name);
}

return () => {
/**
* Remove the series when the component unmounts to make sure the series
* is removed when a `Series` is hidden.
*/
removeSeries(name);
};
}, [
addSeries,
isVisible,
seriesColor,
ready,
removeSeries,
type,
name,
data,
options,
]);

return null;
}

Series.displayName = 'Series';
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SeriesName } from '@lg-charts/series-provider';
type XValue = string | number | Date | null | undefined;
type YValue = string | number | Date | null | undefined;

export interface LineProps {
export interface SeriesProps {
/**
* Series name used for displaying in tooltip and filtering with the legend.
*/
Expand Down
2 changes: 2 additions & 0 deletions charts/core/src/Series/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Line, type LineProps } from './Line';
export { Series } from './Series';
1 change: 1 addition & 0 deletions charts/core/src/ThresholdLine/ThresholdLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ function getThresholdLineConfig({
symbolSize: [7, 10],
symbolRotate: 360,
},
data: [],
};
}

Expand Down
2 changes: 1 addition & 1 deletion charts/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export {
EventMarkerPoint,
type EventMarkerPointProps,
} from './EventMarkers';
export { Line, type LineProps } from './Line';
export { Line, type LineProps } from './Series';
export { ThresholdLine, type ThresholdLineProps } from './ThresholdLine';
export { XAxis, type XAxisProps, type XAxisType } from './XAxis';
export { YAxis, type YAxisProps, type YAxisType } from './YAxis';
2 changes: 1 addition & 1 deletion charts/core/src/testUtils/makeLineData.testUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { faker } from '@faker-js/faker';

import { LineProps } from '../Line/Line.types';
import { LineProps } from '../Series';

/**
* Generates consistent but realistic-looking test data for storybook
Expand Down
Loading