diff --git a/package.json b/package.json index b236adb15..7a6d65764 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "react-date-range", - "version": "1.3.0", + "name": "605-react-date-range", + "version": "1.3.2", "description": "A React component for choosing dates and date ranges.", "main": "dist/index.js", "scripts": { diff --git a/src/components/Calendar/index.js b/src/components/Calendar/index.js index 22541d56b..8b6c995ad 100644 --- a/src/components/Calendar/index.js +++ b/src/components/Calendar/index.js @@ -1,12 +1,4 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { rangeShape } from '../DayCell'; -import Month from '../Month'; -import DateInput from '../DateInput'; -import { calcFocusDate, generateStyles, getMonthDisplayRange } from '../../utils'; import classnames from 'classnames'; -import ReactList from 'react-list'; -import { shallowEqualObjects } from 'shallow-equal'; import { addMonths, format, @@ -27,14 +19,26 @@ import { max, } from 'date-fns'; import defaultLocale from 'date-fns/locale/en-US'; -import coreStyles from '../../styles'; +import PropTypes from 'prop-types'; +import React, { PureComponent } from 'react'; +import ReactList from 'react-list'; +import { shallowEqualObjects } from 'shallow-equal'; import { ariaLabelsShape } from '../../accessibility'; +import coreStyles from '../../styles'; +import { calcFocusDate, generateStyles, getMonthDisplayRange } from '../../utils'; +import DateInput from '../DateInput'; +import { rangeShape } from '../DayCell'; +import Month from '../Month'; class Calendar extends PureComponent { constructor(props, context) { super(props, context); this.dateOptions = { locale: props.locale }; - if (props.weekStartsOn !== undefined) this.dateOptions.weekStartsOn = props.weekStartsOn; + if (props.broadcastCalendar) { + this.dateOptions.weekStartsOn = 1; + } else if (props.weekStartsOn !== undefined) { + this.dateOptions.weekStartsOn = props.weekStartsOn; + }; this.styles = generateStyles([coreStyles, props.classNames]); this.listSizeCache = {}; this.isFirstRender = true; @@ -122,7 +126,10 @@ class Calendar extends PureComponent { date: 'date', }; const targetProp = propMapper[this.props.displayMode]; - if (this.props[targetProp] !== prevProps[targetProp]) { + if ( + this.props[targetProp] !== prevProps[targetProp] || + this.props.focusedRange != prevProps.focusedRange + ) { this.updateShownDate(this.props); } @@ -526,6 +533,7 @@ Calendar.defaultProps = { dateDisplayFormat: 'MMM d, yyyy', monthDisplayFormat: 'MMM yyyy', weekdayDisplayFormat: 'E', + broadcastCalendar: false, dayDisplayFormat: 'd', showDateDisplay: true, showPreview: true, @@ -545,6 +553,7 @@ Calendar.defaultProps = { dragSelectionEnabled: true, fixedHeight: false, ariaLabels: {}, + weekNumberColor: '#B86EF3', }; Calendar.propTypes = { @@ -572,6 +581,8 @@ Calendar.propTypes = { monthDisplayFormat: PropTypes.string, weekdayDisplayFormat: PropTypes.string, weekStartsOn: PropTypes.number, + broadcastCalendar: PropTypes.bool, + weekNumberColor: PropTypes.string, dayDisplayFormat: PropTypes.string, focusedRange: PropTypes.arrayOf(PropTypes.number), initialFocusedRange: PropTypes.arrayOf(PropTypes.number), diff --git a/src/components/DayCell/index.js b/src/components/DayCell/index.js index 26f12539e..860360bc2 100644 --- a/src/components/DayCell/index.js +++ b/src/components/DayCell/index.js @@ -1,8 +1,8 @@ /* eslint-disable no-fallthrough */ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import classnames from 'classnames'; import { startOfDay, format, isSameDay, isAfter, isBefore, endOfDay } from 'date-fns'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; class DayCell extends Component { constructor(props, context) { @@ -58,6 +58,7 @@ class DayCell extends Component { }; getClassNames = () => { const { + broadcastCalendar, isPassive, isToday, isWeekend, @@ -76,7 +77,7 @@ class DayCell extends Component { [styles.dayWeekend]: isWeekend, [styles.dayStartOfWeek]: isStartOfWeek, [styles.dayEndOfWeek]: isEndOfWeek, - [styles.dayStartOfMonth]: isStartOfMonth, + [styles.dayStartOfMonth]: isStartOfMonth && !broadcastCalendar, [styles.dayEndOfMonth]: isEndOfMonth, [styles.dayHovered]: this.state.hover, [styles.dayActive]: this.state.active, @@ -128,7 +129,7 @@ class DayCell extends Component { ...result, { isStartEdge, - isEndEdge: isEndEdge, + isEndEdge, isInRange, ...range, }, @@ -166,21 +167,32 @@ class DayCell extends Component { onKeyUp={this.handleKeyEvent} className={this.getClassNames(this.props.styles)} {...(this.props.disabled || this.props.isPassive ? { tabIndex: -1 } : {})} - style={{ color: this.props.color }}> + style={{ + color: this.props.color, + backgroundColor: this.props.weekNumber ? '#fff' : '', + width: this.props.broadcastCalendar ? 'calc(100% / 8)' : 'calc(100% / 7)', + }}> {this.renderSelectionPlaceholders()} {this.renderPreviewPlaceholder()} - - { - dayContentRenderer?.(this.props.day) || - {format(this.props.day, this.props.dayDisplayFormat)} - } - + {this.props.weekNumber + ? ( + {this.props.weekNumber} + ) + : ( + { + dayContentRenderer?.(this.props.day) || + {format(this.props.day, this.props.dayDisplayFormat)} + } + ) + } ); } } -DayCell.defaultProps = {}; +DayCell.defaultProps = { + weekNumberColor: '#B86EF3', +}; export const rangeShape = PropTypes.shape({ startDate: PropTypes.object, @@ -219,6 +231,8 @@ DayCell.propTypes = { onMouseUp: PropTypes.func, onMouseEnter: PropTypes.func, dayContentRenderer: PropTypes.func, + weekNumber: PropTypes.number, + weekNumberColor: PropTypes.string, }; export default DayCell; diff --git a/src/components/Month/index.js b/src/components/Month/index.js index 6f7802636..ba8b7e650 100644 --- a/src/components/Month/index.js +++ b/src/components/Month/index.js @@ -1,7 +1,4 @@ /* eslint-disable no-fallthrough */ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import DayCell, { rangeShape } from '../DayCell'; import { format, startOfDay, @@ -15,20 +12,41 @@ import { isWithinInterval, eachDayOfInterval, } from 'date-fns'; -import { getMonthDisplayRange } from '../../utils'; +import PropTypes from 'prop-types'; +import React, { PureComponent } from 'react'; +import { + getMonthDisplayRange, + calculateBroadcastWeekNumber, + shouldRenderBroadcastDay +} from '../../utils'; +import DayCell, { rangeShape } from '../DayCell'; -function renderWeekdays(styles, dateOptions, weekdayDisplayFormat) { +function renderWeekdays(styles, dateOptions, weekdayDisplayFormat, broadcastCalendar) { const now = new Date(); return (
{eachDayOfInterval({ start: startOfWeek(now, dateOptions), end: endOfWeek(now, dateOptions), - }).map((day, i) => ( - - {format(day, weekdayDisplayFormat, dateOptions)} - - ))} + }).map((day, i) => { + if (i === 0 && broadcastCalendar) { + return ( + <> + + # + + + {format(day, weekdayDisplayFormat, dateOptions)} + + + ); + } + return ( + + {format(day, weekdayDisplayFormat, dateOptions)} + + ); + })}
); } @@ -57,6 +75,7 @@ class Month extends PureComponent { }); } const showPreview = this.props.showPreview && !drag.disablePreview; + const indexToAddWeekNumber = [0, 7, 14, 21, 28, 35]; return (
{this.props.showMonthName ? ( @@ -65,7 +84,7 @@ class Month extends PureComponent {
) : null} {this.props.showWeekDays && - renderWeekdays(styles, this.props.dateOptions, this.props.weekdayDisplayFormat)} + renderWeekdays(styles, this.props.dateOptions, this.props.weekdayDisplayFormat, this.props.broadcastCalendar)}
{eachDayOfInterval({ start: monthDisplay.start, end: monthDisplay.end }).map( (day, index) => { @@ -77,6 +96,45 @@ class Month extends PureComponent { isSameDay(disabledDate, day) ); const isDisabledDay = disabledDay(day); + if (this.props.broadcastCalendar && !shouldRenderBroadcastDay(day, this.props.month.getMonth())) { + return null; + } + if (indexToAddWeekNumber.includes(index) && this.props.broadcastCalendar) { + const weekNumber = calculateBroadcastWeekNumber(day); + return ( + <> + + + + ); + } return ( { + const ranges = [ + { + startDate: new Date(2021, 0, 10), + endDate: new Date(2022, 10, 20), + }, + ]; + + describe('when focusedRange[1] equals 0', () => { + test('should return startDate', () => { + expect( + calcFocusDate(new Date(), { + ranges, + focusedRange: [0, 0], + displayMode: 'dateRange', + }) + ).toEqual(startOfMonth(ranges[0].startDate)); + }); + }); + + describe('when focusedRange[1] equals 1', () => { + test('should return endDate', () => { + expect( + calcFocusDate(new Date(), { + ranges, + focusedRange: [0, 1], + displayMode: 'dateRange', + }) + ).toEqual(endOfMonth(ranges[0].endDate)); + }); + }); +}); + +describe('calculateBroadcastWeekNumber', () => { + it('should return 9 for 2/21/2022', () => { + const result = calculateBroadcastWeekNumber(new Date('2/21/2022')); + expect(result).toBe(9); + }); + + it('should return 8 for 2/20/2022', () => { + const result = calculateBroadcastWeekNumber(new Date('2/20/2022')); + expect(result).toBe(8); + }); + + it('should return 1 for 01/02/2022', () => { + const result = calculateBroadcastWeekNumber(new Date('01/02/2022')); + expect(result).toBe(1); + }); + + it('should return 2 for 01/03/2022', () => { + const result = calculateBroadcastWeekNumber(new Date('01/03/2022')); + expect(result).toBe(2); + }); + + it('should return 1 for 12/27/2021', () => { + const result = calculateBroadcastWeekNumber(new Date('12/27/2021')); + expect(result).toBe(1); + }); + + it('should return 52 for 12/25/2022', () => { + const result = calculateBroadcastWeekNumber(new Date('12/25/2022')); + expect(result).toBe(52); + }); + + it('should return 1 for 12/26/2022', () => { + const result = calculateBroadcastWeekNumber(new Date('12/26/2022')); + expect(result).toBe(1); + }); + + it('should return 1 for 01/01/2023', () => { + const result = calculateBroadcastWeekNumber(new Date('01/01/2023')); + expect(result).toBe(1); + }); + + it('should return 52 for 12/24/2023', () => { + const result = calculateBroadcastWeekNumber(new Date('12/24/2023')); + expect(result).toBe(52); + }); + + it('should return 53 for 12/25/2023', () => { + const result = calculateBroadcastWeekNumber(new Date('12/25/2023')); + expect(result).toBe(53); + }); +}); + +describe('shouldRenderBroadcastDay', () => { + // broadcast week starts on Mondays and finishes on Sundays + const JanuaryMonthNumber = 0; + const MarchMonthNumber = 2; + const AprilMonthNumber = 3; + const MayMonthNumber = 4; + + describe('5th broadcast week days', () => { + it('should be rendered if last day of that week does not belong to next month', () => { + expect(shouldRenderBroadcastDay(new Date('01/24/2022'), JanuaryMonthNumber)).toBeTruthy(); + expect(shouldRenderBroadcastDay(new Date('01/25/2022'), JanuaryMonthNumber)).toBeTruthy(); + expect(shouldRenderBroadcastDay(new Date('01/26/2022'), JanuaryMonthNumber)).toBeTruthy(); + expect(shouldRenderBroadcastDay(new Date('01/27/2022'), JanuaryMonthNumber)).toBeTruthy(); + expect(shouldRenderBroadcastDay(new Date('01/28/2022'), JanuaryMonthNumber)).toBeTruthy(); + expect(shouldRenderBroadcastDay(new Date('01/29/2022'), JanuaryMonthNumber)).toBeTruthy(); + expect(shouldRenderBroadcastDay(new Date('01/30/2022'), JanuaryMonthNumber)).toBeTruthy(); + }); + + it('should not be rendered if last day of that week belongs to next month', () => { + expect(shouldRenderBroadcastDay(new Date('04/26/2022'), AprilMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/27/2022'), AprilMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/28/2022'), AprilMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/29/2022'), AprilMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/30/2022'), AprilMonthNumber)).toBeFalsy(); + }); + }); + + describe('6th broadcast week days', () => { + it('should not be rendered', () => { + expect(shouldRenderBroadcastDay(new Date('01/31/2022'), JanuaryMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('05/30/2022'), MayMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('05/31/2022'), MayMonthNumber)).toBeFalsy(); + }); + }); + + describe('1st broadcast week days', () => { + it('should not be rendered on previous month', () => { + expect(shouldRenderBroadcastDay(new Date('03/30/2022'), MarchMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('03/31/2022'), MarchMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/01/2022'), MarchMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/02/2022'), MarchMonthNumber)).toBeFalsy(); + expect(shouldRenderBroadcastDay(new Date('04/03/2022'), MarchMonthNumber)).toBeFalsy(); + }); + }); +});