Skip to content

Commit 095796e

Browse files
Merge pull request #4288 from Hacker0x01/week-picker-2023
Week picker (2nd attempt)
2 parents fdd514f + 8b5ff3c commit 095796e

File tree

16 files changed

+762
-53
lines changed

16 files changed

+762
-53
lines changed

CONTRIBUTING.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ Start reading our code, and you'll get the hang of it. We optimize for readabili
2828
Local development configuration is pretty snappy. Here's how to get set up:
2929

3030
1. Install/use node >=16.0.0
31+
1. Install/use yarn <=1.x.x
3132
1. Run `yarn link` from project root
3233
1. Run `cd docs-site && yarn link react-datepicker`
3334
1. Run `yarn install` from project root
3435
1. Run `yarn build` from project root (at least the first time, this will get you the `dist` directory that holds the code that will be linked to)
35-
1. Run `yarn start` from project root
36+
1. Run `yarn start` from project root. (This command launches a documentation app and runs it as a simple webserver at http://localhost:3000.)
3637
1. Open new terminal window
38+
1. Run `yarn build-dev` from project root. (This command sets up a development environment that keeps an eye on any file changes. When a file is updated, it auto-builds using the latest code.)
39+
40+
You can run `yarn test` to execute the test suite and linters. To help you develop the component we’ve set up some tests that cover the basic functionality (can be found in `/tests`). Even though we’re big fans of testing, this only covers a small piece of the component. We highly recommend you add tests when you’re adding new functionality.
41+
3742
1. After each JS change run `yarn build:js` in project root
3843
1. After each SCSS change run `yarn run css:dev && yarn run css:modules:dev` in project root

docs-site/src/components/Examples/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import RenderCustomYear from "../../examples/renderCustomYear";
7979
import TimeInput from "../../examples/timeInput";
8080
import StrictParsing from "../../examples/strictParsing";
8181
import MonthPicker from "../../examples/monthPicker";
82+
import WeekPicker from "../../examples/weekPicker";
8283
import monthPickerFullName from "../../examples/monthPickerFullName";
8384
import monthPickerTwoColumns from "../../examples/monthPickerTwoColumns";
8485
import monthPickerFourColumns from "../../examples/monthPickerFourColumns";
@@ -512,6 +513,10 @@ export default class exampleComponents extends React.Component {
512513
title: "Calendar Start day",
513514
component: CalendarStartDay,
514515
},
516+
{
517+
title: "Week Picker",
518+
component: WeekPicker,
519+
},
515520
{
516521
title: "External Form",
517522
component: ExternalForm,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
() => {
2+
const [startDate, setStartDate] = useState(new Date("2021/02/22"));
3+
return (
4+
<DatePicker
5+
selected={startDate}
6+
onChange={(date) => setStartDate(date)}
7+
dateFormat="I/R"
8+
locale="en-GB"
9+
showWeekNumbers
10+
showWeekPicker
11+
/>
12+
);
13+
};

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
| `showTimeSelectOnly` | `bool` | | |
134134
| `showTwoColumnMonthYearPicker` | `bool` | `false` | |
135135
| `showWeekNumbers` | `bool` | | |
136+
| `showWeekPicker` | `bool` | `false` | |
136137
| `showYearDropdown` | `bool` | | |
137138
| `showYearPicker` | `bool` | `false` | |
138139
| `startDate` | `instanceOfDate` | | |

src/calendar.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export default class Calendar extends React.Component {
140140
showFourColumnMonthYearPicker: PropTypes.bool,
141141
showYearPicker: PropTypes.bool,
142142
showQuarterYearPicker: PropTypes.bool,
143+
showWeekPicker: PropTypes.bool,
143144
showTimeSelectOnly: PropTypes.bool,
144145
timeFormat: PropTypes.string,
145146
timeIntervals: PropTypes.number,
@@ -941,6 +942,7 @@ export default class Calendar extends React.Component {
941942
}
942943
showYearPicker={this.props.showYearPicker}
943944
showQuarterYearPicker={this.props.showQuarterYearPicker}
945+
showWeekPicker={this.props.showWeekPicker}
944946
isInputFocused={this.props.isInputFocused}
945947
containerRef={this.containerRef}
946948
monthShowsDuplicateDaysEnd={monthShowsDuplicateDaysEnd}

src/day.jsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
isBefore,
1515
isAfter,
1616
getDayOfWeekCode,
17+
getStartOfWeek,
1718
formatDate,
1819
} from "./date_utils";
1920

@@ -38,6 +39,8 @@ export default class Day extends React.Component {
3839
selectsEnd: PropTypes.bool,
3940
selectsStart: PropTypes.bool,
4041
selectsRange: PropTypes.bool,
42+
showWeekPicker: PropTypes.bool,
43+
showWeekNumber: PropTypes.bool,
4144
selectsDisabledDaysInRange: PropTypes.bool,
4245
startDate: PropTypes.instanceOf(Date),
4346
renderDayContents: PropTypes.func,
@@ -52,6 +55,7 @@ export default class Day extends React.Component {
5255
PropTypes.string,
5356
PropTypes.shape({ locale: PropTypes.object }),
5457
]),
58+
calendarStartDay: PropTypes.number,
5559
};
5660

5761
componentDidMount() {
@@ -90,13 +94,38 @@ export default class Day extends React.Component {
9094

9195
isKeyboardSelected = () =>
9296
!this.props.disabledKeyboardNavigation &&
93-
!this.isSameDay(this.props.selected) &&
94-
this.isSameDay(this.props.preSelection);
97+
!(
98+
this.isSameDay(this.props.selected) ||
99+
this.isSameWeek(this.props.selected)
100+
) &&
101+
(this.isSameDay(this.props.preSelection) ||
102+
this.isSameWeek(this.props.preSelection));
95103

96104
isDisabled = () => isDayDisabled(this.props.day, this.props);
97105

98106
isExcluded = () => isDayExcluded(this.props.day, this.props);
99107

108+
isStartOfWeek = () =>
109+
isSameDay(
110+
this.props.day,
111+
getStartOfWeek(
112+
this.props.day,
113+
this.props.locale,
114+
this.props.calendarStartDay,
115+
),
116+
);
117+
118+
isSameWeek = (other) =>
119+
this.props.showWeekPicker &&
120+
isSameDay(
121+
other,
122+
getStartOfWeek(
123+
this.props.day,
124+
this.props.locale,
125+
this.props.calendarStartDay,
126+
),
127+
);
128+
100129
getHighLightedClass = () => {
101130
const { day, highlightDates } = this.props;
102131

@@ -246,7 +275,8 @@ export default class Day extends React.Component {
246275

247276
isCurrentDay = () => this.isSameDay(newDate());
248277

249-
isSelected = () => this.isSameDay(this.props.selected);
278+
isSelected = () =>
279+
this.isSameDay(this.props.selected) || this.isSameWeek(this.props.selected);
250280

251281
getClassNames = (date) => {
252282
const dayClassName = this.props.dayClassName
@@ -309,10 +339,14 @@ export default class Day extends React.Component {
309339
getTabIndex = (selected, preSelection) => {
310340
const selectedDay = selected || this.props.selected;
311341
const preSelectionDay = preSelection || this.props.preSelection;
312-
313342
const tabIndex =
314-
this.isKeyboardSelected() ||
315-
(this.isSameDay(selectedDay) && isSameDay(preSelectionDay, selectedDay))
343+
!(
344+
this.props.showWeekPicker &&
345+
(this.props.showWeekNumber || !this.isStartOfWeek())
346+
) &&
347+
(this.isKeyboardSelected() ||
348+
(this.isSameDay(selectedDay) &&
349+
isSameDay(preSelectionDay, selectedDay)))
316350
? 0
317351
: -1;
318352

src/index.jsx

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
getHightLightDaysMap,
3838
getYear,
3939
getMonth,
40+
getStartOfWeek,
4041
registerLocale,
4142
setDefaultLocale,
4243
getDefaultLocale,
@@ -108,6 +109,7 @@ export default class DatePicker extends React.Component {
108109
showFourColumnMonthYearPicker: false,
109110
showYearPicker: false,
110111
showQuarterYearPicker: false,
112+
showWeekPicker: false,
111113
strictParsing: false,
112114
timeIntervals: 30,
113115
timeCaption: "Time",
@@ -256,6 +258,7 @@ export default class DatePicker extends React.Component {
256258
showFourColumnMonthYearPicker: PropTypes.bool,
257259
showYearPicker: PropTypes.bool,
258260
showQuarterYearPicker: PropTypes.bool,
261+
showWeekPicker: PropTypes.bool,
259262
showDateSelect: PropTypes.bool,
260263
showTimeSelect: PropTypes.bool,
261264
showTimeSelectOnly: PropTypes.bool,
@@ -552,6 +555,13 @@ export default class DatePicker extends React.Component {
552555
});
553556
}
554557
if (date || !event.target.value) {
558+
if (this.props.showWeekPicker) {
559+
date = getStartOfWeek(
560+
date,
561+
this.props.locale,
562+
this.props.calendarStartDay,
563+
);
564+
}
555565
this.setSelected(date, event, true);
556566
}
557567
};
@@ -565,6 +575,13 @@ export default class DatePicker extends React.Component {
565575
if (this.props.onChangeRaw) {
566576
this.props.onChangeRaw(event);
567577
}
578+
if (this.props.showWeekPicker) {
579+
date = getStartOfWeek(
580+
date,
581+
this.props.locale,
582+
this.props.calendarStartDay,
583+
);
584+
}
568585
this.setSelected(date, event, false, monthSelectedIn);
569586
if (this.props.showDateSelect) {
570587
this.setState({ isRenderAriaLiveMessage: true });
@@ -665,6 +682,13 @@ export default class DatePicker extends React.Component {
665682
const hasMaxDate = typeof this.props.maxDate !== "undefined";
666683
let isValidDateSelection = true;
667684
if (date) {
685+
if (this.props.showWeekPicker) {
686+
date = getStartOfWeek(
687+
date,
688+
this.props.locale,
689+
this.props.calendarStartDay,
690+
);
691+
}
668692
const dateStartOfDay = startOfDay(date);
669693
if (hasMinDate && hasMaxDate) {
670694
// isDayinRange uses startOfDay internally, so not necessary to manipulate times here
@@ -748,16 +772,18 @@ export default class DatePicker extends React.Component {
748772
return;
749773
}
750774

751-
// if calendar is open, these keys will focus the selected day
775+
// if calendar is open, these keys will focus the selected item
752776
if (this.state.open) {
753777
if (eventKey === "ArrowDown" || eventKey === "ArrowUp") {
754778
event.preventDefault();
755-
const selectedDay =
779+
const selectorString =
780+
this.props.showWeekPicker && this.props.showWeekNumbers
781+
? '.react-datepicker__week-number[tabindex="0"]'
782+
: '.react-datepicker__day[tabindex="0"]';
783+
const selectedItem =
756784
this.calendar.componentNode &&
757-
this.calendar.componentNode.querySelector(
758-
'.react-datepicker__day[tabindex="0"]',
759-
);
760-
selectedDay && selectedDay.focus({ preventScroll: true });
785+
this.calendar.componentNode.querySelector(selectorString);
786+
selectedItem && selectedItem.focus({ preventScroll: true });
761787

762788
return;
763789
}
@@ -828,10 +854,18 @@ export default class DatePicker extends React.Component {
828854
let newSelection;
829855
switch (eventKey) {
830856
case "ArrowLeft":
831-
newSelection = subDays(copy, 1);
857+
if (this.props.showWeekPicker) {
858+
newSelection = subWeeks(copy, 1);
859+
} else {
860+
newSelection = subDays(copy, 1);
861+
}
832862
break;
833863
case "ArrowRight":
834-
newSelection = addDays(copy, 1);
864+
if (this.props.showWeekPicker) {
865+
newSelection = addWeeks(copy, 1);
866+
} else {
867+
newSelection = addDays(copy, 1);
868+
}
835869
break;
836870
case "ArrowUp":
837871
newSelection = subWeeks(copy, 1);
@@ -851,6 +885,9 @@ export default class DatePicker extends React.Component {
851885
case "End":
852886
newSelection = addYears(copy, 1);
853887
break;
888+
default:
889+
newSelection = null;
890+
break;
854891
}
855892
if (!newSelection) {
856893
if (this.props.onInputError) {
@@ -1047,6 +1084,7 @@ export default class DatePicker extends React.Component {
10471084
showFourColumnMonthYearPicker={this.props.showFourColumnMonthYearPicker}
10481085
showYearPicker={this.props.showYearPicker}
10491086
showQuarterYearPicker={this.props.showQuarterYearPicker}
1087+
showWeekPicker={this.props.showWeekPicker}
10501088
showPopperArrow={this.props.showPopperArrow}
10511089
excludeScrollbar={this.props.excludeScrollbar}
10521090
handleOnKeyDown={this.props.onKeyDown}

src/month.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export default class Month extends React.Component {
111111
showTwoColumnMonthYearPicker: PropTypes.bool,
112112
showFourColumnMonthYearPicker: PropTypes.bool,
113113
showQuarterYearPicker: PropTypes.bool,
114+
showWeekPicker: PropTypes.bool,
114115
handleOnKeyDown: PropTypes.func,
115116
isInputFocused: PropTypes.bool,
116117
weekAriaLabelPrefix: PropTypes.string,
@@ -330,6 +331,7 @@ export default class Month extends React.Component {
330331
selectsRange={this.props.selectsRange}
331332
selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
332333
showWeekNumber={this.props.showWeekNumbers}
334+
showWeekPicker={this.props.showWeekPicker}
333335
startDate={this.props.startDate}
334336
endDate={this.props.endDate}
335337
dayClassName={this.props.dayClassName}
@@ -719,6 +721,7 @@ export default class Month extends React.Component {
719721
selectsEnd,
720722
showMonthYearPicker,
721723
showQuarterYearPicker,
724+
showWeekPicker,
722725
} = this.props;
723726

724727
return classnames(
@@ -729,6 +732,7 @@ export default class Month extends React.Component {
729732
},
730733
{ "react-datepicker__monthPicker": showMonthYearPicker },
731734
{ "react-datepicker__quarterPicker": showQuarterYearPicker },
735+
{ "react-datepicker__weekPicker": showWeekPicker },
732736
);
733737
};
734738

src/stylesheets/datepicker.scss

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -368,22 +368,45 @@
368368
&.react-datepicker__week-number--clickable {
369369
cursor: pointer;
370370

371-
&:hover {
371+
&:not(
372+
.react-datepicker__week-number--selected,
373+
.react-datepicker__week-number--keyboard-selected
374+
):hover {
372375
border-radius: $datepicker__border-radius;
373376
background-color: $datepicker__background-color;
374377
}
375378
}
376-
}
377379

378-
.react-datepicker__day-names,
379-
.react-datepicker__week {
380-
white-space: nowrap;
380+
&--selected {
381+
border-radius: $datepicker__border-radius;
382+
background-color: $datepicker__selected-color;
383+
color: #fff;
384+
385+
&:hover {
386+
background-color: darken($datepicker__selected-color, 5%);
387+
}
388+
}
389+
390+
&--keyboard-selected {
391+
border-radius: $datepicker__border-radius;
392+
background-color: lighten($datepicker__selected-color, 10%);
393+
color: #fff;
394+
395+
&:hover {
396+
background-color: darken($datepicker__selected-color, 5%);
397+
}
398+
}
381399
}
382400

383401
.react-datepicker__day-names {
402+
white-space: nowrap;
384403
margin-bottom: -8px;
385404
}
386405

406+
.react-datepicker__week {
407+
white-space: nowrap;
408+
}
409+
387410
.react-datepicker__day-name,
388411
.react-datepicker__day,
389412
.react-datepicker__time-name {

0 commit comments

Comments
 (0)