diff --git a/packages/animation/src/Energy/Energy.animation.test.js b/packages/animation/src/Energy/Energy.animation.test.js
index 9d3f0976..d9955d68 100644
--- a/packages/animation/src/Energy/Energy.animation.test.js
+++ b/packages/animation/src/Energy/Energy.animation.test.js
@@ -32,34 +32,34 @@ test('Should delay energy flow from "exited" the "duration.delay" time', () => {
expect(energy.getFlow().entering).toBe(true);
});
-test('Should get notified with "onActivate" when activation changes', () => {
+test('Should get notified with "onActivation" when activation changes', () => {
let example;
- const onActivate = jest.fn();
+ const onActivation = jest.fn();
class Example extends React.PureComponent {
state = { activate: true }
render () {
const { activate } = this.state;
- return ;
+ return ;
}
}
render( (example = r)} />);
setTimeout(() => example.setState({ activate: false }), 200);
- expect(onActivate).not.toHaveBeenCalled();
+ expect(onActivation).not.toHaveBeenCalled();
jest.advanceTimersByTime(10);
- expect(onActivate).toHaveBeenCalledTimes(1);
- expect(onActivate).toHaveBeenCalledWith(true);
+ expect(onActivation).toHaveBeenCalledTimes(1);
+ expect(onActivation).toHaveBeenCalledWith(true);
jest.advanceTimersByTime(180); // 190ms
- expect(onActivate).toHaveBeenCalledTimes(1);
+ expect(onActivation).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(20); // 210ms
- expect(onActivate).toHaveBeenCalledTimes(2);
- expect(onActivate).toHaveBeenCalledWith(false);
+ expect(onActivation).toHaveBeenCalledTimes(2);
+ expect(onActivation).toHaveBeenCalledWith(false);
jest.advanceTimersByTime(100); // 310ms
- expect(onActivate).toHaveBeenCalledTimes(2);
+ expect(onActivation).toHaveBeenCalledTimes(2);
});
describe('root', () => {
diff --git a/packages/animation/src/Energy/Energy.js b/packages/animation/src/Energy/Energy.js
index f824421e..db7d4971 100644
--- a/packages/animation/src/Energy/Energy.js
+++ b/packages/animation/src/Energy/Energy.js
@@ -1,3 +1,5 @@
+/* eslint-disable react/no-unused-prop-types */
+
import React from 'react';
import PropTypes from 'prop-types';
import { useAnimation } from '../useAnimation';
@@ -6,18 +8,14 @@ import { useEnergy } from '../useEnergy';
import { makeIsAnimate } from '../makeIsAnimate';
import { makeIsRoot } from '../makeIsRoot';
import { makeIsActivated } from '../makeIsActivated';
+import { makeIsOutsourced } from '../makeIsOutsourced';
import { makeGetEnergyInterface } from '../makeGetEnergyInterface';
import { makeDurationManager } from '../makeDurationManager';
import { makeFlowManager } from '../makeFlowManager';
import { makeScheduler } from '../makeScheduler';
-
-const ENTERING = 'entering';
-const ENTERED = 'entered';
-const EXITING = 'exiting';
-const EXITED = 'exited';
+import { ENERGY_TYPE, ENTERING, ENTERED, EXITING, EXITED } from '../constants';
class Component extends React.PureComponent {
- /* eslint-disable react/no-unused-prop-types */
static propTypes = {
animate: PropTypes.bool,
root: PropTypes.bool,
@@ -31,12 +29,12 @@ class Component extends React.PureComponent {
})
]),
merge: PropTypes.bool,
- onActivate: PropTypes.func,
+ imperative: PropTypes.bool,
+ onActivation: PropTypes.func,
animationContext: PropTypes.any,
parentEnergyContext: PropTypes.any,
children: PropTypes.any
};
- /* eslint-enable react/no-unused-prop-types */
constructor () {
super(...arguments);
@@ -44,11 +42,13 @@ class Component extends React.PureComponent {
this.isAnimate = makeIsAnimate(this);
this.isRoot = makeIsRoot(this);
this.isActivated = makeIsActivated(this);
+ this.isOutsourced = makeIsOutsourced(this);
this.getEnergyInterface = makeGetEnergyInterface(this);
this.durationManager = makeDurationManager(this);
this.flowManager = makeFlowManager(this);
this.scheduler = makeScheduler();
+ this.type = ENERGY_TYPE;
this.state = this.getInitialState();
this._flowHasEntered = false;
this._flowHasExited = false;
@@ -70,7 +70,8 @@ class Component extends React.PureComponent {
}
componentWillUnmount () {
- this.scheduler.stop();
+ this.flowManager.checkUnmount();
+ this.scheduler.stopAll();
}
render () {
@@ -101,32 +102,41 @@ class Component extends React.PureComponent {
return this.state.energyInterface.flow;
}
- getDuration = () => {
+ getDuration () {
return this.durationManager.get();
}
- getDurationIn = () => {
+ getDurationIn () {
const duration = this.durationManager.get();
return duration.enter + duration.delay;
}
- getDurationOut = () => {
+ getDurationOut () {
const duration = this.durationManager.get();
return duration.exit;
}
- updateDuration = duration => {
+ updateDuration (duration) {
this.durationManager.update(duration);
}
- hasEntered = () => {
+ hasEntered () {
return this._flowHasEntered;
}
- hasExited = () => {
+ hasExited () {
return this._flowHasExited;
}
+ updateActivation (activated) {
+ if (this.isOutsourced()) {
+ activated ? this.enter() : this.exit();
+ }
+ else {
+ throw new Error('"updateActivation" can not be called if not outsourced.');
+ }
+ }
+
enter () {
const flowValue = this.state.flowValue;
@@ -137,11 +147,15 @@ class Component extends React.PureComponent {
const duration = this.getDuration();
const delay = flowValue === EXITED ? duration.delay : 0;
- this.scheduler.start(delay, () => {
+ this.scheduler.start(0, delay, () => {
const duration = this.getDuration();
+ if (this.props.onActivation) {
+ this.props.onActivation(true);
+ }
+
this.setFlowValue(ENTERING);
- this.scheduler.start(duration.enter, () => this.setFlowValue(ENTERED));
+ this.scheduler.start(0, duration.enter, () => this.setFlowValue(ENTERED));
});
}
@@ -152,11 +166,15 @@ class Component extends React.PureComponent {
return;
}
- this.scheduler.start(0, () => {
+ this.scheduler.start(0, 0, () => {
const duration = this.getDuration();
+ if (this.props.onActivation) {
+ this.props.onActivation(false);
+ }
+
this.setFlowValue(EXITING);
- this.scheduler.start(duration.exit, () => this.setFlowValue(EXITED));
+ this.scheduler.start(0, duration.exit, () => this.setFlowValue(EXITED));
});
}
}
diff --git a/packages/animation/src/Energy/Energy.test.js b/packages/animation/src/Energy/Energy.test.js
index 8e53f13a..83c1d654 100644
--- a/packages/animation/src/Energy/Energy.test.js
+++ b/packages/animation/src/Energy/Energy.test.js
@@ -33,6 +33,21 @@ test('Should provide energy interface API as immutable', () => {
render();
});
+describe('getFlow()', () => {
+ test('Should return current flow object', () => {
+ let energy;
+ render( (energy = r)} />);
+ expect(energy.getFlow()).toEqual({ value: 'exited', exited: true });
+ });
+
+ test('Should return a frozen flow object', () => {
+ let energy;
+ render( (energy = r)} animate={false} />);
+ const flow = energy.getFlow();
+ expect(() => (flow.value = 'xxx')).toThrow();
+ });
+});
+
describe('getDuration()', () => {
test('Should get duration', () => {
let energy;
@@ -71,18 +86,3 @@ describe('updateDuration()', () => {
expect(energy.getDuration()).toMatchObject({ enter: 70, exit: 70 });
});
});
-
-describe('getFlow()', () => {
- test('Should return current flow object', () => {
- let energy;
- render( (energy = r)} />);
- expect(energy.getFlow()).toEqual({ value: 'exited', exited: true });
- });
-
- test('Should return a frozen flow object', () => {
- let energy;
- render( (energy = r)} animate={false} />);
- const flow = energy.getFlow();
- expect(() => (flow.value = 'xxx')).toThrow();
- });
-});
diff --git a/packages/animation/src/constants.js b/packages/animation/src/constants.js
new file mode 100644
index 00000000..0a184dbe
--- /dev/null
+++ b/packages/animation/src/constants.js
@@ -0,0 +1,6 @@
+export const ENERGY_TYPE = 'energy';
+export const STREAM_TYPE = 'stream';
+export const ENTERING = 'entering';
+export const ENTERED = 'entered';
+export const EXITING = 'exiting';
+export const EXITED = 'exited';
diff --git a/packages/animation/src/makeDurationManager/makeDurationManager.js b/packages/animation/src/makeDurationManager/makeDurationManager.js
index 812752be..44d534ff 100644
--- a/packages/animation/src/makeDurationManager/makeDurationManager.js
+++ b/packages/animation/src/makeDurationManager/makeDurationManager.js
@@ -2,7 +2,7 @@ function makeDurationManager (component) {
let customDuration;
function get () {
- const defaultDuration = { enter: 200, exit: 200, stagger: 0, delay: 0 };
+ const defaultDuration = { enter: 200, exit: 200, stagger: 50, delay: 0 };
const providedDuration = component.props.animationContext.duration;
diff --git a/packages/animation/src/makeDurationManager/makeDurationManager.test.js b/packages/animation/src/makeDurationManager/makeDurationManager.test.js
index 4d71de6c..23b946da 100644
--- a/packages/animation/src/makeDurationManager/makeDurationManager.test.js
+++ b/packages/animation/src/makeDurationManager/makeDurationManager.test.js
@@ -39,13 +39,13 @@ describe('update()', () => {
const component = { props: { animationContext: {} } };
const durationManager = makeDurationManager(component);
durationManager.update({ enter: 900 });
- expect(durationManager.get()).toMatchObject({ enter: 900, exit: 200, delay: 0, stagger: 0 });
+ expect(durationManager.get()).toMatchObject({ enter: 900, exit: 200, delay: 0, stagger: 50 });
});
test('Should update duration with number', () => {
const component = { props: { animationContext: {} } };
const durationManager = makeDurationManager(component);
durationManager.update(700);
- expect(durationManager.get()).toMatchObject({ enter: 700, exit: 700, delay: 0, stagger: 0 });
+ expect(durationManager.get()).toMatchObject({ enter: 700, exit: 700, delay: 0, stagger: 50 });
});
});
diff --git a/packages/animation/src/makeFlowManager/makeFlowManager.js b/packages/animation/src/makeFlowManager/makeFlowManager.js
index 5124a3e4..04080e81 100644
--- a/packages/animation/src/makeFlowManager/makeFlowManager.js
+++ b/packages/animation/src/makeFlowManager/makeFlowManager.js
@@ -1,26 +1,35 @@
+import { STREAM_TYPE } from '../constants';
+
function makeFlowManager (component) {
let isFlowActivated = false;
function checkMount () {
- const animate = component.isAnimate();
- const activated = component.isActivated();
+ const { parentEnergyContext } = component.props;
+
+ // TODO: Add tests.
+ if (parentEnergyContext && parentEnergyContext.type === STREAM_TYPE) {
+ parentEnergyContext._subscribe(component);
+ }
- if (animate && activated) {
+ if (!component.isAnimate() || component.isOutsourced()) {
+ return;
+ }
+
+ if (component.isActivated()) {
component.enter();
}
}
function checkUpdate () {
- const animate = component.isAnimate();
+ if (!component.isAnimate() || component.isOutsourced()) {
+ return;
+ }
+
const activated = component.isActivated();
- if (animate && activated !== isFlowActivated) {
+ if (activated !== isFlowActivated) {
isFlowActivated = activated;
- if (component.props.onActivate) {
- component.props.onActivate(activated);
- }
-
if (activated) {
component.enter();
}
@@ -30,7 +39,16 @@ function makeFlowManager (component) {
}
}
- return { checkMount, checkUpdate };
+ // TODO: Add tests.
+ function checkUnmount () {
+ const { parentEnergyContext } = component.props;
+
+ if (parentEnergyContext && parentEnergyContext.type === STREAM_TYPE) {
+ parentEnergyContext._unsubscribe(component);
+ }
+ }
+
+ return { checkMount, checkUpdate, checkUnmount };
}
export { makeFlowManager };
diff --git a/packages/animation/src/makeFlowManager/makeFlowManager.test.js b/packages/animation/src/makeFlowManager/makeFlowManager.test.js
index dec97abf..f5b7debb 100644
--- a/packages/animation/src/makeFlowManager/makeFlowManager.test.js
+++ b/packages/animation/src/makeFlowManager/makeFlowManager.test.js
@@ -3,11 +3,24 @@
import { makeFlowManager } from './makeFlowManager';
describe('checkMount()', () => {
- test('Should do nothing if not animated not activated', () => {
- const isAnimate = jest.fn();
- const isActivated = jest.fn();
+ test('Should do nothing if not animated', () => {
+ const isAnimate = jest.fn(() => false);
+ const isActivated = jest.fn(() => true);
+ const isOutsourced = jest.fn(() => false);
+ const enter = jest.fn();
+ const component = { props: {}, isAnimate, isActivated, isOutsourced, enter };
+ const flowManager = makeFlowManager(component);
+
+ flowManager.checkMount();
+ expect(enter).not.toHaveBeenCalled();
+ });
+
+ test('Should do nothing if outsourced', () => {
+ const isAnimate = jest.fn(() => true);
+ const isActivated = jest.fn(() => true);
+ const isOutsourced = jest.fn(() => true);
const enter = jest.fn();
- const component = { isAnimate, isActivated, enter };
+ const component = { props: {}, isAnimate, isActivated, isOutsourced, enter };
const flowManager = makeFlowManager(component);
flowManager.checkMount();
@@ -17,8 +30,9 @@ describe('checkMount()', () => {
test('Should enter() if animated and activated', () => {
const isAnimate = jest.fn(() => true);
const isActivated = jest.fn(() => true);
+ const isOutsourced = jest.fn(() => false);
const enter = jest.fn();
- const component = { isAnimate, isActivated, enter };
+ const component = { props: {}, isAnimate, isActivated, isOutsourced, enter };
const flowManager = makeFlowManager(component);
flowManager.checkMount();
@@ -34,6 +48,7 @@ describe('checkUpdate()', () => {
props: {},
isAnimate: jest.fn(() => true),
isActivated: jest.fn(() => true),
+ isOutsourced: jest.fn(() => false),
enter,
exit
};
@@ -51,6 +66,7 @@ describe('checkUpdate()', () => {
props: {},
isAnimate: jest.fn(() => true),
isActivated: jest.fn(() => true),
+ isOutsourced: jest.fn(() => false),
enter,
exit
};
@@ -63,24 +79,21 @@ describe('checkUpdate()', () => {
expect(exit).toHaveBeenCalledTimes(1);
});
- test('Should call onActivate with value when activation changes', () => {
- const onActivate = jest.fn();
+ test('Should do nothing if outsourced', () => {
const enter = jest.fn();
const exit = jest.fn();
const component = {
- props: { onActivate },
+ props: { imperative: true },
isAnimate: jest.fn(() => true),
isActivated: jest.fn(() => true),
+ isOutsourced: jest.fn(() => true),
enter,
exit
};
const flowManager = makeFlowManager(component);
flowManager.checkUpdate();
- expect(onActivate).toHaveBeenCalledWith(true);
-
- component.isActivated = jest.fn(() => false);
- flowManager.checkUpdate();
- expect(onActivate).toHaveBeenCalledWith(false);
+ expect(enter).not.toHaveBeenCalled();
+ expect(exit).not.toHaveBeenCalled();
});
});
diff --git a/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.js b/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.js
index 5c0b4682..30d6f718 100644
--- a/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.js
+++ b/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.js
@@ -1,26 +1,29 @@
+const METHODS = [
+ 'getDuration',
+ 'getDurationIn',
+ 'getDurationOut',
+ 'updateDuration',
+ 'hasEntered',
+ 'hasExited',
+ '_subscribe',
+ '_unsubscribe'
+];
+
function makeGetEnergyInterface (component) {
return function getEnergyInterface (flowValue) {
- const {
- getDuration,
- getDurationIn,
- getDurationOut,
- updateDuration,
- hasEntered,
- hasExited
- } = component;
+ const { type } = component;
const flow = Object.freeze({ value: flowValue, [flowValue]: true });
+ const methods = {};
- return Object.freeze({
- getDuration,
- getDurationIn,
- getDurationOut,
- updateDuration,
- hasEntered,
- hasExited,
- flow
+ METHODS.forEach(methodName => {
+ if (component[methodName]) {
+ methods[methodName] = component[methodName].bind(component);
+ }
});
+
+ return Object.freeze({ type, flow, ...methods });
};
}
-export { makeGetEnergyInterface };
+export { METHODS, makeGetEnergyInterface };
diff --git a/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.test.js b/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.test.js
index 2da391aa..175706d2 100644
--- a/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.test.js
+++ b/packages/animation/src/makeGetEnergyInterface/makeGetEnergyInterface.test.js
@@ -1,38 +1,29 @@
/* eslint-env jest */
-import { makeGetEnergyInterface } from './makeGetEnergyInterface';
+import { METHODS, makeGetEnergyInterface } from './makeGetEnergyInterface';
test('Should get energy interface API with flow and component methods', () => {
- const component = {
- getDuration: 0,
- getDurationIn: 1,
- getDurationOut: 2,
- updateDuration: 3,
- hasEntered: 4,
- hasExited: 5
- };
+ const methods = {};
+ METHODS.forEach(name => (methods[name] = { bind: jest.fn() }));
+ const component = { type: 0, ...methods };
const getEnergyInterface = makeGetEnergyInterface(component);
const flowValue = 'entering';
const energy = getEnergyInterface(flowValue);
expect(energy).toEqual({
- ...component,
- flow: {
- value: flowValue,
- [flowValue]: true
- }
+ type: 0,
+ flow: { value: flowValue, [flowValue]: true }
+ });
+ METHODS.forEach(name => {
+ const method = methods[name];
+ expect(method.bind).toHaveBeenCalledWith(component);
});
});
test('Should get energy interface API as immutable', () => {
- const component = {
- getDuration: 0,
- getDurationIn: 1,
- getDurationOut: 2,
- updateDuration: 3,
- hasEntered: 4,
- hasExited: 5
- };
+ const methods = {};
+ METHODS.forEach(name => (methods[name] = { bind: jest.fn() }));
+ const component = { type: 0, ...methods };
const getEnergyInterface = makeGetEnergyInterface(component);
const energy = getEnergyInterface('entering');
diff --git a/packages/animation/src/makeIsOutsourced/index.js b/packages/animation/src/makeIsOutsourced/index.js
new file mode 100644
index 00000000..f77a5e2e
--- /dev/null
+++ b/packages/animation/src/makeIsOutsourced/index.js
@@ -0,0 +1 @@
+export { makeIsOutsourced } from './makeIsOutsourced';
diff --git a/packages/animation/src/makeIsOutsourced/makeIsOutsourced.js b/packages/animation/src/makeIsOutsourced/makeIsOutsourced.js
new file mode 100644
index 00000000..f78eaf07
--- /dev/null
+++ b/packages/animation/src/makeIsOutsourced/makeIsOutsourced.js
@@ -0,0 +1,24 @@
+import { STREAM_TYPE } from '../constants';
+
+function makeIsOutsourced (component) {
+ return function isOutsourced () {
+ if (component.isRoot()) {
+ return false;
+ }
+
+ if (component.props.imperative) {
+ return true;
+ }
+
+ if (
+ component.props.parentEnergyContext &&
+ component.props.parentEnergyContext.type === STREAM_TYPE
+ ) {
+ return true;
+ }
+
+ return false;
+ };
+}
+
+export { makeIsOutsourced };
diff --git a/packages/animation/src/makeIsOutsourced/makeIsOutsourced.test.js b/packages/animation/src/makeIsOutsourced/makeIsOutsourced.test.js
new file mode 100644
index 00000000..d9ee277d
--- /dev/null
+++ b/packages/animation/src/makeIsOutsourced/makeIsOutsourced.test.js
@@ -0,0 +1,40 @@
+/* eslint-env jest */
+
+import { STREAM_TYPE } from '../constants';
+import { makeIsOutsourced } from './makeIsOutsourced';
+
+test('Should return false by default', () => {
+ const component = {
+ props: {},
+ isRoot: jest.fn(() => false)
+ };
+ const isOutsourced = makeIsOutsourced(component);
+ expect(isOutsourced()).toBe(false);
+});
+
+test('Should return false if root', () => {
+ const component = {
+ props: {},
+ isRoot: jest.fn(() => true)
+ };
+ const isOutsourced = makeIsOutsourced(component);
+ expect(isOutsourced()).toBe(false);
+});
+
+test('Should return true if imperative', () => {
+ const component = {
+ props: { imperative: true },
+ isRoot: jest.fn(() => false)
+ };
+ const isOutsourced = makeIsOutsourced(component);
+ expect(isOutsourced()).toBe(true);
+});
+
+test('Should return true if parent is stream', () => {
+ const component = {
+ props: { parentEnergyContext: { type: STREAM_TYPE } },
+ isRoot: jest.fn(() => false)
+ };
+ const isOutsourced = makeIsOutsourced(component);
+ expect(isOutsourced()).toBe(true);
+});
diff --git a/packages/animation/src/makeScheduler/makeScheduler.js b/packages/animation/src/makeScheduler/makeScheduler.js
index 821898a7..4e77f644 100644
--- a/packages/animation/src/makeScheduler/makeScheduler.js
+++ b/packages/animation/src/makeScheduler/makeScheduler.js
@@ -1,16 +1,20 @@
function makeScheduler () {
- let timeout;
+ const timeouts = {};
- function stop () {
- clearTimeout(timeout);
+ function stop (id) {
+ clearTimeout(timeouts[id]);
}
- function start (time, callback) {
- stop();
- timeout = setTimeout(callback, time);
+ function stopAll () {
+ Object.values(timeouts).forEach(clearTimeout);
}
- return { stop, start };
+ function start (id, time, callback) {
+ stop(id);
+ timeouts[id] = setTimeout(callback, time);
+ }
+
+ return { stop, stopAll, start };
}
export { makeScheduler };
diff --git a/packages/animation/src/makeScheduler/makeScheduler.test.js b/packages/animation/src/makeScheduler/makeScheduler.test.js
index c2983bcc..9ac2a02c 100644
--- a/packages/animation/src/makeScheduler/makeScheduler.test.js
+++ b/packages/animation/src/makeScheduler/makeScheduler.test.js
@@ -10,22 +10,22 @@ afterEach(() => {
jest.clearAllTimers();
});
-test('Should schedule a function call by given time', () => {
+test('Should schedule a function call by given id, time', () => {
const scheduler = makeScheduler();
const spy = jest.fn();
- scheduler.start(100, spy);
+ scheduler.start(0, 100, spy);
jest.advanceTimersByTime(90);
expect(spy).not.toHaveBeenCalled();
jest.advanceTimersByTime(20); // 110ms
expect(spy).toHaveBeenCalledTimes(1);
});
-test('Should stop scheduled function call', () => {
+test('Should stop scheduled function call by id', () => {
const scheduler = makeScheduler();
const spy = jest.fn();
- scheduler.start(100, spy);
+ scheduler.start(0, 100, spy);
jest.advanceTimersByTime(90);
- scheduler.stop();
+ scheduler.stop(0);
expect(spy).not.toHaveBeenCalled();
jest.advanceTimersByTime(20); // 110ms
expect(spy).not.toHaveBeenCalled();
@@ -35,9 +35,9 @@ test('Should re-set schedule function call if called multiple times', () => {
const scheduler = makeScheduler();
const spy1 = jest.fn();
const spy2 = jest.fn();
- scheduler.start(100, spy1);
+ scheduler.start(0, 100, spy1);
jest.advanceTimersByTime(90);
- scheduler.start(100, spy2);
+ scheduler.start(0, 100, spy2);
jest.advanceTimersByTime(90); // 180ms
expect(spy1).not.toHaveBeenCalled();
expect(spy2).not.toHaveBeenCalled();
@@ -45,3 +45,30 @@ test('Should re-set schedule function call if called multiple times', () => {
expect(spy1).not.toHaveBeenCalled();
expect(spy2).toHaveBeenCalledTimes(1);
});
+
+test('Should be able to schedule multiple functions', () => {
+ const scheduler = makeScheduler();
+ const spy1 = jest.fn();
+ const spy2 = jest.fn();
+ scheduler.start(0, 100, spy1);
+ scheduler.start(1, 100, spy2);
+ jest.advanceTimersByTime(90);
+ expect(spy1).not.toHaveBeenCalled();
+ expect(spy2).not.toHaveBeenCalled();
+ jest.advanceTimersByTime(20); // 110ms
+ expect(spy1).toHaveBeenCalledTimes(1);
+ expect(spy2).toHaveBeenCalledTimes(1);
+});
+
+test('Should be able to stop all scheduled functions', () => {
+ const scheduler = makeScheduler();
+ const spy1 = jest.fn();
+ const spy2 = jest.fn();
+ scheduler.start(0, 100, spy1);
+ scheduler.start(1, 100, spy2);
+ jest.advanceTimersByTime(10);
+ scheduler.stopAll();
+ jest.advanceTimersByTime(100); // 110ms
+ expect(spy1).not.toHaveBeenCalled();
+ expect(spy2).not.toHaveBeenCalled();
+});
diff --git a/packages/animation/src/withEnergy/withEnergy.js b/packages/animation/src/withEnergy/withEnergy.js
index 0bc98524..4c87ec0d 100644
--- a/packages/animation/src/withEnergy/withEnergy.js
+++ b/packages/animation/src/withEnergy/withEnergy.js
@@ -35,9 +35,15 @@ function withEnergy (options) {
useEffect(() => {
if (inner && cycles) {
if (energy.flow.entering) {
+ if (!inner.current.enter) {
+ throw new Error('"withEnergy" provided component requires "enter" method.');
+ }
inner.current.enter();
}
else if (energy.flow.exiting) {
+ if (!inner.current.exit) {
+ throw new Error('"withEnergy" provided component requires "exit" method.');
+ }
inner.current.exit();
}
}