Skip to content

Commit

Permalink
Merge pull request #286 from airbnb/avoid-rendering-when-closed
Browse files Browse the repository at this point in the history
Avoid rendering DayPicker when not visible
  • Loading branch information
majapw committed Feb 7, 2017
2 parents 629d8c0 + 91f1ae4 commit 57dcbaf
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 88 deletions.
8 changes: 0 additions & 8 deletions css/DateRangePicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@
top: $react-dates-spacing-vertical-picker;
}

.DateRangePicker__picker--show {
visibility: visible;
}

.DateRangePicker__picker--invisible {
visibility: hidden;
}

.DateRangePicker__picker--direction-left {
left: 0;
}
Expand Down
8 changes: 0 additions & 8 deletions css/SingleDatePicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@
top: $react-dates-spacing-vertical-picker;
}

.SingleDatePicker__picker--show {
visibility: visible;
}

.SingleDatePicker__picker--invisible {
visibility: hidden;
}

.SingleDatePicker__picker--direction-left {
left: 0;
}
Expand Down
39 changes: 28 additions & 11 deletions src/components/DateRangePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,30 +92,32 @@ export default class DateRangePicker extends React.Component {
return shallowCompare(this, nextProps, nextState);
}

componentDidUpdate(prevProps) {
if (!prevProps.focusedInput && this.props.focusedInput && this.isOpened()) {
// The date picker just changed from being closed to being open.
this.responsivizePickerPosition();
}
}

componentWillUnmount() {
window.removeEventListener('resize', this.responsivizePickerPosition);
}

onOutsideClick() {
const { focusedInput, onFocusChange } = this.props;
if (!focusedInput) return;
const { onFocusChange } = this.props;
if (!this.isOpened()) return;

onFocusChange(null);
}

getDayPickerContainerClasses() {
const {
focusedInput,
orientation,
withPortal,
withFullScreenPortal,
anchorDirection,
} = this.props;
const showDatepicker = focusedInput === START_DATE || focusedInput === END_DATE;

const dayPickerClassName = cx('DateRangePicker__picker', {
'DateRangePicker__picker--show': showDatepicker,
'DateRangePicker__picker--invisible': !showDatepicker,
'DateRangePicker__picker--direction-left': anchorDirection === ANCHOR_LEFT,
'DateRangePicker__picker--direction-right': anchorDirection === ANCHOR_RIGHT,
'DateRangePicker__picker--horizontal': orientation === HORIZONTAL_ORIENTATION,
Expand All @@ -131,7 +133,16 @@ export default class DateRangePicker extends React.Component {
return ReactDOM.findDOMNode(this.dayPicker);
}

isOpened() {
const { focusedInput } = this.props;
return focusedInput === START_DATE || focusedInput === END_DATE;
}

responsivizePickerPosition() {
if (!this.isOpened()) {
return;
}

const { anchorDirection, horizontalMargin, withPortal, withFullScreenPortal } = this.props;
const { dayPickerContainerStyles } = this.state;

Expand All @@ -154,11 +165,15 @@ export default class DateRangePicker extends React.Component {
}

maybeRenderDayPickerWithPortal() {
const { focusedInput, withPortal, withFullScreenPortal } = this.props;
const { withPortal, withFullScreenPortal } = this.props;

if (!this.isOpened()) {
return null;
}

if (withPortal || withFullScreenPortal) {
return (
<Portal isOpened={focusedInput !== null}>
<Portal isOpened>
{this.renderDayPicker()}
</Portal>
);
Expand Down Expand Up @@ -193,7 +208,9 @@ export default class DateRangePicker extends React.Component {
} = this.props;
const { dayPickerContainerStyles } = this.state;

const onOutsideClick = (!withFullScreenPortal && withPortal) ? this.onOutsideClick : undefined;
const onOutsideClick = (!withFullScreenPortal && withPortal)
? this.onOutsideClick
: undefined;

return (
<div
Expand All @@ -215,7 +232,7 @@ export default class DateRangePicker extends React.Component {
endDate={endDate}
monthFormat={monthFormat}
withPortal={withPortal || withFullScreenPortal}
hidden={!focusedInput}
hidden={!this.isOpened()}
initialVisibleMonth={initialVisibleMonth}
onOutsideClick={onOutsideClick}
navPrev={navPrev}
Expand Down
32 changes: 26 additions & 6 deletions src/components/SingleDatePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ export default class SingleDatePicker extends React.Component {
this.today = moment();
}

componentDidUpdate(prevProps) {
if (!prevProps.focused && this.props.focused) {
this.responsivizePickerPosition();
}
}

/* istanbul ignore next */
componentWillUnmount() {
window.removeEventListener('resize', this.responsivizePickerPosition);
Expand Down Expand Up @@ -161,12 +167,10 @@ export default class SingleDatePicker extends React.Component {
}

getDayPickerContainerClasses() {
const { orientation, withPortal, withFullScreenPortal, anchorDirection, focused } = this.props;
const { orientation, withPortal, withFullScreenPortal, anchorDirection } = this.props;
const { hoverDate } = this.state;

const dayPickerClassName = cx('SingleDatePicker__picker', {
'SingleDatePicker__picker--show': focused,
'SingleDatePicker__picker--invisible': !focused,
'SingleDatePicker__picker--direction-left': anchorDirection === ANCHOR_LEFT,
'SingleDatePicker__picker--direction-right': anchorDirection === ANCHOR_RIGHT,
'SingleDatePicker__picker--horizontal': orientation === HORIZONTAL_ORIENTATION,
Expand Down Expand Up @@ -194,9 +198,19 @@ export default class SingleDatePicker extends React.Component {

/* istanbul ignore next */
responsivizePickerPosition() {
const { anchorDirection, horizontalMargin, withPortal, withFullScreenPortal } = this.props;
const {
anchorDirection,
horizontalMargin,
withPortal,
withFullScreenPortal,
focused,
} = this.props;
const { dayPickerContainerStyles } = this.state;

if (!focused) {
return;
}

const isAnchoredLeft = anchorDirection === ANCHOR_LEFT;

if (!withPortal && !withFullScreenPortal) {
Expand Down Expand Up @@ -236,9 +250,13 @@ export default class SingleDatePicker extends React.Component {
maybeRenderDayPickerWithPortal() {
const { focused, withPortal, withFullScreenPortal } = this.props;

if (!focused) {
return null;
}

if (withPortal || withFullScreenPortal) {
return (
<Portal isOpened={focused}>
<Portal isOpened>
{this.renderDayPicker()}
</Portal>
);
Expand Down Expand Up @@ -278,7 +296,9 @@ export default class SingleDatePicker extends React.Component {
selected: day => this.isSelected(day),
};

const onOutsideClick = (!withFullScreenPortal && withPortal) ? this.onClearFocus : undefined;
const onOutsideClick = (!withFullScreenPortal && withPortal)
? this.onClearFocus
: undefined;

return (
<div
Expand Down
58 changes: 35 additions & 23 deletions test/components/DateRangePicker_spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,69 +25,79 @@ describe('DateRangePicker', () => {
});

it('renders .DateRangePicker__picker class', () => {
const wrapper = shallow(<DateRangePicker />);
const wrapper = shallow(<DateRangePicker focusedInput={START_DATE} />);
expect(wrapper.find('.DateRangePicker__picker')).to.have.length(1);
});

it('renders <DateRangePickerInputWithHandlers />', () => {
const wrapper = shallow(<DateRangePicker />);
const wrapper = shallow(<DateRangePicker focusedInput={START_DATE} />);
expect(wrapper.find(DateRangePickerInputController)).to.have.length(1);
});

it('renders <DayPickerRangeController />', () => {
const wrapper = shallow(<DateRangePicker />);
const wrapper = shallow(<DateRangePicker focusedInput={START_DATE} />);
expect(wrapper.find(DayPickerRangeController)).to.have.length(1);
});

describe('props.orientation === VERTICAL_ORIENTATION', () => {
it('renders .DateRangePicker__picker--vertical class', () => {
const wrapper = shallow(<DateRangePicker orientation={VERTICAL_ORIENTATION} />);
const wrapper = shallow(
<DateRangePicker orientation={VERTICAL_ORIENTATION} focusedInput={START_DATE} />,
);
expect(wrapper.find('.DateRangePicker__picker--vertical')).to.have.length(1);
});
});

describe('props.orientation === HORIZONTAL_ORIENTATION', () => {
it('renders .DateRangePicker__picker--horizontal class', () => {
const wrapper = shallow(<DateRangePicker orientation={HORIZONTAL_ORIENTATION} />);
const wrapper = shallow(
<DateRangePicker orientation={HORIZONTAL_ORIENTATION} focusedInput={START_DATE} />,
);
expect(wrapper.find('.DateRangePicker__picker--horizontal')).to.have.length(1);
});

it('renders <DayPickerRangeController /> with props.numberOfMonths === 2', () => {
const wrapper = shallow(<DateRangePicker orientation={HORIZONTAL_ORIENTATION} />);
const wrapper = shallow(
<DateRangePicker orientation={HORIZONTAL_ORIENTATION} focusedInput={START_DATE} />,
);
expect(wrapper.find(DayPickerRangeController).props().numberOfMonths).to.equal(2);
});
});

describe('props.anchorDirection === ANCHOR_LEFT', () => {
it('renders .DateRangePicker__picker--direction-left class', () => {
const wrapper = shallow(<DateRangePicker anchorDirection={ANCHOR_LEFT} />);
const wrapper = shallow(
<DateRangePicker anchorDirection={ANCHOR_LEFT} focusedInput={START_DATE} />,
);
expect(wrapper.find('.DateRangePicker__picker--direction-left')).to.have.length(1);
});
});

describe('props.orientation === ANCHOR_RIGHT', () => {
it('renders .DateRangePicker__picker--direction-right class', () => {
const wrapper = shallow(<DateRangePicker anchorDirection={ANCHOR_RIGHT} />);
const wrapper = shallow(
<DateRangePicker anchorDirection={ANCHOR_RIGHT} focusedInput={START_DATE} />,
);
expect(wrapper.find('.DateRangePicker__picker--direction-right')).to.have.length(1);
});
});

describe('props.withPortal is truthy', () => {
it('renders .DateRangePicker__picker--portal class', () => {
const wrapper = shallow(<DateRangePicker withPortal />);
const wrapper = shallow(<DateRangePicker withPortal focusedInput={START_DATE} />);
expect(wrapper.find('.DateRangePicker__picker--portal')).to.have.length(1);
});

describe('<Portal />', () => {
it('is rendered', () => {
const wrapper = shallow(<DateRangePicker withPortal />);
const wrapper = shallow(<DateRangePicker withPortal focusedInput={START_DATE} />);
expect(wrapper.find(Portal)).to.have.length(1);
});

it('isOpened prop is false if props.focusedInput === null', () => {
it('is not rendered if props.focusedInput === null', () => {
const wrapper =
shallow(<DateRangePicker focusedInput={null} withPortal />);
expect(wrapper.find(Portal).props().isOpened).to.equal(false);
expect(wrapper.find(Portal)).to.have.length(0);
});

it('isOpened prop is true if props.focusedInput !== null', () => {
Expand All @@ -99,30 +109,32 @@ describe('DateRangePicker', () => {

describe('props.withFullScreenPortal is truthy', () => {
it('renders .DateRangePicker__picker--portal class', () => {
const wrapper = shallow(<DateRangePicker withFullScreenPortal />);
const wrapper = shallow(<DateRangePicker withFullScreenPortal focusedInput={START_DATE} />);
expect(wrapper.find('.DateRangePicker__picker--portal')).to.have.length(1);
});

it('renders .DateRangePicker__picker--full-screen-portal class', () => {
const wrapper = shallow(<DateRangePicker withFullScreenPortal />);
const wrapper = shallow(<DateRangePicker withFullScreenPortal focusedInput={START_DATE} />);
expect(wrapper.find('.DateRangePicker__picker--full-screen-portal')).to.have.length(1);
});

it('renders .DateRangePicker__close class', () => {
it('does not render <DayPickerRangeController>', () => {
const wrapper = shallow(<DateRangePicker withFullScreenPortal />);
expect(wrapper.find('.DateRangePicker__close')).to.have.length(1);
expect(wrapper.find(DayPickerRangeController)).to.have.length(0);
});

describe('<Portal />', () => {
it('is rendered', () => {
const wrapper = shallow(<DateRangePicker withFullScreenPortal />);
const wrapper = shallow(
<DateRangePicker withFullScreenPortal focusedInput={START_DATE} />,
);
expect(wrapper.find(Portal)).to.have.length(1);
});

it('isOpened prop is false if props.focusedInput === null', () => {
it('is not rendered if props.focusedInput === null', () => {
const wrapper =
shallow(<DateRangePicker focusedInput={null} withFullScreenPortal />);
expect(wrapper.find(Portal).props().isOpened).to.equal(false);
expect(wrapper.find(Portal)).to.have.length(0);
});

it('isOpened prop is true if props.focusedInput !== null', () => {
Expand All @@ -134,14 +146,14 @@ describe('DateRangePicker', () => {
});

describe('props.focusedInput', () => {
it('shows datepicker if props.focusedInput != null', () => {
it('renders <DayPickerRangeController> if props.focusedInput != null', () => {
const wrapper = shallow(<DateRangePicker focusedInput={START_DATE} />);
expect(wrapper.find('.DateRangePicker__picker--show')).to.have.length(1);
expect(wrapper.find(DayPickerRangeController)).to.have.length(1);
});

it('hides datepicker if props.focusedInput = null', () => {
it('does not render <DayPickerRangeController> if props.focusedInput = null', () => {
const wrapper = shallow(<DateRangePicker focusedInput={null} />);
expect(wrapper.find('.DateRangePicker__picker--invisible')).to.have.length(1);
expect(wrapper.find(DayPickerRangeController)).to.have.length(0);
});
});
});
Expand Down
Loading

0 comments on commit 57dcbaf

Please sign in to comment.