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

No jsx bind #265

Merged
merged 9 commits into from
Feb 15, 2016
1 change: 1 addition & 0 deletions src/__tests__/ComboBox-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* global jest describe beforeEach it expect */

jest.dontMock('../components/ComboBox');
jest.dontMock('../components/ComboBoxItem');
jest.dontMock('../components/Option');

import React from 'react';
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/DatePicker-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

jest.dontMock('../components/ActionArea');
jest.dontMock('../components/DatePicker');
jest.dontMock('../components/DisabledDay');
jest.dontMock('../components/Day');
jest.dontMock('../utils/inject-style');
jest.dontMock('../utils/date-helpers');
jest.dontMock('../config/i18n');
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/Select-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* global jest describe beforeEach it expect */

jest.dontMock('../components/Select');
jest.dontMock('../components/SelectItem');
jest.dontMock('../components/Option');
jest.dontMock('../components/Placeholder');
jest.dontMock('../components/Separator');
Expand Down
20 changes: 10 additions & 10 deletions src/components/ComboBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { injectStyles, removeAllStyles } from '../utils/inject-style';
import unionClassNames from '../utils/union-class-names';
import { omit, filterReactChildren, has, isEmpty, find, getArrayForReactChildren } from '../utils/helpers';
import style from '../style/combo-box';
import ComboBoxItem from '../components/ComboBoxItem';

/**
* Update hover style for the specified styleId.
Expand Down Expand Up @@ -742,19 +743,18 @@ export default class ComboBox extends Component {
>
{
React.Children.map(this.filteredOptions, (entry, index) => ((
<li
<ComboBoxItem
key={ index }
onTouchStart={ (event) => this._onTouchStartAtOption(event, index) }
onTouchEnd={ (event) => this._onTouchEndAtOption(event, index) }
onTouchCancel={ this._onTouchCancelAtOption }
onClick={ () => this._onClickAtOption(index) }
onMouseEnter={ () => this._onMouseEnterAtOption(index) }
onMouseLeave={ this._onMouseLeaveAtOption }
onMouseDown={ (event) => event.preventDefault() }
role="option"
index={ index }
onItemTouchStart={ this._onTouchStartAtOption }
onItemTouchEnd={ this._onTouchEndAtOption }
onItemTouchCancel={ this._onTouchCancelAtOption }
onItemClick={ this._onClickAtOption(index) }
onItemMouseEnter={ this._onMouseEnterAtOption }
onItemMouseLeave={ this._onMouseLeaveAtOption }
>
{ entry }
</li>
</ComboBoxItem>
)))
}
</ul>
Expand Down
67 changes: 67 additions & 0 deletions src/components/ComboBoxItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { Component, PropTypes } from 'react';

/**
* Belle internal component to wrap an Option in a ComboBox.
*
* This component exists to avoid binding functions in JSX.
*/
export default class ComboBoxItem extends Component {

static displayName = 'ComboBoxItem';

static propTypes = {
children: PropTypes.node.isRequired,
index: PropTypes.number.isRequired,
onItemClick: PropTypes.func.isRequired,
onItemTouchStart: PropTypes.func.isRequired,
onItemTouchEnd: PropTypes.func.isRequired,
onItemTouchCancel: PropTypes.func.isRequired,
onItemMouseEnter: PropTypes.func.isRequired,
onItemMouseLeave: PropTypes.func.isRequired,
};

_onClick = () => {
this.props.onItemClick(this.props.index);
};

_onTouchStart = (event) => {
this.props.onItemTouchStart(event, this.props.index);
};

_onTouchEnd = (event) => {
this.props.onItemTouchEnd(event, this.props.index);
};

_onTouchCancel = () => {
this.props.onItemTouchCancel();
};

_onMouseEnter = () => {
this.props.onItemMouseEnter(this.props.index);
};

_onMouseLeave = () => {
this.props.onItemMouseLeave();
};

_onMouseDown = (event) => {
event.preventDefault();
};

render() {
return (
<li
onClick={ this._onClick }
onMouseEnter={ this._onMouseEnter }
onMouseLeave={ this._onMouseLeave }
onMouseDown={ this._onMouseDown }
onTouchStart={ this._onTouchStart }
onTouchEnd={ this._onTouchEnd }
onTouchCancel={ this._onTouchCancel }
role="option"
>
{ this.props.children }
</li>
);
}
}
57 changes: 30 additions & 27 deletions src/components/DatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import {
import defaultStyle from '../style/date-picker';
import config from '../config/datePicker';
import ActionArea from './ActionArea';
import DisabledDay from './DisabledDay';
import Day from './Day';

/**
* Returns an object with properties that are relevant for the wrapping div of the date picker.
*/
function sanitizeWrapperProps(properties) {
return omit(properties, [
'ref',
'tabIndex',
'onFocus',
'onBlur',
Expand All @@ -43,7 +44,6 @@ function sanitizeWrapperProps(properties) {
function sanitizeEmptyDayProps(properties) {
return omit(properties, [
'key',
'ref',
'style',
]);
}
Expand All @@ -54,7 +54,6 @@ function sanitizeEmptyDayProps(properties) {
function sanitizeDisabledDayProps(properties) {
return omit(properties, [
'key',
'ref',
'onMouseEnter',
'onMouseLeave',
'style',
Expand All @@ -67,7 +66,6 @@ function sanitizeDisabledDayProps(properties) {
function sanitizeDayProps(properties) {
return omit(properties, [
'key',
'ref',
'onMouseDown',
'onMouseUp',
'onMouseEnter',
Expand Down Expand Up @@ -675,8 +673,12 @@ export default class DatePicker extends Component {
* Callback is called when some day receives mouseUp.
* It will reset this.state.activeDay and call props.onDayMouseUp.
*/
_onDayMouseUp = (dateKey, day, month, year, event) => {
_onDayMouseUp = (dateKey, event) => {
if (event.button === 0 && !this.props.disabled && !this.props.readOnly && this.state.activeDay === dateKey) {
const date = getDateForDateKey(dateKey);
const day = date.getDate();
const month = date.getMonth();
const year = date.getFullYear();
this._triggerSelectDate(day, month, year);
this.setState({
// Note: updating focusedDateKey in mouseEnter normally would be good enough,
Expand Down Expand Up @@ -745,8 +747,12 @@ export default class DatePicker extends Component {
* Callback is called when some day receives touchEnd.
* It will reset this.state.activeDay and call props.onDayTouchEnd.
*/
_onDayTouchEnd = (dateKey, day, month, year, event) => {
_onDayTouchEnd = (dateKey, event) => {
if (!this.props.disabled && !this.props.readOnly) {
const date = getDateForDateKey(dateKey);
const day = date.getDate();
const month = date.getMonth();
const year = date.getFullYear();
this._triggerSelectDate(day, month, year);
if (this.state.activeDay === dateKey) {
this.setState({
Expand Down Expand Up @@ -1197,7 +1203,6 @@ export default class DatePicker extends Component {
return (
<span
key={ `day-${index}` }
ref={ dateKey }
style={ dayStyle }
{...this.emptyDayProps}
/>
Expand All @@ -1206,37 +1211,36 @@ export default class DatePicker extends Component {

if (isDisabledDay) {
return (
<span
<DisabledDay
key={ `day-${index}` }
ref={ dateKey }
style={ dayStyle }
onMouseEnter={ (event) => this._onDayMouseEnter(dateKey, event) }
onMouseLeave={ (event) => this._onDayMouseLeave(dateKey, event) }
{...this.disabledDayProps}
dateKey={ dateKey }
onDayMouseEnter={ this._onDayMouseEnter }
onDayMouseLeave={ this._onDayMouseLeave }
disabledDayProps={ this.disabledDayProps }
>
{ renderedDay }
</span>
</DisabledDay>
);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikgraf I do not like the idea of extracting a component which just handles event callbacks to get rid of bindings. A component should be a more logical entity and should be able to handle its styling logic and stuff like making dayKey out of date.

If we can do this kind of component it will help us in not just getting rid of bindings but also simplify these complex components.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to a PR to simplify the whole structure 😄

I'm not a big fan of having these components just for event handling, but it's the best option I'm aware of to avoid performance issue and therefor more important than not doing it at all.

return (
<span
<Day
key={ `day-${index}` }
ref={ dateKey }
onMouseDown={ (event) => this._onDayMouseDown(dateKey, event) }
onMouseUp={ (event) => this._onDayMouseUp(dateKey, day, month, year, event) }
onMouseEnter={ (event) => this._onDayMouseEnter(dateKey, event) }
onMouseLeave={ (event) => this._onDayMouseLeave(dateKey, event) }
onTouchStart={ (event) => this._onDayTouchStart(dateKey, event) }
onTouchEnd={ (event) => this._onDayTouchEnd(dateKey, day, month, year, event) }
onTouchCancel={ (event) => this._onDayTouchCancel(dateKey, event) }
aria-selected={ ariaSelected }
dateKey={ dateKey }
onDayMouseDown={ this._onDayMouseDown }
onDayMouseUp={ this._onDayMouseUp }
onDayMouseEnter={ this._onDayMouseEnter }
onDayMouseLeave={ this._onDayMouseLeave }
onDayTouchStart={ this._onDayTouchStart }
onDayTouchEnd={ this._onDayTouchEnd }
onDayTouchCancel={ this._onDayTouchCancel }
selected={ ariaSelected }
style={ dayStyle }
role="gridcell"
{...this.dayProps}
dayProps={this.dayProps}
>
{ renderedDay }
</span>
</Day>
);
}

Expand Down Expand Up @@ -1298,7 +1302,6 @@ export default class DatePicker extends Component {

return (
<div
ref="datePicker"
tabIndex={ tabIndex }
onFocus={ this._onFocus }
onBlur={ this._onBlur }
Expand Down
75 changes: 75 additions & 0 deletions src/components/Day.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { Component, PropTypes } from 'react';

/**
* Belle internal component to wrap a Day in a DatePicker.
*
* This component exists to avoid binding functions in JSX.
*/
export default class Day extends Component {

static displayName = 'Day';

static propTypes = {
children: PropTypes.node.isRequired,
dateKey: PropTypes.string.isRequired,
onDayMouseEnter: PropTypes.func.isRequired,
onDayMouseLeave: PropTypes.func.isRequired,
onDayMouseDown: PropTypes.func.isRequired,
onDayMouseUp: PropTypes.func.isRequired,
onDayTouchStart: PropTypes.func.isRequired,
onDayTouchEnd: PropTypes.func.isRequired,
onDayTouchCancel: PropTypes.func.isRequired,
style: PropTypes.object.isRequired,
dayProps: PropTypes.any,
selected: PropTypes.bool.isRequired,
};

_onMouseEnter = (event) => {
this.props.onDayMouseEnter(this.props.dateKey, event);
};

_onMouseLeave = (event) => {
this.props.onDayMouseLeave(this.props.dateKey, event);
};

_onMouseDown = (event) => {
this.props.onDayMouseDown(this.props.dateKey, event);
};

_onMouseUp = (event) => {
this.props.onDayMouseUp(this.props.dateKey, event);
};

_onTouchStart = (event) => {
this.props.onDayTouchStart(this.props.dateKey, event);
};

_onTouchEnd = (event) => {
this.props.onDayTouchEnd(this.props.dateKey, event);
};

_onTouchCancel = (event) => {
this.props.onDayTouchCancel(this.props.dateKey, event);
};

render() {
return (
<span
style={ this.props.style }
onMouseEnter={ this._onMouseEnter }
onMouseLeave={ this._onMouseLeave }
onMouseDown={ this._onMouseDown }
onMouseUp={ this._onMouseUp }
onTouchStart={ this._onTouchStart }
onTouchEnd={ this._onTouchEnd }
onTouchCancel={ this._onTouchCancel }
aria-selected={ this.props.selected }
style={ this.props.style }
role="gridcell"
{...this.props.dayProps}
>
{ this.props.children }
</span>
);
}
}
41 changes: 41 additions & 0 deletions src/components/DisabledDay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { Component, PropTypes } from 'react';

/**
* Belle internal component to wrap a DisabledDay in a DatePicker.
*
* This component exists to avoid binding functions in JSX.
*/
export default class DisabledDay extends Component {

static displayName = 'DisabledDay';

static propTypes = {
children: PropTypes.node.isRequired,
dateKey: PropTypes.string.isRequired,
onDayMouseEnter: PropTypes.func.isRequired,
onDayMouseLeave: PropTypes.func.isRequired,
style: PropTypes.object.isRequired,
disabledDayProps: PropTypes.any,
};

_onMouseEnter = (event) => {
this.props.onDayMouseEnter(this.props.dateKey, event);
};

_onMouseLeave = (event) => {
this.props.onDayMouseLeave(this.props.dateKey, event);
};

render() {
return (
<span
style={ this.props.style }
onMouseEnter={ this._onDayMouseEnter }
onMouseLeave={ this._onDayMouseLeave }
{...this.props.disabledDayProps}
>
{ this.props.children }
</span>
);
}
}
Loading