diff --git a/.vscode/settings.json b/.vscode/settings.json index f0525518f..b6a593f2a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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, diff --git a/packages/polaris-viz/src/components/LineChart/Chart.tsx b/packages/polaris-viz/src/components/LineChart/Chart.tsx index a4033e962..e833f5a74 100644 --- a/packages/polaris-viz/src/components/LineChart/Chart.tsx +++ b/packages/polaris-viz/src/components/LineChart/Chart.tsx @@ -337,6 +337,7 @@ export function Chart({ xScale, drawableWidth, drawableHeight, + theme, })} {reversedSeries.map((singleSeries, index) => { diff --git a/packages/polaris-viz/src/components/LineChartPredictive/LineChartPredictive.tsx b/packages/polaris-viz/src/components/LineChartPredictive/LineChartPredictive.tsx new file mode 100644 index 000000000..7bb8adadf --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/LineChartPredictive.tsx @@ -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 ( + { + return ( + + ); + }, + }} + state={state} + theme={theme} + tooltipOptions={tooltipOptions} + xAxisOptions={xAxisOptions} + yAxisOptions={yAxisOptions} + /> + ); +} diff --git a/packages/polaris-viz/src/components/LineChartPredictive/components/PredictiveLineSeries/PredictiveLineSeries.tsx b/packages/polaris-viz/src/components/LineChartPredictive/components/PredictiveLineSeries/PredictiveLineSeries.tsx new file mode 100644 index 000000000..99bdcc8d5 --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/components/PredictiveLineSeries/PredictiveLineSeries.tsx @@ -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 ( + + {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 ( + + + {isGradientType(color) ? ( + + + + ) : null} + + + ); + })} + + ); +} diff --git a/packages/polaris-viz/src/components/LineChartPredictive/components/PredictiveLineSeries/index.ts b/packages/polaris-viz/src/components/LineChartPredictive/components/PredictiveLineSeries/index.ts new file mode 100644 index 000000000..13fae2153 --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/components/PredictiveLineSeries/index.ts @@ -0,0 +1 @@ +export {PredictiveLineSeries} from './PredictiveLineSeries'; diff --git a/packages/polaris-viz/src/components/LineChartPredictive/components/index.ts b/packages/polaris-viz/src/components/LineChartPredictive/components/index.ts new file mode 100644 index 000000000..13fae2153 --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/components/index.ts @@ -0,0 +1 @@ +export {PredictiveLineSeries} from './PredictiveLineSeries'; diff --git a/packages/polaris-viz/src/components/LineChartPredictive/stories/Default.stories.tsx b/packages/polaris-viz/src/components/LineChartPredictive/stories/Default.stories.tsx new file mode 100644 index 000000000..bb81e4c9b --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/stories/Default.stories.tsx @@ -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 = Template.bind({}); + +Default.args = { + ...DEFAULT_PROPS, + data: DEFAULT_DATA, + isAnimated: false, + showLegend: true, +}; diff --git a/packages/polaris-viz/src/components/LineChartPredictive/stories/data.tsx b/packages/polaris-viz/src/components/LineChartPredictive/stories/data.tsx new file mode 100644 index 000000000..6e73af18e --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/stories/data.tsx @@ -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 = (args: LineChartProps) => { + return ; +}; + +export const DEFAULT_PROPS: Partial = { + 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, + }, + }, + }, +]; diff --git a/packages/polaris-viz/src/components/LineChartPredictive/stories/meta.tsx b/packages/polaris-viz/src/components/LineChartPredictive/stories/meta.tsx new file mode 100644 index 000000000..517d7cb87 --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/stories/meta.tsx @@ -0,0 +1,46 @@ +import type {Meta} from '@storybook/react'; + +import { + ANNOTATIONS_ARGS, + CHART_STATE_CONTROL_ARGS, + CONTROLS_ARGS, + DATA_SERIES_ARGS, + EMPTY_STATE_TEXT_ARGS, + IS_ANIMATED_ARGS, + LEGEND_CONTROL_ARGS, + RENDER_LEGEND_CONTENT_ARGS, + SKIP_LINK_ARGS, + THEME_CONTROL_ARGS, + X_AXIS_OPTIONS_ARGS, + Y_AXIS_OPTIONS_ARGS, +} from '../../../storybook/constants'; +import {PageWithSizingInfo} from '../../Docs/stories'; +import {LineChartPredictive} from '../LineChartPredictive'; + +export const META: Meta = { + title: 'polaris-viz/Charts/LineChartPredictive', + component: LineChartPredictive, + decorators: [(Story: any) =>
{Story()}
], + parameters: { + controls: CONTROLS_ARGS, + docs: { + page: PageWithSizingInfo, + description: { + component: 'Used to show change over time, comparisons, and trends.', + }, + }, + }, + argTypes: { + annotations: ANNOTATIONS_ARGS, + data: DATA_SERIES_ARGS, + xAxisOptions: X_AXIS_OPTIONS_ARGS, + emptyStateText: EMPTY_STATE_TEXT_ARGS, + isAnimated: IS_ANIMATED_ARGS, + renderLegendContent: RENDER_LEGEND_CONTENT_ARGS, + skipLinkText: SKIP_LINK_ARGS, + yAxisOptions: Y_AXIS_OPTIONS_ARGS, + theme: THEME_CONTROL_ARGS, + state: CHART_STATE_CONTROL_ARGS, + showLegend: LEGEND_CONTROL_ARGS, + }, +}; diff --git a/packages/polaris-viz/src/components/LineChartPredictive/types.ts b/packages/polaris-viz/src/components/LineChartPredictive/types.ts new file mode 100644 index 000000000..ce2fc6fbe --- /dev/null +++ b/packages/polaris-viz/src/components/LineChartPredictive/types.ts @@ -0,0 +1,17 @@ +import type {DataSeries} from '@shopify/polaris-viz-core'; + +import type {LineChartProps} from '../../components/LineChart'; + +export interface MetaData { + isPredictive?: boolean; + relatedIndex?: number; + startKey?: string; +} + +export interface LineChartPredictiveDataSeries extends DataSeries { + metadata?: MetaData; +} + +export interface LineChartPredictiveProps extends Omit { + data: LineChartPredictiveDataSeries[]; +} diff --git a/packages/polaris-viz/src/types.ts b/packages/polaris-viz/src/types.ts index 313f3724b..ce21f0efe 100644 --- a/packages/polaris-viz/src/types.ts +++ b/packages/polaris-viz/src/types.ts @@ -239,4 +239,5 @@ export interface LineChartSlotProps { drawableWidth: number; xScale: ScaleLinear; yScale: ScaleLinear; + theme: string; }