diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e15c9510..266e86b20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,9 +26,10 @@ Local development configuration is pretty snappy. Here's how to get set up: 1. Install/use node >=11.10.1 2. Run `yarn link` from project root -3. Run `cd docs-sites && yarn link react-datepicker` -4. Run `yarn install` from project root -5. Run `yarn start` from project root -6. Open new terminal window -7. After each JS change run `yarn build:js` in project root -8. After each SCSS change run `yarn run css:dev && yarn run css:modules:dev` in project root +3. Run `cd docs-site && yarn link react-datepicker` +4. 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) +5. Run `yarn install` from project root +6. Run `yarn start` from project root +7. Open new terminal window +8. After each JS change run `yarn build:js` in project root +9. After each SCSS change run `yarn run css:dev && yarn run css:modules:dev` in project root diff --git a/src/calendar.jsx b/src/calendar.jsx index 8334452ba..95364458f 100644 --- a/src/calendar.jsx +++ b/src/calendar.jsx @@ -158,7 +158,8 @@ export default class Calendar extends React.Component { handleOnKeyDown: PropTypes.func, isInputFocused: PropTypes.bool, customTimeInput: PropTypes.element, - weekAriaLabelPrefix: PropTypes.string + weekAriaLabelPrefix: PropTypes.string, + setPreSelection: PropTypes.func }; constructor(props) { @@ -284,6 +285,8 @@ export default class Calendar extends React.Component { this.props.setOpen(true); } } + + this.props.setPreSelection && this.props.setPreSelection(date); }; handleMonthYearChange = date => { @@ -722,6 +725,7 @@ export default class Calendar extends React.Component { showMonthYearPicker={this.props.showMonthYearPicker} showQuarterYearPicker={this.props.showQuarterYearPicker} isInputFocused={this.props.isInputFocused} + containerRef={this.containerRef} /> ); diff --git a/src/day.jsx b/src/day.jsx index 2f066d1ec..26100d19d 100644 --- a/src/day.jsx +++ b/src/day.jsx @@ -38,32 +38,18 @@ export default class Day extends React.Component { startDate: PropTypes.instanceOf(Date), renderDayContents: PropTypes.func, handleOnKeyDown: PropTypes.func, - isInputFocused: PropTypes.bool + containerRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }) + ]) }; componentDidMount() { - const newTabIndex = this.getTabIndex(); - - if (newTabIndex === 0 && this.isSameDay(this.props.preSelection)) { - // focus the day on mount so that keyboard navigation works while cycling through months - // prevent focus for these activeElement cases so we don't pull focus from the input as the calendar opens - (!document.activeElement || document.activeElement === document.body) && - this.dayEl.current.focus(); - } + this.handleFocusDay(); } componentDidUpdate(prevProps) { - const newTabIndex = this.getTabIndex(); - - if ( - newTabIndex === 0 && - this.isSameDay(this.props.preSelection) !== - this.isSameDay(prevProps.preSelection) - ) { - // only do this while the input isn't focused - // otherwise, typing/backspacing the date manually may steal focus away from the input - !prevProps.isInputFocused && this.dayEl.current.focus(); - } + this.handleFocusDay(prevProps); } dayEl = React.createRef(); @@ -266,6 +252,35 @@ export default class Day extends React.Component { return tabIndex; }; + // various cases when we need to apply focus to the preselected day + // focus the day on mount/update so that keyboard navigation works while cycling through months with up or down keys (not for prev and next month buttons) + // prevent focus for these activeElement cases so we don't pull focus from the input as the calendar opens + handleFocusDay = (prevProps = {}) => { + let shouldFocusDay = false; + // only do this while the input isn't focused + // otherwise, typing/backspacing the date manually may steal focus away from the input + if ( + this.getTabIndex() === 0 && + !prevProps.isInputFocused && + this.isSameDay(this.props.preSelection) + ) { + // there is currently no activeElement + if (!document.activeElement || document.activeElement === document.body) { + shouldFocusDay = true; + } + // the activeElement is in the container, and it is another instance of Day + if ( + this.props.containerRef && + this.props.containerRef.current && + this.props.containerRef.current.contains(document.activeElement) && + document.activeElement.classList.contains("react-datepicker__day") + ) { + shouldFocusDay = true; + } + } + + shouldFocusDay && this.dayEl.current.focus(); + }; render = () => (