Skip to content

Commit

Permalink
Merge pull request #1784 from airbnb/bottom-nav-position
Browse files Browse the repository at this point in the history
Add support for positioning month navigation under the calendar
  • Loading branch information
noratarano committed Sep 26, 2019
2 parents 0444bcf + c82f9b0 commit bf31594
Show file tree
Hide file tree
Showing 16 changed files with 222 additions and 5 deletions.
9 changes: 8 additions & 1 deletion examples/DateRangePickerWrapper.jsx
Expand Up @@ -8,7 +8,13 @@ import DateRangePicker from '../src/components/DateRangePicker';

import { DateRangePickerPhrases } from '../src/defaultPhrases';
import DateRangePickerShape from '../src/shapes/DateRangePickerShape';
import { START_DATE, END_DATE, HORIZONTAL_ORIENTATION, ANCHOR_LEFT } from '../src/constants';
import {
START_DATE,
END_DATE,
HORIZONTAL_ORIENTATION,
ANCHOR_LEFT,
NAV_POSITION_TOP,
} from '../src/constants';
import isInclusivelyAfterDay from '../src/utils/isInclusivelyAfterDay';

const propTypes = {
Expand Down Expand Up @@ -66,6 +72,7 @@ const defaultProps = {
isRTL: false,

// navigation related props
navPosition: NAV_POSITION_TOP,
navPrev: null,
navNext: null,
onPrevMonthClick() {},
Expand Down
7 changes: 7 additions & 0 deletions src/components/DateRangePicker.jsx
Expand Up @@ -35,6 +35,7 @@ import {
INFO_POSITION_BOTTOM,
FANG_HEIGHT_PX,
DEFAULT_VERTICAL_SPACING,
NAV_POSITION_TOP,
} from '../constants';

const propTypes = forbidExtraProps({
Expand Down Expand Up @@ -98,6 +99,8 @@ const defaultProps = {
horizontalMonthPadding: undefined,

// navigation related props
dayPickerNavigationInlineStyles: null,
navPosition: NAV_POSITION_TOP,
navPrev: null,
navNext: null,

Expand Down Expand Up @@ -405,6 +408,8 @@ class DateRangePicker extends React.PureComponent {
monthFormat,
renderMonthText,
renderWeekHeaderElement,
dayPickerNavigationInlineStyles,
navPosition,
navPrev,
navNext,
onPrevMonthClick,
Expand Down Expand Up @@ -510,6 +515,8 @@ class DateRangePicker extends React.PureComponent {
daySize={daySize}
initialVisibleMonth={initialVisibleMonthThunk}
hideKeyboardShortcutsPanel={hideKeyboardShortcutsPanel}
dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles}
navPosition={navPosition}
navPrev={navPrev}
navNext={navNext}
minimumNights={minimumNights}
Expand Down
18 changes: 17 additions & 1 deletion src/components/DayPicker.jsx
Expand Up @@ -27,6 +27,7 @@ import getActiveElement from '../utils/getActiveElement';
import isDayVisible from '../utils/isDayVisible';

import ModifiersShape from '../shapes/ModifiersShape';
import NavPositionShape from '../shapes/NavPositionShape';
import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape';
import DayOfWeekShape from '../shapes/DayOfWeekShape';
import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape';
Expand All @@ -41,6 +42,8 @@ import {
INFO_POSITION_BEFORE,
INFO_POSITION_AFTER,
MODIFIER_KEY_NAMES,
NAV_POSITION_TOP,
NAV_POSITION_BOTTOM,
} from '../constants';

const MONTH_PADDING = 23;
Expand Down Expand Up @@ -75,8 +78,10 @@ const propTypes = forbidExtraProps({
renderKeyboardShortcutsPanel: PropTypes.func,

// navigation props
dayPickerNavigationInlineStyles: PropTypes.object,
disablePrev: PropTypes.bool,
disableNext: PropTypes.bool,
navPosition: NavPositionShape,
navPrev: PropTypes.node,
navNext: PropTypes.node,
noNavButtons: PropTypes.bool,
Expand Down Expand Up @@ -138,8 +143,10 @@ export const defaultProps = {
renderKeyboardShortcutsPanel: undefined,

// navigation props
dayPickerNavigationInlineStyles: null,
disablePrev: false,
disableNext: false,
navPosition: NAV_POSITION_TOP,
navPrev: null,
navNext: null,
noNavButtons: false,
Expand Down Expand Up @@ -833,8 +840,10 @@ class DayPicker extends React.PureComponent {

renderNavigation() {
const {
dayPickerNavigationInlineStyles,
disablePrev,
disableNext,
navPosition,
navPrev,
navNext,
noNavButtons,
Expand All @@ -855,8 +864,10 @@ class DayPicker extends React.PureComponent {
<DayPickerNavigation
disablePrev={disablePrev}
disableNext={disableNext}
inlineStyles={dayPickerNavigationInlineStyles}
onPrevMonthClick={this.onPrevMonthClick}
onNextMonthClick={onNextMonthClick}
navPosition={navPosition}
navPrev={navPrev}
navNext={navNext}
orientation={orientation}
Expand Down Expand Up @@ -968,6 +979,7 @@ class DayPicker extends React.PureComponent {
transitionDuration,
verticalBorderSpacing,
horizontalMonthPadding,
navPosition,
} = this.props;

const { reactDates: { spacing: { dayPickerHorizontalPadding } } } = theme;
Expand Down Expand Up @@ -1088,7 +1100,7 @@ class DayPicker extends React.PureComponent {
aria-roledescription={phrases.roleDescription}
aria-label={phrases.calendarLabel}
>
{!verticalScrollable && this.renderNavigation()}
{!verticalScrollable && navPosition === NAV_POSITION_TOP && this.renderNavigation()}

<div
{...css(
Expand Down Expand Up @@ -1135,6 +1147,10 @@ class DayPicker extends React.PureComponent {
{verticalScrollable && this.renderNavigation()}
</div>

{!verticalScrollable
&& navPosition === NAV_POSITION_BOTTOM
&& this.renderNavigation()}

{!isTouch && !hideKeyboardShortcutsPanel && (
<DayPickerKeyboardShortcuts
block={this.isVertical() && !withPortal}
Expand Down
37 changes: 37 additions & 0 deletions src/components/DayPickerNavigation.jsx
Expand Up @@ -11,17 +11,21 @@ import LeftArrow from './LeftArrow';
import RightArrow from './RightArrow';
import ChevronUp from './ChevronUp';
import ChevronDown from './ChevronDown';
import NavPositionShape from '../shapes/NavPositionShape';
import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape';

import {
HORIZONTAL_ORIENTATION,
NAV_POSITION_BOTTOM,
NAV_POSITION_TOP,
VERTICAL_SCROLLABLE,
} from '../constants';

const propTypes = forbidExtraProps({
...withStylesPropTypes,
disablePrev: PropTypes.bool,
disableNext: PropTypes.bool,
navPosition: NavPositionShape,
navPrev: PropTypes.node,
navNext: PropTypes.node,
orientation: ScrollableOrientationShape,
Expand All @@ -32,12 +36,14 @@ const propTypes = forbidExtraProps({
// internationalization
phrases: PropTypes.shape(getPhrasePropTypes(DayPickerNavigationPhrases)),

inlineStyles: PropTypes.object,
isRTL: PropTypes.bool,
});

const defaultProps = {
disablePrev: false,
disableNext: false,
navPosition: NAV_POSITION_TOP,
navPrev: null,
navNext: null,
orientation: HORIZONTAL_ORIENTATION,
Expand All @@ -47,13 +53,17 @@ const defaultProps = {

// internationalization
phrases: DayPickerNavigationPhrases,

inlineStyles: null,
isRTL: false,
};

function DayPickerNavigation({
css,
inlineStyles,
disablePrev,
disableNext,
navPosition,
navPrev,
navNext,
onPrevMonthClick,
Expand All @@ -66,13 +76,16 @@ function DayPickerNavigation({
const isHorizontal = orientation === HORIZONTAL_ORIENTATION;
const isVertical = orientation !== HORIZONTAL_ORIENTATION;
const isVerticalScrollable = orientation === VERTICAL_SCROLLABLE;
const isBottomNavPosition = navPosition === NAV_POSITION_BOTTOM;
const hasInlineStyles = !!inlineStyles;

let navPrevIcon = navPrev;
let navNextIcon = navNext;
let isDefaultNavPrev = false;
let isDefaultNavNext = false;
let navPrevTabIndex = {};
let navNextTabIndex = {};

if (!navPrevIcon) {
navPrevTabIndex = { tabIndex: '0' };
isDefaultNavPrev = true;
Expand Down Expand Up @@ -126,6 +139,11 @@ function DayPickerNavigation({
styles.DayPickerNavigation__verticalScrollable,
isDefaultNav && styles.DayPickerNavigation__verticalScrollableDefault,
] : []),
...(isBottomNavPosition ? [
styles.DayPickerNavigation__bottom,
isDefaultNav && styles.DayPickerNavigation__bottomDefault,
] : []),
hasInlineStyles && inlineStyles,
)}
>
{!isVerticalScrollable && (
Expand All @@ -140,6 +158,7 @@ function DayPickerNavigation({
styles.DayPickerNavigation_button__horizontal,
...(isDefaultNavPrev ? [
styles.DayPickerNavigation_button__horizontalDefault,
isBottomNavPosition && styles.DayPickerNavigation_bottomButton__horizontalDefault,
!isRTL && styles.DayPickerNavigation_leftButton__horizontalDefault,
isRTL && styles.DayPickerNavigation_rightButton__horizontalDefault,
] : []),
Expand Down Expand Up @@ -178,6 +197,7 @@ function DayPickerNavigation({
styles.DayPickerNavigation_button__horizontal,
...(isDefaultNavNext ? [
styles.DayPickerNavigation_button__horizontalDefault,
isBottomNavPosition && styles.DayPickerNavigation_bottomButton__horizontalDefault,
isRTL && styles.DayPickerNavigation_leftButton__horizontalDefault,
!isRTL && styles.DayPickerNavigation_rightButton__horizontalDefault,
] : []),
Expand Down Expand Up @@ -238,6 +258,15 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({
position: 'relative',
},

DayPickerNavigation__bottom: {
height: 'auto',
},

DayPickerNavigation__bottomDefault: {
display: 'flex',
justifyContent: 'space-between',
},

DayPickerNavigation_button: {
cursor: 'pointer',
userSelect: 'none',
Expand Down Expand Up @@ -292,6 +321,14 @@ export default withStyles(({ reactDates: { color, zIndex } }) => ({
padding: '6px 9px',
},

DayPickerNavigation_bottomButton__horizontalDefault: {
position: 'static',
marginLeft: 22,
marginRight: 22,
marginBottom: 30,
marginTop: -10,
},

DayPickerNavigation_leftButton__horizontalDefault: {
left: noflip(22),
},
Expand Down
10 changes: 10 additions & 0 deletions src/components/DayPickerRangeController.jsx
Expand Up @@ -28,6 +28,7 @@ import FocusedInputShape from '../shapes/FocusedInputShape';
import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape';
import DayOfWeekShape from '../shapes/DayOfWeekShape';
import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape';
import NavPositionShape from '../shapes/NavPositionShape';

import {
START_DATE,
Expand All @@ -36,6 +37,7 @@ import {
VERTICAL_SCROLLABLE,
DAY_SIZE,
INFO_POSITION_BOTTOM,
NAV_POSITION_TOP,
} from '../constants';

import DayPicker from './DayPicker';
Expand Down Expand Up @@ -77,6 +79,8 @@ const propTypes = forbidExtraProps({
verticalBorderSpacing: nonNegativeInteger,
horizontalMonthPadding: nonNegativeInteger,

dayPickerNavigationInlineStyles: PropTypes.object,
navPosition: NavPositionShape,
navPrev: PropTypes.node,
navNext: PropTypes.node,
noNavButtons: PropTypes.bool,
Expand Down Expand Up @@ -142,6 +146,8 @@ const defaultProps = {
initialVisibleMonth: null,
daySize: DAY_SIZE,

dayPickerNavigationInlineStyles: null,
navPosition: NAV_POSITION_TOP,
navPrev: null,
navNext: null,
noNavButtons: false,
Expand Down Expand Up @@ -1136,6 +1142,8 @@ export default class DayPickerRangeController extends React.PureComponent {
monthFormat,
renderMonthText,
renderWeekHeaderElement,
dayPickerNavigationInlineStyles,
navPosition,
navPrev,
navNext,
noNavButtons,
Expand Down Expand Up @@ -1202,6 +1210,8 @@ export default class DayPickerRangeController extends React.PureComponent {
onOutsideClick={onOutsideClick}
disablePrev={disablePrev}
disableNext={disableNext}
dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles}
navPosition={navPosition}
navPrev={navPrev}
navNext={navNext}
noNavButtons={noNavButtons}
Expand Down
10 changes: 10 additions & 0 deletions src/components/DayPickerSingleDateController.jsx
Expand Up @@ -20,12 +20,14 @@ import { addModifier, deleteModifier } from '../utils/modifiers';
import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape';
import DayOfWeekShape from '../shapes/DayOfWeekShape';
import CalendarInfoPositionShape from '../shapes/CalendarInfoPositionShape';
import NavPositionShape from '../shapes/NavPositionShape';

import {
HORIZONTAL_ORIENTATION,
VERTICAL_SCROLLABLE,
DAY_SIZE,
INFO_POSITION_BOTTOM,
NAV_POSITION_TOP,
} from '../constants';

import DayPicker from './DayPicker';
Expand Down Expand Up @@ -62,6 +64,8 @@ const propTypes = forbidExtraProps({
transitionDuration: nonNegativeInteger,
horizontalMonthPadding: nonNegativeInteger,

dayPickerNavigationInlineStyles: PropTypes.object,
navPosition: NavPositionShape,
navPrev: PropTypes.node,
navNext: PropTypes.node,

Expand Down Expand Up @@ -119,6 +123,8 @@ const defaultProps = {
transitionDuration: undefined,
horizontalMonthPadding: 13,

dayPickerNavigationInlineStyles: null,
navPosition: NAV_POSITION_TOP,
navPrev: null,
navNext: null,

Expand Down Expand Up @@ -573,6 +579,8 @@ export default class DayPickerSingleDateController extends React.PureComponent {
monthFormat,
renderMonthText,
renderWeekHeaderElement,
dayPickerNavigationInlineStyles,
navPosition,
navPrev,
navNext,
onOutsideClick,
Expand Down Expand Up @@ -626,6 +634,8 @@ export default class DayPickerSingleDateController extends React.PureComponent {
initialVisibleMonth={() => currentMonth}
firstDayOfWeek={firstDayOfWeek}
onOutsideClick={onOutsideClick}
dayPickerNavigationInlineStyles={dayPickerNavigationInlineStyles}
navPosition={navPosition}
navPrev={navPrev}
navNext={navNext}
renderMonthText={renderMonthText}
Expand Down

0 comments on commit bf31594

Please sign in to comment.