Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## v3.1

- component internally redesigned to use locale of date-fns with `locale` prop
## v3.0

- component internally redesigned to use CSS Grid rather than Flexbox, resulting in cleaner markup and CSS
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ To customize the UI, you can either:

**default value**: `'4 px'`

#### `locale`

**type**: `string`

**description**: The locale used by date-fns. It should have the name of the folder https://github.com/date-fns/date-fns/tree/master/src/locale

**required**: no

**default value**: `'en-GB'`

#### `unselectedColor`

**type**: `string`
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-schedule-selector",
"version": "3.0.0",
"version": "3.1.0",
"description": "A mobile-friendly when2meet-style grid-based schedule selector",
"author": "Bibek Ghimire",
"repository": {
Expand Down Expand Up @@ -45,7 +45,7 @@
"@emotion/core": "^10.0.27",
"@emotion/styled": "^10.0.27",
"@types/jest": "^26.0.15",
"date-fns": "^1.29.0",
"date-fns": "^2.28.0",
"src": "^1.1.2"
},
"devDependencies": {
Expand Down Expand Up @@ -117,4 +117,4 @@
},
"verbose": false
}
}
}
4 changes: 4 additions & 0 deletions src/lib/ScheduleSelector.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { Locale } from 'date-fns';
import { SelectionSchemeType, SelectionType } from './selection-schemes';
export declare const GridCell: import("styled-components").StyledComponent<"div", any, {}, never>;
declare type PropsType = {
Expand All @@ -14,6 +15,7 @@ declare type PropsType = {
timeFormat: string;
columnGap: string;
rowGap: string;
locale: string;
unselectedColor: string;
selectedColor: string;
hoveredColor: string;
Expand All @@ -25,6 +27,7 @@ declare type StateType = {
selectionDraft: Array<Date>;
selectionType: SelectionType | null;
selectionStart: Date | null;
locale: Locale | null;
isTouchDragging: boolean;
dates: Array<Array<Date>>;
};
Expand All @@ -40,6 +43,7 @@ export default class ScheduleSelector extends React.Component<PropsType, StateTy
static computeDatesMatrix(props: PropsType): Array<Array<Date>>;
constructor(props: PropsType);
componentDidMount(): void;
componentDidUpdate(prevProps: PropsType, prevState: StateType): void;
componentWillUnmount(): void;
getTimeFromTouchEvent(event: React.TouchEvent<any>): Date | null;
endSelection(): void;
Expand Down
42 changes: 30 additions & 12 deletions src/lib/ScheduleSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import * as React from 'react'
import styled from 'styled-components'

// Import only the methods we need from date-fns in order to keep build size small
import addMinutes from 'date-fns/add_minutes'
import addHours from 'date-fns/add_hours'
import addDays from 'date-fns/add_days'
import startOfDay from 'date-fns/start_of_day'
import isSameMinute from 'date-fns/is_same_minute'
import { addMinutes, addHours, addDays, startOfDay, isSameMinute } from 'date-fns'
import formatDate from 'date-fns/format'
import { enGB, es } from 'date-fns/locale'
import { Locale } from 'date-fns'

import { Text, Subtitle } from './typography'
import colors from './colors'
Expand Down Expand Up @@ -79,6 +77,7 @@ type PropsType = {
timeFormat: string
columnGap: string
rowGap: string
locale: string
unselectedColor: string
selectedColor: string
hoveredColor: string
Expand All @@ -93,6 +92,7 @@ type StateType = {
selectionDraft: Array<Date>
selectionType: SelectionType | null
selectionStart: Date | null
locale: Locale | null
isTouchDragging: boolean
dates: Array<Array<Date>>
}
Expand Down Expand Up @@ -122,23 +122,27 @@ export default class ScheduleSelector extends React.Component<PropsType, StateTy
hourlyChunks: 1,
startDate: new Date(),
timeFormat: 'ha',
dateFormat: 'M/D',
dateFormat: 'M/d',
columnGap: '4px',
rowGap: '4px',
locale: 'en-GB',
selectedColor: colors.blue,
unselectedColor: colors.paleBlue,
hoveredColor: colors.lightBlue,
onChange: () => {}
}

static getDerivedStateFromProps(props: PropsType, state: StateType): Partial<StateType> | null {
const updatedState: Partial<StateType> = {}

// As long as the user isn't in the process of selecting, allow prop changes to re-populate selection state
if (state.selectionStart == null) {
return {
selectionDraft: [...props.selection],
dates: ScheduleSelector.computeDatesMatrix(props)
}
updatedState.selectionDraft = [...props.selection]
updatedState.dates = ScheduleSelector.computeDatesMatrix(props)
}

if (Object.keys(updatedState).length !== 0) return updatedState

return null
}

Expand Down Expand Up @@ -166,6 +170,7 @@ export default class ScheduleSelector extends React.Component<PropsType, StateTy
selectionType: null,
selectionStart: null,
isTouchDragging: false,
locale: null,
dates: ScheduleSelector.computeDatesMatrix(props)
}

Expand All @@ -183,6 +188,11 @@ export default class ScheduleSelector extends React.Component<PropsType, StateTy
}

componentDidMount() {
if (this.props.locale === 'en-GB' && !this.state.locale) {
this.setState({ locale: enGB })
} else if (this.props.locale === 'es' && !this.state.locale) {
this.setState({ locale: es })
}
// We need to add the endSelection event listener to the document itself in order
// to catch the cases where the users ends their mouse-click somewhere besides
// the date cells (in which case none of the DateCell's onMouseUp handlers would fire)
Expand All @@ -200,6 +210,14 @@ export default class ScheduleSelector extends React.Component<PropsType, StateTy
})
}

componentDidUpdate(prevProps: PropsType, prevState: StateType) {
if (this.props.locale === 'en-GB' && !this.state.locale) {
this.setState({ locale: enGB })
} else if (this.props.locale === 'es' && !this.state.locale) {
this.setState({ locale: es })
}
}

componentWillUnmount() {
document.removeEventListener('mouseup', this.endSelection)
this.cellToDate.forEach((value, dateCell) => {
Expand Down Expand Up @@ -361,15 +379,15 @@ export default class ScheduleSelector extends React.Component<PropsType, StateTy
if (this.props.renderTimeLabel) {
return this.props.renderTimeLabel(time)
} else {
return <TimeText>{formatDate(time, this.props.timeFormat)}</TimeText>
return <TimeText>{formatDate(time, this.props.timeFormat, { locale: this.state.locale || enGB })}</TimeText>
}
}

renderDateLabel = (date: Date): JSX.Element => {
if (this.props.renderDateLabel) {
return this.props.renderDateLabel(date)
} else {
return <DateLabel>{formatDate(date, this.props.dateFormat)}</DateLabel>
return <DateLabel>{formatDate(date, this.props.dateFormat, { locale: this.state.locale || enGB })}</DateLabel>
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/lib/date-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import startOfDay from 'date-fns/start_of_day'
import isAfter from 'date-fns/is_after'
import { startOfDay, isAfter } from 'date-fns'

// Helper function that uses date-fns methods to determine if a date is between two other dates
export const dateHourIsBetween = (start: Date, candidate: Date, end: Date): boolean =>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/selection-schemes/linear.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import isBefore from 'date-fns/is_before'
import { isBefore } from 'date-fns'

import * as dateUtils from '../date-utils'

Expand Down
3 changes: 1 addition & 2 deletions src/lib/selection-schemes/square.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import isBefore from 'date-fns/is_before'
import startOfDay from 'date-fns/start_of_day'
import { isBefore, startOfDay } from 'date-fns'

import * as dateUtils from '../date-utils'

Expand Down
4 changes: 2 additions & 2 deletions test/lib/ScheduleSelector.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ describe('ScheduleSelector', () => {
.at(1)
.render()
.text()
).toEqual('1:15am')
).toEqual('1:15AM')
// 5-minute resolution
const componentTwo = shallow(<ScheduleSelector minTime={1} maxTime={2} timeFormat="h:mma" hourlyChunks={12} />)
expect(
Expand All @@ -325,7 +325,7 @@ describe('ScheduleSelector', () => {
.at(1)
.render()
.text()
).toEqual('1:05am')
).toEqual('1:05AM')
})
})
})
28 changes: 14 additions & 14 deletions test/lib/__snapshots__/ScheduleSelector.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1353,7 +1353,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
9am
9AM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1433,7 +1433,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
10am
10AM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1513,7 +1513,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
11am
11AM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1593,7 +1593,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
12pm
12PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1673,7 +1673,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
1pm
1PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1753,7 +1753,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
2pm
2PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1833,7 +1833,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
3pm
3PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1913,7 +1913,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
4pm
4PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -1993,7 +1993,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
5pm
5PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -2073,7 +2073,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
6pm
6PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -2153,7 +2153,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
7pm
7PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -2233,7 +2233,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
8pm
8PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -2313,7 +2313,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
9pm
9PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down Expand Up @@ -2393,7 +2393,7 @@ exports[`ScheduleSelector snapshot tests renders correctly with default render l
<p
className="c4 c5"
>
10pm
10PM
</p>
<div
className="c6 rgdp__grid-cell"
Expand Down
Loading