Skip to content

Commit

Permalink
Add LineSeriesPredictive
Browse files Browse the repository at this point in the history
  • Loading branch information
envex committed Jan 5, 2024
1 parent ea2a6da commit 8659ae6
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"css.validate": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
Expand Down
1 change: 1 addition & 0 deletions packages/polaris-viz/src/components/LineChart/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ export function Chart({
xScale,
drawableWidth,
drawableHeight,
theme,
})}

{reversedSeries.map((singleSeries, index) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
DEFAULT_CHART_PROPS,
useTheme,
useThemeSeriesColors,
} from '@shopify/polaris-viz-core';

import {LineChart} from '../LineChart';

import type {LineChartPredictiveProps} from './types';
import {PredictiveLineSeries} from './components';

export function LineChartPredictive(props: LineChartPredictiveProps) {
const {
annotations = [],
data,
errorText,
emptyStateText,
id,
isAnimated,
renderLegendContent,
showLegend = true,
skipLinkText,
state,
theme,
tooltipOptions,
xAxisOptions,
yAxisOptions,
} = {
...DEFAULT_CHART_PROPS,
...props,
};

const nonPredictiveData: LineChartPredictiveProps['data'] = [];
const predictiveData: LineChartPredictiveProps['data'] = [];

for (const series of data) {
if (series.metadata?.isPredictive === true) {
predictiveData.push(series);
} else {
nonPredictiveData.push(series);
}
}

const selectedTheme = useTheme(theme);
const seriesColors = useThemeSeriesColors(nonPredictiveData, selectedTheme);

return (
<LineChart
annotations={annotations}
data={nonPredictiveData}
emptyStateText={emptyStateText}
errorText={errorText}
id={id}
isAnimated={isAnimated}
renderLegendContent={renderLegendContent}
showLegend={showLegend}
skipLinkText={skipLinkText}
slots={{
chart: ({xScale, yScale, drawableHeight, drawableWidth, theme}) => {
return (
<PredictiveLineSeries
data={predictiveData}
drawableHeight={drawableHeight}
drawableWidth={drawableWidth}
seriesColors={seriesColors}
theme={theme}
xScale={xScale}
yScale={yScale}
/>
);
},
}}
state={state}
theme={theme}
tooltipOptions={tooltipOptions}
xAxisOptions={xAxisOptions}
yAxisOptions={yAxisOptions}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type {Color} from '@shopify/polaris-viz-core';
import {
COLOR_VISION_SINGLE_ITEM,
LineSeries,
LinearGradientWithStops,
changeColorOpacity,
changeGradientOpacity,
isGradientType,
uniqueId,
} from '@shopify/polaris-viz-core';
import {Fragment, useMemo, useState} from 'react';
import type {LineChartSlotProps} from 'types';

import {Point} from '../../../../components/Point';
import {useWatchColorVisionEvents} from '../../../../hooks';
import {getLineChartDataWithDefaults} from '../../../../utilities/getLineChartDataWithDefaults';
import type {LineChartPredictiveProps} from '../../types';

interface PredictiveLinesProps extends LineChartSlotProps {
data: LineChartPredictiveProps['data'];
seriesColors: Color[];
theme: string;
}

export function PredictiveLineSeries({
data,
drawableHeight,
drawableWidth,
seriesColors,
theme,
xScale,
yScale,
}: PredictiveLinesProps) {
const [activeLineIndex, setActiveLineIndex] = useState(-1);
const id = useMemo(() => uniqueId('PredictiveLines'), []);

useWatchColorVisionEvents({
type: COLOR_VISION_SINGLE_ITEM,
onIndexChange: ({detail}) => setActiveLineIndex(detail.index),
});

const dataWithDefaults = getLineChartDataWithDefaults(data, seriesColors);

return (
<Fragment>
{dataWithDefaults.map((series, index) => {
const pointGradientId = `${id}-point-${index}`;

const predictiveStartIndex = series.data.findIndex(
({key}) => key === series.metadata?.startKey,
);

const color = series.color;

const pointColor = isGradientType(color)
? `url(#${pointGradientId})`
: changeColorOpacity(color);

return (
<Fragment key={`${series.name}-${index}`}>
<LineSeries
activeLineIndex={activeLineIndex}
data={series}
index={index}
svgDimensions={{
height: drawableHeight,
width: drawableWidth,
}}
xScale={xScale}
yScale={yScale}
theme={theme}
/>
{isGradientType(color) ? (
<defs>
<LinearGradientWithStops
id={pointGradientId}
gradient={changeGradientOpacity(color)}
gradientUnits="userSpaceOnUse"
y1="100%"
y2="0%"
/>
</defs>
) : null}
<Point
color={pointColor}
cx={xScale(predictiveStartIndex)}
cy={yScale(series.data[predictiveStartIndex]?.value ?? -1)}
active
index={index}
isAnimated={false}
ariaHidden
/>
</Fragment>
);
})}
</Fragment>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {PredictiveLineSeries} from './PredictiveLineSeries';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {PredictiveLineSeries} from './PredictiveLineSeries';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type {Story} from '@storybook/react';

export {META as default} from './meta';

import {DEFAULT_DATA, DEFAULT_PROPS, Template} from './data';
import type {LineChartProps} from 'components/LineChart/LineChart';

export const Default: Story<LineChartProps> = Template.bind({});

Default.args = {
...DEFAULT_PROPS,
data: DEFAULT_DATA,
isAnimated: false,
showLegend: true,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import type {Story} from '@storybook/react';
import type {LineChartProps} from 'components/LineChart/LineChart';

import {LineChartPredictive} from '../LineChartPredictive';
import {
formatLinearXAxisLabel,
formatLinearYAxisLabel,
} from '../../../storybook/utilities';
import type {LineChartPredictiveDataSeries} from '../types';

export const Template: Story<LineChartProps> = (args: LineChartProps) => {
return <LineChartPredictive {...args} />;
};

export const DEFAULT_PROPS: Partial<LineChartProps> = {
xAxisOptions: {
labelFormatter: formatLinearXAxisLabel,
},
yAxisOptions: {labelFormatter: formatLinearYAxisLabel},
showLegend: false,
};

export const DEFAULT_DATA: LineChartPredictiveDataSeries[] = [
{
name: 'One',
data: [
{value: 88, key: '2020-03-01T12:00:00'},
{value: 559, key: '2020-03-02T12:00:00'},
{value: 40, key: '2020-03-03T12:00:00'},
{value: 0, key: '2020-03-04T12:00:00'},
{value: 87, key: '2020-03-05T12:00:00'},
{value: 22, key: '2020-03-06T12:00:00'},
{value: null, key: '2020-03-07T12:00:00'},
{value: null, key: '2020-03-08T12:00:00'},
{value: null, key: '2020-03-09T12:00:00'},
{value: null, key: '2020-03-10T12:00:00'},
{value: null, key: '2020-03-11T12:00:00'},
{value: null, key: '2020-03-12T12:00:00'},
],
styleOverride: {
line: {
hasArea: false,
},
},
},
{
name: 'Predictive',
data: [
{value: null, key: '2020-03-01T12:00:00'},
{value: null, key: '2020-03-02T12:00:00'},
{value: null, key: '2020-03-03T12:00:00'},
{value: null, key: '2020-03-04T12:00:00'},
{value: null, key: '2020-03-05T12:00:00'},
{value: 22, key: '2020-03-06T12:00:00'},
{value: 430, key: '2020-03-07T12:00:00'},
{value: 0, key: '2020-03-08T12:00:00'},
{value: 240, key: '2020-03-09T12:00:00'},
{value: 0, key: '2020-03-10T12:00:00'},
{value: 540, key: '2020-03-11T12:00:00'},
{value: 641, key: '2020-03-12T12:00:00'},
],
metadata: {
relatedIndex: 0,
isPredictive: true,
startKey: '2020-03-06T12:00:00',
},
styleOverride: {
line: {
strokeDasharray: '1 10 1',
hasArea: false,
},
},
},
{
name: 'Two',
data: [
{value: 23, key: '2020-03-01T12:00:00'},
{value: 12, key: '2020-03-02T12:00:00'},
{value: 234, key: '2020-03-03T12:00:00'},
{value: 29, key: '2020-03-04T12:00:00'},
{value: null, key: '2020-03-05T12:00:00'},
{value: null, key: '2020-03-06T12:00:00'},
{value: null, key: '2020-03-07T12:00:00'},
{value: null, key: '2020-03-08T12:00:00'},
{value: null, key: '2020-03-09T12:00:00'},
{value: null, key: '2020-03-10T12:00:00'},
{value: null, key: '2020-03-11T12:00:00'},
{value: null, key: '2020-03-12T12:00:00'},
],
styleOverride: {
line: {
hasArea: false,
},
},
},
{
name: 'Two Predictive',
data: [
{value: null, key: '2020-03-01T12:00:00'},
{value: null, key: '2020-03-02T12:00:00'},
{value: null, key: '2020-03-03T12:00:00'},
{value: 29, key: '2020-03-04T12:00:00'},
{value: 23, key: '2020-03-05T12:00:00'},
{value: 57, key: '2020-03-06T12:00:00'},
{value: 43, key: '2020-03-07T12:00:00'},
{value: 12, key: '2020-03-08T12:00:00'},
{value: 23, key: '2020-03-09T12:00:00'},
{value: 152, key: '2020-03-10T12:00:00'},
{value: 300, key: '2020-03-11T12:00:00'},
{value: 500, key: '2020-03-12T12:00:00'},
],
metadata: {
relatedIndex: 2,
isPredictive: true,
startKey: '2020-03-04T12:00:00',
},
styleOverride: {
line: {
strokeDasharray: '1 10 1',
hasArea: false,
},
},
},
{
name: 'Comparison',
data: [
{value: 458, key: '2020-03-01T12:00:00'},
{value: 311, key: '2020-03-02T12:00:00'},
{value: 245, key: '2020-03-03T12:00:00'},
{value: 74, key: '2020-03-04T12:00:00'},
{value: 228, key: '2020-03-05T12:00:00'},
{value: 497, key: '2020-03-06T12:00:00'},
{value: 46, key: '2020-03-07T12:00:00'},
{value: 165, key: '2020-03-08T12:00:00'},
{value: 200, key: '2020-03-09T12:00:00'},
{value: 483, key: '2020-03-10T12:00:00'},
{value: 255, key: '2020-03-11T12:00:00'},
{value: 395, key: '2020-03-12T12:00:00'},
],
isComparison: true,
styleOverride: {
line: {
hasArea: false,
},
},
},
];
Loading

0 comments on commit 8659ae6

Please sign in to comment.