Skip to content

Commit

Permalink
Merge pull request #1578 from Shopify/fix-stacked-area-arity-bug
Browse files Browse the repository at this point in the history
Fix stacked area arity bug
  • Loading branch information
maryamkaka authored Aug 24, 2023
2 parents 861746b + db172d0 commit 1daee45
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 92 deletions.
1 change: 1 addition & 0 deletions packages/polaris-viz/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
### Fixed

- Fixed issue in `<LineChart />` where tooltip would be rendered in the wrong position when performance was impacted.
- Fixed issue in `<StackedArea />` where changing data to new data with different length would cause the chart to throw

## [9.10.4] - 2023-08-09

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {useRef} from 'react';
import {animated, useSpring} from '@react-spring/web';
import type {SpringValue} from '@react-spring/web';
import type {GradientStop} from '@shopify/polaris-viz-core';
import {
LinearGradientWithStops,
getColorVisionEventAttrs,
COLOR_VISION_SINGLE_ITEM,
getColorVisionStylesForActiveIndex,
useChartContext,
AREAS_LOAD_ANIMATION_CONFIG,
getGradientFromColor,
useSpringConfig,
AREAS_TRANSITION_CONFIG,
} from '@shopify/polaris-viz-core';

import type {AreaProps} from './types';
import styles from './Area.scss';

export function AnimatedArea({
activeLineIndex,
animationIndex,
areaGenerator,
colors,
data,
duration,
id,
index,
lineGenerator,
selectedTheme,
zeroLineValues,
}: AreaProps) {
const {shouldAnimate} = useChartContext();
const delay = animationIndex * (duration / 2);

const mounted = useRef(false);

const springConfig = useSpringConfig({
shouldAnimate,
animationDelay: delay,
mountedSpringConfig: AREAS_TRANSITION_CONFIG,
unmountedSpringConfig: AREAS_LOAD_ANIMATION_CONFIG,
});

const {
animatedAreaShape,
animatedLineShape,
opacity,
}: {
animatedAreaShape: SpringValue;
animatedLineShape: SpringValue;
opacity: SpringValue;
} = useSpring({
from: {
opacity: 0,
animatedAreaShape: areaGenerator(mounted.current ? data : zeroLineValues),
animatedLineShape: lineGenerator(mounted.current ? data : zeroLineValues),
},
to: {
opacity: 0.25,
animatedAreaShape: areaGenerator(data),
animatedLineShape: lineGenerator(data),
},
...springConfig,
});

if (animatedAreaShape == null || animatedLineShape == null) {
return null;
}

const gradient = getGradientFromColor(colors[index]);

return (
<g
{...getColorVisionEventAttrs({
type: COLOR_VISION_SINGLE_ITEM,
index,
})}
tabIndex={-1}
>
<defs>
<LinearGradientWithStops
id={`area-${id}-${index}`}
gradient={gradient as GradientStop[]}
gradientUnits="userSpaceOnUse"
y1="100%"
y2="0%"
/>
</defs>
<g
style={getColorVisionStylesForActiveIndex({
activeIndex: activeLineIndex,
index,
})}
aria-hidden="true"
tabIndex={-1}
className={styles.Group}
>
<animated.path
key={`line-${index}`}
d={animatedLineShape}
fill="none"
stroke={`url(#area-${id}-${index})`}
strokeWidth={selectedTheme.line.width}
/>
<animated.path
key={index}
style={{opacity}}
d={animatedAreaShape}
fill={`url(#area-${id}-${index})`}
/>
</g>
</g>
);
}
Original file line number Diff line number Diff line change
@@ -1,86 +1,30 @@
import {useRef} from 'react';
import {animated, useSpring} from '@react-spring/web';
import type {SpringValue} from '@react-spring/web';
import type {Area as D3Area, Line} from 'd3-shape';
import {
LinearGradientWithStops,
getColorVisionEventAttrs,
COLOR_VISION_SINGLE_ITEM,
getColorVisionStylesForActiveIndex,
useChartContext,
AREAS_LOAD_ANIMATION_CONFIG,
getGradientFromColor,
useSpringConfig,
AREAS_TRANSITION_CONFIG,
} from '@shopify/polaris-viz-core';
import type {Color, Theme, GradientStop} from '@shopify/polaris-viz-core';

import type {StackedSeries} from '../../../../types';
import type {GradientStop} from '@shopify/polaris-viz-core';

import styles from './Area.scss';

export interface AreaProps {
activeLineIndex: number;
animationIndex: number;
areaGenerator: D3Area<number[]>;
colors: Color[];
data: StackedSeries;
zeroLineValues: StackedSeries;
duration: number;
id: string;
index: number;
lineGenerator: Line<number[]>;
selectedTheme: Theme;
}
import type {AreaProps} from './types';

export function Area({
activeLineIndex,
animationIndex,
areaGenerator,
colors,
data,
duration,
id,
index,
lineGenerator,
selectedTheme,
zeroLineValues,
}: AreaProps) {
const {shouldAnimate} = useChartContext();
const delay = animationIndex * (duration / 2);

const mounted = useRef(false);

const springConfig = useSpringConfig({
shouldAnimate,
animationDelay: delay,
mountedSpringConfig: AREAS_TRANSITION_CONFIG,
unmountedSpringConfig: AREAS_LOAD_ANIMATION_CONFIG,
});

const {
animatedAreaShape,
animatedLineShape,
opacity,
}: {
animatedAreaShape: SpringValue;
animatedLineShape: SpringValue;
opacity: SpringValue;
} = useSpring({
from: {
opacity: 0,
animatedAreaShape: areaGenerator(mounted.current ? data : zeroLineValues),
animatedLineShape: lineGenerator(mounted.current ? data : zeroLineValues),
},
to: {
opacity: 0.25,
animatedAreaShape: areaGenerator(data),
animatedLineShape: lineGenerator(data),
},
...springConfig,
});
const opacity = 0.25;
const areaShape = areaGenerator(data);
const lineShape = lineGenerator(data);

if (animatedAreaShape == null || animatedLineShape == null) {
if (areaShape == null || lineShape == null) {
return null;
}

Expand Down Expand Up @@ -112,17 +56,17 @@ export function Area({
tabIndex={-1}
className={styles.Group}
>
<animated.path
<path
key={`line-${index}`}
d={animatedLineShape}
d={lineShape}
fill="none"
stroke={`url(#area-${id}-${index})`}
strokeWidth={selectedTheme.line.width}
/>
<animated.path
<path
key={index}
style={{opacity}}
d={animatedAreaShape}
d={areaShape}
fill={`url(#area-${id}-${index})`}
/>
</g>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export {Area} from './Area';
export {AnimatedArea} from './AnimatedArea';
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import {mount} from '@shopify/react-testing';
import {area, line} from 'd3-shape';
import {scaleLinear} from 'd3-scale';
import React from 'react';

import {mountWithProvider} from '../../../../../test-utilities';
import {mockDefaultTheme} from '../../../../../test-utilities/mountWithProvider';
import type {AreaProps} from '../Area';
import {Area} from '../Area';
import {DEFAULT_THEME} from '../../../../../constants';
import type {StackedSeries, Theme} from '../../../../../types';
import type {AreaProps} from '../types';
import {AnimatedArea} from '../AnimatedArea';

jest.mock('d3-scale', () => ({
scaleLinear: jest.requireActual('d3-scale').scaleLinear,
}));

describe('<Area />', () => {
describe('<AnimatedArea />', () => {
const xScale = scaleLinear();
const yScale = scaleLinear();

Expand Down Expand Up @@ -54,7 +55,7 @@ describe('<Area />', () => {
it('renders a path for each series', () => {
const stackedArea = mount(
<svg>
<Area {...mockProps} />
<AnimatedArea {...mockProps} />
</svg>,
);

Expand All @@ -65,7 +66,7 @@ describe('<Area />', () => {
const {themes} = mockDefaultTheme({line: {width: 10}});
const stackedArea = mountWithProvider(
<svg>
<Area {...mockProps} selectedTheme={themes.Default as Theme} />
<AnimatedArea {...mockProps} selectedTheme={themes.Default as Theme} />
</svg>,
);

Expand All @@ -79,7 +80,7 @@ describe('<Area />', () => {
it('renders correctly when line.hasSpline is false', () => {
const stackedArea = mountWithProvider(
<svg>
<Area {...mockProps} />
<AnimatedArea {...mockProps} />
</svg>,

mockDefaultTheme({line: {hasSpline: false}}),
Expand All @@ -98,7 +99,7 @@ describe('<Area />', () => {

const stackedArea = mount(
<svg>
<Area {...mockProps} />
<AnimatedArea {...mockProps} />
</svg>,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type {Area as D3Area, Line} from 'd3-shape';
import type {Color, Theme} from '@shopify/polaris-viz-core';

import type {StackedSeries} from '../../../../types';

export interface AreaProps {
activeLineIndex: number;
animationIndex: number;
areaGenerator: D3Area<number[]>;
colors: Color[];
data: StackedSeries;
zeroLineValues: StackedSeries;
duration: number;
id: string;
index: number;
lineGenerator: Line<number[]>;
selectedTheme: Theme;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
curveStepRounded,
uniqueId,
COLOR_VISION_SINGLE_ITEM,
usePrevious,
} from '@shopify/polaris-viz-core';

import {
Expand All @@ -16,7 +17,7 @@ import {
} from '../../../../constants';
import type {StackedSeries} from '../../../../types';
import {useTheme, useWatchColorVisionEvents} from '../../../../hooks';
import {Area} from '..';
import {AnimatedArea, Area} from '../Area';

interface Props {
colors: Color[];
Expand All @@ -36,6 +37,7 @@ export function StackedAreas({
zeroLineValues,
}: Props) {
const [activeLineIndex, setActiveLineIndex] = useState(-1);
const previousStackedValues = usePrevious(stackedValues);

useWatchColorVisionEvents({
type: COLOR_VISION_SINGLE_ITEM,
Expand Down Expand Up @@ -84,8 +86,14 @@ export function StackedAreas({
return (
<Fragment>
{stackedValues.map((data, index) => {
const dataIsValidForAnimation =
!previousStackedValues ||
data.length === previousStackedValues[index].length;

const AreaComponent = dataIsValidForAnimation ? AnimatedArea : Area;

return (
<Area
<AreaComponent
activeLineIndex={activeLineIndex}
animationIndex={stackedValues.length - 1 - index}
areaGenerator={areaGenerator}
Expand Down
Loading

0 comments on commit 1daee45

Please sign in to comment.