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

new(demo): respect prefersReducedMotion and leverage in happo #1037

Merged
merged 11 commits into from
Jan 27, 2021
Merged
1 change: 1 addition & 0 deletions packages/visx-demo/.happo.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
targets: {
'chrome-desktop': new RemoteBrowserTarget('chrome', {
viewport: '800x552',
prefersReducedMotion: true,
}),
},

Expand Down
44 changes: 6 additions & 38 deletions packages/visx-demo/happo/gallery.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import React from 'react';
import { tiles as examples } from '../src/components/Gallery';
import AxisTile from '../src/components/Gallery/AxisTile';
import XYChartTile from '../src/components/Gallery/XYChartTile';
import { asyncTimeout } from '../.happo-variables.js';

type HappoSnapshot = {
component: string;
Expand All @@ -13,42 +10,13 @@ type HappoSnapshot = {
};
};

const specialCases = new Set(['@visx/demo-axis', '@visx/demo-xychart']);

// renders an example with a timeout
const renderWithTimeout: (
Example: React.ReactElement,
) => HappoSnapshot['variants'][string] = Example => renderInDom => {
return new Promise(resolve => {
renderInDom(Example);
setTimeout(resolve, asyncTimeout);
});
};

const getComponentName = (Example: typeof examples[0]) =>
Example.packageJson.name || 'missing-name';

const snapshots: HappoSnapshot[] = examples
.filter(Example => !specialCases.has(getComponentName(Example)))
.map(Example => ({
// note: this (reasonably) asserts Examples have unique names
component: getComponentName(Example),
variants: { default: () => <Example.default /> },
}));
const snapshots: HappoSnapshot[] = examples.map(Example => ({
// note: this (reasonably) asserts Examples have unique names
component: getComponentName(Example),
variants: { default: () => <Example.default /> },
}));

export default snapshots.concat([
// needs timeout for animated axes
{
component: '@visx/demo-axis',
variants: {
default: renderWithTimeout(<AxisTile />),
},
},
// needs timeout for animated axes
{
component: '@visx/demo-xychart',
variants: {
default: renderWithTimeout(<XYChartTile />),
},
},
]);
export default snapshots;
122 changes: 84 additions & 38 deletions packages/visx-demo/src/sandboxes/visx-axis/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import React, { useState, useMemo } from 'react';
import AreaClosed from '@visx/shape/lib/shapes/AreaClosed';
import { curveMonotoneX } from '@visx/curve';
import { scaleUtc, scaleLinear, scaleLog, scaleBand, ScaleInput, coerceNumber } from '@visx/scale';
import { Orientation, SharedAxisProps, AxisScale } from '@visx/axis';
import { Axis, Orientation, SharedAxisProps, AxisScale } from '@visx/axis';
import { GridRows, GridColumns } from '@visx/grid';
import { AnimatedAxis, AnimatedGridRows, AnimatedGridColumns } from '@visx/react-spring';
import { getSeededRandom } from '@visx/mock-data';
import { LinearGradient } from '@visx/gradient';
import { timeFormat } from 'd3-time-format';
import { GridRowsProps } from '@visx/grid/lib/grids/GridRows';
import { GridColumnsProps } from '@visx/grid/lib/grids/GridColumns';

export const backgroundColor = '#da7cff';
const axisColor = '#fff';
Expand Down Expand Up @@ -40,23 +43,51 @@ export type AxisProps = {
showControls?: boolean;
};

type AnimationTrajectory = 'outside' | 'center' | 'min' | 'max' | undefined;

type AxisComponent = React.FC<
SharedAxisProps<AxisScale> & {
animationTrajectory: AnimationTrajectory;
}
>;
type GridRowsComponent = React.FC<
GridRowsProps<AxisScale> & {
animationTrajectory: AnimationTrajectory;
}
>;
type GridColumnsComponent = React.FC<
GridColumnsProps<AxisScale> & {
animationTrajectory: AnimationTrajectory;
}
>;

export default function Example({
width: outerWidth = 800,
height: outerHeight = 800,
showControls = true,
}: AxisProps) {
// use non-animated components if prefers-reduced-motion is set
const prefersReducedMotionQuery = window?.matchMedia('(prefers-reduced-motion: reduce)');
const prefersReducedMotion = !prefersReducedMotionQuery || !!prefersReducedMotionQuery.matches;
const [useAnimatedComponents, setUseAnimatedComponents] = useState(!prefersReducedMotion);

// in svg, margin is subtracted from total width/height
const width = outerWidth - margin.left - margin.right;
const height = outerHeight - margin.top - margin.bottom;
const [dataToggle, setDataToggle] = useState(true);
const [animationTrajectory, setAnimationTrajectory] = useState<
'outside' | 'center' | 'min' | 'max'
>('center');
const [animationTrajectory, setAnimationTrajectory] = useState<AnimationTrajectory>('center');

// define some types
interface AxisDemoProps<Scale extends AxisScale> extends SharedAxisProps<Scale> {
values: ScaleInput<Scale>[];
}

const AxisComponent: AxisComponent = useAnimatedComponents ? AnimatedAxis : Axis;
const GridRowsComponent: GridRowsComponent = useAnimatedComponents ? AnimatedGridRows : GridRows;
const GridColumnsComponent: GridColumnsComponent = useAnimatedComponents
? AnimatedGridColumns
: GridColumns;

const axes: AxisDemoProps<AxisScale<number>>[] = useMemo(() => {
// toggle between two value ranges to demo animation
const linearValues = dataToggle ? [0, 2, 4, 6, 8, 10] : [6, 8, 10, 12];
Expand Down Expand Up @@ -144,7 +175,7 @@ export default function Example({
<g transform={`translate(${margin.left},${margin.top})`}>
{axes.map(({ scale, values, label, tickFormat }, i) => (
<g key={`scale-${i}`} transform={`translate(0, ${i * (scaleHeight + scalePadding)})`}>
<AnimatedGridRows
<GridRowsComponent
// force remount when this changes to see the animation difference
key={`gridrows-${animationTrajectory}`}
scale={yScale}
Expand All @@ -153,7 +184,7 @@ export default function Example({
numTicks={dataToggle ? 1 : 3}
animationTrajectory={animationTrajectory}
/>
<AnimatedGridColumns
<GridColumnsComponent
// force remount when this changes to see the animation difference
key={`gridcolumns-${animationTrajectory}`}
scale={scale}
Expand All @@ -176,7 +207,7 @@ export default function Example({
fill={gridColor}
fillOpacity={0.2}
/>
<AnimatedAxis
<AxisComponent
// force remount when this changes to see the animation difference
key={`axis-${animationTrajectory}`}
orientation={Orientation.bottom}
Expand Down Expand Up @@ -208,42 +239,57 @@ export default function Example({
</svg>
{showControls && (
<>
<div style={{ fontSize: 11 }}>
<strong>animation trajectory</strong>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('outside')}
checked={animationTrajectory === 'outside'}
/>{' '}
outside
</label>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('center')}
checked={animationTrajectory === 'center'}
/>{' '}
center
</label>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('min')}
checked={animationTrajectory === 'min'}
/>{' '}
min
</label>
<div style={{ fontSize: 12 }}>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('max')}
checked={animationTrajectory === 'max'}
type="checkbox"
onChange={() => setUseAnimatedComponents(!useAnimatedComponents)}
checked={useAnimatedComponents}
/>{' '}
max
enable animation
</label>
&nbsp;&nbsp;&nbsp;
{useAnimatedComponents && (
<>
<strong>animation trajectory</strong>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('outside')}
checked={animationTrajectory === 'outside'}
/>{' '}
outside
</label>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('center')}
checked={animationTrajectory === 'center'}
/>{' '}
center
</label>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('min')}
checked={animationTrajectory === 'min'}
/>{' '}
min
</label>
<label>
<input
type="radio"
onChange={() => setAnimationTrajectory('max')}
checked={animationTrajectory === 'max'}
/>{' '}
max
</label>
</>
)}
</div>
<button onClick={() => setDataToggle(!dataToggle)}>Update scales</button>
{useAnimatedComponents && (
<button onClick={() => setDataToggle(!dataToggle)}>Update scales</button>
)}
</>
)}
</>
Expand Down
52 changes: 30 additions & 22 deletions packages/visx-demo/src/sandboxes/visx-pattern/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@ export type PatternProps = {
margin?: typeof defaultMargin;
};

const Patterns: React.FC<{ id: string }>[] = [
const Patterns: React.FC<{ id: string; prefersReducedMotion?: boolean }>[] = [
({ id }) => <PatternLines id={id} height={6} width={6} stroke="black" strokeWidth={1} />,
({ id }) => (
({ id, prefersReducedMotion }) => (
<CustomPattern id={id} width={10} height={10}>
<animateTransform
attributeType="xml"
attributeName="patternTransform"
type="translate"
from="0 0"
to="0 30"
dur="10s"
repeatCount="indefinite"
/>
{!prefersReducedMotion && (
<animateTransform
attributeType="xml"
attributeName="patternTransform"
type="translate"
from="0 0"
to="0 30"
dur="10s"
repeatCount="indefinite"
/>
)}
<circle cx={5} cy={5} r="3" stroke="none" fill="black" transform-origin="center" />
</CustomPattern>
),
Expand Down Expand Up @@ -78,21 +80,23 @@ const Patterns: React.FC<{ id: string }>[] = [
/>
),
({ id }) => <PatternCircles id={id} height={10} width={10} fill="black" complement />,
({ id }) => {
({ id, prefersReducedMotion }) => {
const width = 10;
const height = 10;

return (
<CustomPattern id={id} width={width} height={height}>
<animateTransform
attributeType="xml"
attributeName="patternTransform"
type="translate"
from="0 0"
to="50 0"
dur="10s"
repeatCount="indefinite"
/>
{!prefersReducedMotion && (
<animateTransform
attributeType="xml"
attributeName="patternTransform"
type="translate"
from="0 0"
to="50 0"
dur="10s"
repeatCount="indefinite"
/>
)}
<path
d={`M 0 ${height / 2} c ${height / 8} ${-height / 4} , ${(height * 3) / 8} ${-height /
4} , ${height / 2} 0
Expand All @@ -115,6 +119,10 @@ const Patterns: React.FC<{ id: string }>[] = [
];

export default function Example({ width, height, margin = defaultMargin }: PatternProps) {
// use non-animated components if prefers-reduced-motion is set
const prefersReducedMotionQuery = window?.matchMedia('(prefers-reduced-motion: reduce)');
const prefersReducedMotion = !prefersReducedMotionQuery || !!prefersReducedMotionQuery.matches;

const numColumns = 3;
const numRows = Patterns.length / numColumns;
const columnWidth = Math.max((width - margin.left - margin.right) / numColumns, 0);
Expand All @@ -132,7 +140,7 @@ export default function Example({ width, height, margin = defaultMargin }: Patte
return (
<React.Fragment key={id}>
{/** Like SVG <defs />, Patterns are rendered with an id */}
<Pattern id={id} />
<Pattern id={id} prefersReducedMotion={prefersReducedMotion} />

{/** And are then referenced for a style attribute. */}
<Bar
Expand Down
Loading