import React, { forwardRef } from 'react';
import posed from '../posed';
import PoseGroup from '../components/Transition/PoseGroup';
import { render, cleanup } from 'react-testing-library';
afterEach(cleanup);
const Parent = posed.div({
init: { x: 10, transition: { duration: 30 } },
foo: { x: 20, transition: { duration: 30 } },
bar: { x: 30, transition: { duration: 30 } },
fromProps: { x: ({ i }) => i, transition: { duration: 30 } },
preEnter: { x: 40, transition: { duration: 30 } },
enter: { x: 50, transition: { duration: 30 } },
exit: { x: 60, transition: { duration: 30 } },
dynamicEnter: { x: ({ x }) => x * 2, transition: { duration: 30 } },
dynamicExit: {
x: ({ x }) => x,
transition: { duration: 30 }
}
});
const Child = posed.div({
init: { y: 15, transition: { duration: 30 } },
foo: { y: 25, transition: { duration: 30 } },
bar: { y: 35, transition: { duration: 30 } },
fromProps: { y: ({ i }) => i, transition: { duration: 30 } },
preEnter: { y: 45, transition: { duration: 30 } },
enter: { y: 55, transition: { duration: 30 } },
exit: { y: 65, transition: { duration: 30 } },
dynamicEnter: { y: 75, transition: { duration: 30 } },
dynamicExit: { y: 85, transition: { duration: 30 } },
dynamicExitDuration: {
y: 85,
transition: ({ i }) => ({ duration: (i + 1) * 30 })
}
});
test('posed: initial state - named initial pose', () => {
let x = 0;
let y = 0;
render(
(x = v) }}>
(y = v) }} />
);
expect(x).toBe(20);
expect(y).toBe(25);
});
test('posed: initial state - auto `init` pose', () => {
let x = 0;
let y = 0;
render(
(x = v) }}>
(y = v) }} />
);
expect(x).toBe(10);
expect(y).toBe(15);
});
test('posed: nested poses get merged together', () => {
let parentX = 0;
let childX = 0;
let childY = 0;
const Parent = posed.div({
foo: { x: 20, transition: { duration: 30 } }
});
const Child = posed.div({
foo: { x: 10, transition: { duration: 30 } },
bar: { y: 30, transition: { duration: 30 } }
});
render(
(parentX = v) }}>
(childX = v), y: v => (childY = v) }}
/>
);
expect(parentX).toBe(20);
expect(childX).toBe(10);
expect(childY).toBe(30);
});
test('posed: mount animation with `initialPose`', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
render(
{
expect(x).toBe(20);
expect(y).toBe(25);
resolve();
}}
onValueChange={{ x: v => (x = v) }}
>
(y = v) }} />
);
expect(x).toBe(30);
expect(y).toBe(35);
});
});
test('posed: passes through props', () => {
let x = 0;
let y = 0;
render(
(x = v) }}>
(y = v) }} />
);
expect(x).toBe(1);
expect(y).toBe(2);
});
test('posed: `onPoseComplete` gets called with pose as argument', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
render(
{
expect(pose).toBe('foo');
resolve();
}}
/>
);
});
});
test('PoseGroup: Initial visibility (visible)', () => {
let x = 0;
let y = 0;
const Group = ({ isVisible = true }) => (
{isVisible && (
(x = v) }}>
(y = v) }} />
)}
);
render();
expect(x).toBe(50);
expect(y).toBe(55);
});
test('PoseGroup: Initial visibility (hidden)', () => {
let x = 0;
let y = 0;
const Group = ({ isVisible = false }) => (
{isVisible && (
(x = v) }}>
(y = v) }} />
)}
);
render();
expect(x).toBe(0);
expect(y).toBe(0);
});
test('PoseGroup: Animate on mount', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
const Group = ({ isVisible = true }) => (
{isVisible && (
{
expect(x).toBe(50);
expect(y).toBe(55);
resolve();
}}
onValueChange={{ x: v => (x = v) }}
>
(y = v) }} />
)}
);
render();
expect(x).toBe(60);
expect(y).toBe(65);
});
});
test('PoseGroup: onRest fires', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
const Group = ({ isVisible = true }) => (
{
expect(x).toBe(60);
expect(y).toBe(65);
resolve();
}}
>
{isVisible && (
(x = v) }}>
(y = v) }} />
)}
);
const { rerender } = render();
rerender();
expect(x).toBe(50);
expect(y).toBe(55);
});
});
test('PoseGroup: onRest fires when exit pose starts during exit pose', () => {
const range = n => Array.from({ length: n }, (_, i) => i);
return new Promise(resolve => {
class Group extends React.Component {
state = {
list: range(6)
};
componentDidMount() {
this.pop2();
}
pop2 = () => {
const { list } = this.state;
if (!list.length) {
return;
}
this.setState({ list: list.slice(0, -2) });
};
render() {
return (
{this.state.list.map(i => (
))}
);
}
}
const wrapper = render();
});
});
test('PoseGroup: Animate conditionally', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
const Group = ({ isVisible = false }) => (
{isVisible && (
{
expect(x).toBe(50);
expect(y).toBe(55);
resolve();
}}
onValueChange={{ x: v => (x = v) }}
>
(y = v) }} />
)}
);
const { rerender } = render();
expect(x).toBe(0);
expect(y).toBe(0);
rerender();
expect(x).toBe(60);
expect(y).toBe(65);
});
});
test('PoseGroup: All remaining children have a flip transition when one is removed', () => {
const items = [1, 2, 3, 4, 5, 6];
let nbEnterPoseCompleted = 0;
let flipTransitions = [];
return new Promise(resolve => {
const Group = ({ items }) => (
{items.map((item, i) => (
{
if (poses.includes('enter')) {
flipTransitions[i] = poses.includes('flip');
nbEnterPoseCompleted += 1;
if (nbEnterPoseCompleted === items.length) {
expect(flipTransitions).toEqual([
true,
true,
true,
true,
true
]);
resolve();
}
}
}}
/>
))}
);
const { rerender } = render();
expect(flipTransitions).toEqual([]);
rerender();
});
});
test('PoseGroup: Forward props from PoseGroup to direct child', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
const Group = ({ isVisible = false }) => (
{isVisible && (
{
expect(x).toBe(200);
expect(y).toBe(75);
resolve();
}}
onValueChange={{ x: v => (x = v) }}
>
(y = v) }} />
)}
);
const { rerender } = render();
expect(x).toBe(0);
expect(y).toBe(0);
rerender();
expect(x).toBe(100);
expect(y).toBe(85);
});
});
test('PoseGroup: Override props on child', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
const Group = ({ isVisible = false }) => (
{isVisible && (
{
if (pose === 'dynamicExit') {
expect(x).toBe(333);
expect(y).toBe(85);
resolve();
} else {
expect(x).toBe(202);
expect(y).toBe(75);
rerender();
}
}}
onValueChange={{ x: v => (x = v) }}
>
(y = v) }} />
)}
);
const { rerender } = render();
expect(x).toBe(0);
expect(y).toBe(0);
rerender();
expect(x).toBe(101);
expect(y).toBe(85);
});
});
test('PoseGroup: Provides group props to children on mount', () => {
let x = 0;
let y = 0;
const ChildWithGroupProps = posed(
forwardRef(({ text, ...props }, innerRef) => (
{`text: ${text}`}
))
)({});
const Group = () => (
);
const { getByTestId, debug } = render();
const child = getByTestId('child');
expect(child.textContent).toBe('text: foo bar');
});
test('PoseGroup: `onPoseComplete` gets called for leaving child', () => {
return new Promise(resolve => {
const Group = ({ isVisible = true }) => (
{isVisible && (
{
if (pose === 'enter') {
rerender();
return;
}
expect(pose).toBe('exit');
resolve();
}}
>
)}
);
const { rerender } = render();
});
});
test('PoseGroup: special pre-enter pose', () => {
let x = 0;
let y = 0;
return new Promise(resolve => {
const Group = ({ isVisible = false }) => (
{isVisible && (
{
expect(x).toBe(50);
expect(y).toBe(55);
resolve();
}}
onValueChange={{ x: v => (x = v) }}
>
(y = v) }} />
)}
);
const { rerender } = render();
expect(x).toBe(0);
expect(y).toBe(0);
rerender();
expect(x).toBe(20);
expect(y).toBe(25);
});
});
test('StrictMode: PoseGroup removes children correctly', () => {
return new Promise(resolve => {
const First = posed.div({
enter: { opacity: 1 },
exit: { opacity: 0 }
});
const Second = posed.div({
enter: { opacity: 1 },
exit: { opacity: 0 }
});
const Group = props => (
);
const { rerender } = render(
{
expect(pose).toBe('exit');
resolve();
}}
/>
);
rerender(
);
});
});