From c00b043455a608df12ccfab29e43b135ad4f901d Mon Sep 17 00:00:00 2001 From: Wojciech Maj Date: Sat, 13 Jul 2019 03:38:16 +1000 Subject: [PATCH 1/3] Implement showDoubleView --- README.md | 1 + src/Calendar.jsx | 24 ++++++++++--- src/Calendar.less | 14 ++++++++ src/Calendar/Navigation.jsx | 67 ++++++++++++++++++++++++------------- test/Test.jsx | 4 +++ test/ViewOptions.jsx | 23 ++++++++++++- 6 files changed, 103 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9e6c7e46..dd4c77cb 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ Displays a complete, interactive calendar. |prev2AriaLabel|`aria-label` attribute of the "previous on higher level" button on the navigation pane.|n/a|`"Jump backwards"`| |prev2Label|Content of the "previous on higher level" button on the navigation pane.| `"«"`|| |returnValue|Which dates shall be passed by the calendar to the onChange function and onClick{Period} functions. Can be `"start"`, `"end"` or `"range"`. The latter will cause an array with start and end values to be passed.|`"start"`|`"range"`| +|showDoubleView|Whether to show two months/years/… at a time instead of one. Forces `showFixedNumberOfWeeks` prop to be `true`.|`false`|`true`| |showFixedNumberOfWeeks|Whether to always show fixed number of weeks (6). Forces `showNeighboringMonth` prop to be `true`.|`false`|`true`| |showNavigation|Whether a navigation bar with arrows and title shall be rendered.|`true`|`false`| |showNeighboringMonth|Whether days from previous or next month shall be rendered if the month doesn't start on the first day of the week or doesn't end on the last day of the week, respectively.|`true`|`false`| diff --git a/src/Calendar.jsx b/src/Calendar.jsx index d31d7f65..c4279120 100644 --- a/src/Calendar.jsx +++ b/src/Calendar.jsx @@ -8,7 +8,9 @@ import DecadeView from './DecadeView'; import YearView from './YearView'; import MonthView from './MonthView'; -import { getBegin, getEnd, getValueRange } from './shared/dates'; +import { + getBegin, getBeginNext, getEnd, getValueRange, +} from './shared/dates'; import { isCalendarType, isClassName, isMaxDate, isMinDate, isValue, isView, } from './shared/propTypes'; @@ -352,9 +354,9 @@ export default class Calendar extends Component { this.setState({ hover: null }); } - renderContent() { + renderContent(next) { const { - activeStartDate, + activeStartDate: currentActiveStartDate, onMouseOver, valueType, value, @@ -373,6 +375,12 @@ export default class Calendar extends Component { } = this.props; const { hover } = this; + const activeStartDate = ( + next + ? getBeginNext(view, currentActiveStartDate) + : currentActiveStartDate + ); + const commonProps = { activeStartDate, hover, @@ -428,6 +436,7 @@ export default class Calendar extends Component { formatShortWeekday, onClickDay, onClickWeekNumber, + showDoubleView, showFixedNumberOfWeeks, showNeighboringMonth, showWeekNumbers, @@ -441,7 +450,7 @@ export default class Calendar extends Component { onClick={mergeFunctions(clickAction, onClickDay)} onClickWeekNumber={onClickWeekNumber} onMouseLeave={onMouseLeave} - showFixedNumberOfWeeks={showFixedNumberOfWeeks} + showFixedNumberOfWeeks={showFixedNumberOfWeeks || showDoubleView} showNeighboringMonth={showNeighboringMonth} showWeekNumbers={showWeekNumbers} {...commonProps} @@ -479,6 +488,7 @@ export default class Calendar extends Component { prev2Label, prevAriaLabel, prevLabel, + showDoubleView, } = this.props; return ( @@ -501,6 +511,7 @@ export default class Calendar extends Component { prevAriaLabel={prevAriaLabel} prevLabel={prevLabel} setActiveStartDate={this.setActiveStartDate} + showDoubleView={showDoubleView} view={view} views={getLimitedViews(minDetail, maxDetail)} /> @@ -508,7 +519,7 @@ export default class Calendar extends Component { } render() { - const { className, selectRange } = this.props; + const { className, selectRange, showDoubleView } = this.props; const { onMouseLeave, value } = this; const valueArray = [].concat(value); @@ -517,6 +528,7 @@ export default class Calendar extends Component { className={mergeClassNames( baseClassName, selectRange && valueArray.length === 1 && `${baseClassName}--selectRange`, + showDoubleView && `${baseClassName}--doubleView`, className, )} > @@ -527,6 +539,7 @@ export default class Calendar extends Component { onMouseLeave={selectRange ? onMouseLeave : null} > {this.renderContent()} + {showDoubleView && this.renderContent(true)} ); @@ -586,6 +599,7 @@ Calendar.propTypes = { renderChildren: PropTypes.func, // For backwards compatibility returnValue: PropTypes.oneOf(['start', 'end', 'range']), selectRange: PropTypes.bool, + showDoubleView: PropTypes.bool, showFixedNumberOfWeeks: PropTypes.bool, showNavigation: PropTypes.bool, showNeighboringMonth: PropTypes.bool, diff --git a/src/Calendar.less b/src/Calendar.less index b7af7e2e..cb3db80b 100644 --- a/src/Calendar.less +++ b/src/Calendar.less @@ -6,6 +6,20 @@ font-family: Arial, Helvetica, sans-serif; line-height: 1.125em; + &--doubleView { + width: 700px; + + .react-calendar__viewContainer { + display: flex; + margin: -.5em; + + > * { + width: 50%; + margin: .5em; + } + } + } + &, & *, & *:before, & *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; diff --git a/src/Calendar/Navigation.jsx b/src/Calendar/Navigation.jsx index ba65b9be..c8547172 100644 --- a/src/Calendar/Navigation.jsx +++ b/src/Calendar/Navigation.jsx @@ -20,7 +20,7 @@ import { isView, isViews } from '../shared/propTypes'; const className = 'react-calendar__navigation'; export default function Navigation({ - activeStartDate: date, + activeStartDate, drillUp, formatMonthYear, formatYear, @@ -38,22 +38,26 @@ export default function Navigation({ prevAriaLabel, prevLabel, setActiveStartDate, + showDoubleView, view, views, }) { const drillUpAvailable = views.indexOf(view) > 0; const shouldShowPrevNext2Buttons = view !== 'century'; - const previousActiveStartDate = getBeginPrevious(view, date); - const previousActiveStartDate2 = shouldShowPrevNext2Buttons && getBeginPrevious2(view, date); - const nextActiveStartDate = getBeginNext(view, date); - const nextActiveStartDate2 = shouldShowPrevNext2Buttons && getBeginNext2(view, date); + const previousActiveStartDate = getBeginPrevious(view, activeStartDate); + const previousActiveStartDate2 = ( + shouldShowPrevNext2Buttons + && getBeginPrevious2(view, activeStartDate) + ); + const nextActiveStartDate = getBeginNext(view, activeStartDate); + const nextActiveStartDate2 = shouldShowPrevNext2Buttons && getBeginNext2(view, activeStartDate); const prevButtonDisabled = (() => { if (previousActiveStartDate.getFullYear() < 1000) { return true; } - const previousActiveEndDate = getEndPrevious(view, date); + const previousActiveEndDate = getEndPrevious(view, activeStartDate); return minDate && minDate >= previousActiveEndDate; })(); @@ -61,7 +65,7 @@ export default function Navigation({ if (previousActiveStartDate2.getFullYear() < 1000) { return true; } - const previousActiveEndDate = getEndPrevious2(view, date); + const previousActiveEndDate = getEndPrevious2(view, activeStartDate); return minDate && minDate >= previousActiveEndDate; })(); @@ -89,20 +93,28 @@ export default function Navigation({ setActiveStartDate(nextActiveStartDate2); } - const label = (() => { - switch (view) { - case 'century': - return getCenturyLabel(locale, formatYear, date); - case 'decade': - return getDecadeLabel(locale, formatYear, date); - case 'year': - return formatYear(locale, date); - case 'month': - return formatMonthYear(locale, date); - default: - throw new Error(`Invalid view: ${view}.`); - } - })(); + function renderLabel(date) { + const label = (() => { + switch (view) { + case 'century': + return getCenturyLabel(locale, formatYear, date); + case 'decade': + return getDecadeLabel(locale, formatYear, date); + case 'year': + return formatYear(locale, date); + case 'month': + return formatMonthYear(locale, date); + default: + throw new Error(`Invalid view: ${view}.`); + } + })(); + + return ( + navigationLabel + ? navigationLabel({ date, view, label }) + : label + ); + } return (
- {navigationLabel - ? navigationLabel({ date, view, label }) - : label} + {renderLabel(activeStartDate)} + {showDoubleView && ( + <> + {' '} + – + {' '} + {renderLabel(nextActiveStartDate)} + + )}
+ +
+ Date: Sat, 13 Jul 2019 03:45:04 +1000 Subject: [PATCH 2/3] Fix tests for Navigation component --- src/Calendar/__tests__/Navigation.jsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Calendar/__tests__/Navigation.jsx b/src/Calendar/__tests__/Navigation.jsx index fa8ac1c1..47ff1575 100644 --- a/src/Calendar/__tests__/Navigation.jsx +++ b/src/Calendar/__tests__/Navigation.jsx @@ -63,9 +63,9 @@ describe('Navigation', () => { /> ); - const [, , drillUp] = component.children(); + const drillUp = component.find('.react-calendar__navigation__label'); - expect(drillUp.props.children.toString()).toBe('January 2017'); + expect(drillUp.text()).toBe('January 2017'); }); it('displays proper title for year view', () => { @@ -79,9 +79,9 @@ describe('Navigation', () => { /> ); - const [, , drillUp] = component.children(); + const drillUp = component.find('.react-calendar__navigation__label'); - expect(drillUp.props.children.toString()).toBe('2017'); + expect(drillUp.text()).toBe('2017'); }); it('displays proper title for decade view', () => { @@ -95,9 +95,9 @@ describe('Navigation', () => { /> ); - const [, , drillUp] = component.children(); + const drillUp = component.find('.react-calendar__navigation__label'); - expect(drillUp.props.children.toString()).toBe('2011 – 2020'); + expect(drillUp.text()).toBe('2011 – 2020'); }); it('displays proper title for century view', () => { @@ -111,9 +111,9 @@ describe('Navigation', () => { /> ); - const [, drillUp] = component.children(); + const drillUp = component.find('.react-calendar__navigation__label'); - expect(drillUp.props.children.toString()).toBe('2001 – 2100'); + expect(drillUp.text()).toBe('2001 – 2100'); }); it('displays proper user-defined labels on prev2, prev, next and next2 buttons', () => { @@ -522,9 +522,9 @@ describe('Navigation', () => { /> ); - const [, , drillUp] = component.children(); + const drillUp = component.find('.react-calendar__navigation__label'); expect(navigationLabel).toHaveBeenCalledWith({ date, view, label: 'January 2017' }); - expect(drillUp.props.children.toString()).toBe(label); + expect(drillUp.text()).toBe(label); }); }); From f086b89fb3c17afebd472c6860c44d8f8cdc2cb7 Mon Sep 17 00:00:00 2001 From: Wojciech Maj Date: Sat, 13 Jul 2019 03:49:44 +1000 Subject: [PATCH 3/3] Add unit tests for showDoubleView --- src/Calendar/__tests__/Navigation.jsx | 17 +++++++++++++ src/__tests__/Calendar.jsx | 36 +++++++++++++++++++++------ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Calendar/__tests__/Navigation.jsx b/src/Calendar/__tests__/Navigation.jsx index 47ff1575..609674da 100644 --- a/src/Calendar/__tests__/Navigation.jsx +++ b/src/Calendar/__tests__/Navigation.jsx @@ -116,6 +116,23 @@ describe('Navigation', () => { expect(drillUp.text()).toBe('2001 – 2100'); }); + it('displays proper title for month view given showDouble flags is set to true', () => { + const component = shallow( + + ); + + const drillUp = component.find('.react-calendar__navigation__label'); + + expect(drillUp.text()).toBe('January 2017 – February 2017'); + }); + it('displays proper user-defined labels on prev2, prev, next and next2 buttons', () => { const component = shallow( { expect(monthView).toHaveLength(1); }); - it('renders maximum allowed view when given maxDetail', () => { + it('renders maximum allowed view given maxDetail', () => { const component = mount( ); @@ -49,7 +49,7 @@ describe('Calendar', () => { expect(yearView).toHaveLength(1); }); - it('renders maximum allowed view when given view that is not allowed', () => { + it('renders maximum allowed view given view that is not allowed', () => { const component = mount( { expect(yearView).toHaveLength(1); }); - it('renders maximum allowed view when given changed maxDetail', () => { + it('renders maximum allowed view given changed maxDetail', () => { const component = mount( { expect(yearView).toHaveLength(1); }); - it('renders month view when given view = "month"', () => { + it('renders month view given view = "month"', () => { const component = mount( ); @@ -104,7 +104,7 @@ describe('Calendar', () => { expect(monthView).toHaveLength(1); }); - it('renders month view with week numbers when given view = "month" and showWeekNumbers flag set to true', () => { + it('renders month view with week numbers given view = "month" and showWeekNumbers flag set to true', () => { const component = mount( { expect(monthView).toHaveLength(1); }); - it('renders year view when given view = "year"', () => { + it('renders year view given view = "year"', () => { const component = mount( ); @@ -127,7 +127,7 @@ describe('Calendar', () => { expect(yearView).toHaveLength(1); }); - it('renders decade view when given view = "decade"', () => { + it('renders decade view given view = "decade"', () => { const component = mount( ); @@ -137,7 +137,7 @@ describe('Calendar', () => { expect(decadeView).toHaveLength(1); }); - it('renders century view when given view = "century"', () => { + it('renders century view given view = "century"', () => { const component = mount( ); @@ -193,6 +193,26 @@ describe('Calendar', () => { expect(firstDayTileTimeAbbr).toBe(format(beginOfCurrentMonth)); }); + it('renders two views given showDoubleView flag is set to true', () => { + const component = mount( + + ); + + const monthView = component.find('.react-calendar__month-view'); + + expect(monthView).toHaveLength(2); + }); + + it('passes showDoubleView flag to Navigation', () => { + const component = mount( + + ); + + const navigation = component.find('Navigation'); + + expect(navigation.prop('showDoubleView')).toBeTruthy(); + }); + it('drills up when allowed', () => { const component = mount(