Skip to content

Commit

Permalink
Align onParentEventType callbacks across XYPlot (uber#857)
Browse files Browse the repository at this point in the history
* Aligned mouse handlers docs and callback format

Mouse handlers in XYPlot receive SyntheticEvents and not Native Events
by defaults.

Aligned all callback to be called with event parameters, and not with
an object like { event: event }

* Add onParentMouseEnter and onParentMouseLeave

Align the mouseEnter and mouseLeave handler to call respective
onParentEventType callback for every series child

* Add onMouseUp calls and handlers

* Updated docs for onMouseUp prop

* Fix wrong callback for onTouchStart

* Add tests for onParentEventType handlers
  • Loading branch information
markov00 authored and mcnuttandrew committed Jul 10, 2018
1 parent a4a21bc commit a288f91
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 14 deletions.
5 changes: 5 additions & 0 deletions docs/interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ Type: `function`
Default: none
This event handler is triggered whenever the mousebutton of the user is down while their mouse cursor is in the plot area. It passes a mouse event.

### onMouseUp
Type: `function`
Default: none
This event handler is triggered whenever the user release the mouse button while their mouse cursor is in the plot area. It passes a mouse event.

### onMouseEnter
Type: `function`
Default: none
Expand Down
10 changes: 9 additions & 1 deletion docs/xy-plot.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ XYPlot is a wrapper for series, hints, axes and other components. Most of these
* `size` (optional)
* `style` (optional) - css properties as an object.

Accessors can also be used to retreieve the properties above from the `data` object. For instance, the `getX` and `getY` accessors can be passed to the XYPlot object to access the `x` and `y` properties from `data` for each series.
Accessors can also be used to retreieve the properties above from the `data` object. For instance, the `getX` and `getY` accessors can be passed to the XYPlot object to access the `x` and `y` properties from `data` for each series.

```jsx
<XYPlot
Expand Down Expand Up @@ -238,6 +238,14 @@ The function that is triggered each time mouse moves over at the component.
Type: `function()`
The function that is triggered each time the mouse enters the component.

#### onMouseDown (optional)
Type: `function()`
The function that is triggered each time the mouse button is pressed over the component.

#### onMouseUp (optional)
Type: `function()`
The function that is triggered each time the mouse button is released over the component.

#### onTouchStart (optional)
Type: `function()`
The function that is triggered each time the touch starts.
Expand Down
61 changes: 48 additions & 13 deletions src/plot/xy-plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class XYPlot extends React.Component {
onClick: PropTypes.func,
onDoubleClick: PropTypes.func,
onMouseDown: PropTypes.func,
onMouseUp: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onMouseMove: PropTypes.func,
Expand All @@ -131,6 +132,7 @@ class XYPlot extends React.Component {
this._clickHandler = this._clickHandler.bind(this);
this._doubleClickHandler = this._doubleClickHandler.bind(this);
this._mouseDownHandler = this._mouseDownHandler.bind(this);
this._mouseUpHandler = this._mouseUpHandler.bind(this);
this._mouseLeaveHandler = this._mouseLeaveHandler.bind(this);
this._mouseEnterHandler = this._mouseEnterHandler.bind(this);
this._mouseMoveHandler = this._mouseMoveHandler.bind(this);
Expand Down Expand Up @@ -203,6 +205,24 @@ class XYPlot extends React.Component {
}
});
}
/**
* Trigger mouse-up related callbacks if they are available.
* @param {React.SyntheticEvent} event Mouse up event.
* @private
*/
_mouseUpHandler(event) {
const {onMouseUp, children} = this.props;
if (onMouseUp) {
onMouseUp(event);
}
const seriesChildren = getSeriesChildren(children);
seriesChildren.forEach((child, index) => {
const component = this[`series${index}`];
if (component && component.onParentMouseUp) {
component.onParentMouseUp(event);
}
});
}

/**
* Trigger movement-related callbacks if they are available.
Expand All @@ -225,26 +245,40 @@ class XYPlot extends React.Component {

/**
* Trigger onMouseLeave handler if it was passed in props.
* @param {Event} event Native event.
* @param {React.SyntheticEvent} event Mouse leave event.
* @private
*/
_mouseLeaveHandler(event) {
const {onMouseLeave} = this.props;
const {onMouseLeave, children} = this.props;
if (onMouseLeave) {
onMouseLeave({event});
onMouseLeave(event);
}
const seriesChildren = getSeriesChildren(children);
seriesChildren.forEach((child, index) => {
const component = this[`series${index}`];
if (component && component.onParentMouseLeave) {
component.onParentMouseLeave(event);
}
});
}

/**
* Trigger onMouseEnter handler if it was passed in props.
* @param {Event} event Native event.
* @param {React.SyntheticEvent} event Mouse enter event.
* @private
*/
_mouseEnterHandler(event) {
const {onMouseEnter} = this.props;
const {onMouseEnter, children} = this.props;
if (onMouseEnter) {
onMouseEnter({event});
onMouseEnter(event);
}
const seriesChildren = getSeriesChildren(children);
seriesChildren.forEach((child, index) => {
const component = this[`series${index}`];
if (component && component.onParentMouseEnter) {
component.onParentMouseEnter(event);
}
});
}

/**
Expand All @@ -253,9 +287,9 @@ class XYPlot extends React.Component {
* @private
*/
_touchStartHandler(event) {
const {onMouseDown, children} = this.props;
if (onMouseDown) {
onMouseDown(event);
const {onTouchStart, children} = this.props;
if (onTouchStart) {
onTouchStart(event);
}
const seriesChildren = getSeriesChildren(children);
seriesChildren.forEach((child, index) => {
Expand Down Expand Up @@ -287,25 +321,25 @@ class XYPlot extends React.Component {

/**
* Trigger onTouchEnd handler if it was passed in props.
* @param {Event} event Native event.
* @param {React.SyntheticEvent} event Touch End event.
* @private
*/
_touchEndHandler(event) {
const {onTouchEnd} = this.props;
if (onTouchEnd) {
onTouchEnd({event});
onTouchEnd(event);
}
}

/**
* Trigger onTouchCancel handler if it was passed in props.
* @param {Event} event Native event.
* @param {React.SyntheticEvent} event Touch Cancel event.
* @private
*/
_touchCancelHandler(event) {
const {onTouchCancel} = this.props;
if (onTouchCancel) {
onTouchCancel({event});
onTouchCancel(event);
}
}

Expand Down Expand Up @@ -518,6 +552,7 @@ class XYPlot extends React.Component {
onClick={this._clickHandler}
onDoubleClick={this._doubleClickHandler}
onMouseDown={this._mouseDownHandler}
onMouseUp={this._mouseUpHandler}
onMouseMove={this._mouseMoveHandler}
onMouseLeave={this._mouseLeaveHandler}
onMouseEnter={this._mouseEnterHandler}
Expand Down
62 changes: 62 additions & 0 deletions tests/components/xy-plot-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import test from 'tape';
import React from 'react';
import {mount, shallow} from 'enzyme';

import AbstractSeries from 'plot/series/abstract-series';
import VerticalBarSeries from 'plot/series/vertical-bar-series';
import BarSeries from 'plot/series/bar-series';
import LineSeries from 'plot/series/line-series';
Expand Down Expand Up @@ -241,3 +242,64 @@ test('Render a line series with data accessors', t => {
);
t.end();
});

test('Trigger all onParentMouse handlers on Series components', t => {
t.plan(14);
class ExtendedSeries extends AbstractSeries {
onParentMouseUp(event) {
t.pass(`onParentMouseUp on ${this.props.name} is called correctly`);
}
onParentMouseDown(event) {
t.pass(`onParentMouseDown on ${this.props.name} is called correctly`);
}
onParentMouseMove(event) {
t.pass(`onParentMouseMove on ${this.props.name} is called correctly`);
}
onParentMouseLeave(event) {
t.pass(`onParentMouseLeave on ${this.props.name} is called correctly`);
}
onParentMouseEnter(event) {
t.pass(`onParentMouseEnter on ${this.props.name} is called correctly`);
}
onParentTouchStart(event) {
t.pass(`onParentTouchStart on ${this.props.name} is called correctly`);
}
onParentTouchMove(event) {
t.pass(`onParentTouchMove on ${this.props.name} is called correctly`);
}
render() {
return null;
}
}
const $ = mount(
<XYPlot
width={300}
height={300}
getX={d => d[0]}
getY={d => d[1]}>
<ExtendedSeries
name="series-1"
data={[
[1, 0],
[2, 1],
[3, 2]
]}
/>
<ExtendedSeries
name="series-2"
data={[
[1, 0],
[2, 1],
[3, 2]
]}
/>
</XYPlot>
);
$.find('svg').at(0).simulate('mouseenter');
$.find('svg').at(0).simulate('mousedown');
$.find('svg').at(0).simulate('mousemove');
$.find('svg').at(0).simulate('mouseup');
$.find('svg').at(0).simulate('mouseleave');
$.find('svg').at(0).simulate('touchstart');
$.find('svg').at(0).simulate('touchmove');
});

0 comments on commit a288f91

Please sign in to comment.