Skip to content

Commit 321084a

Browse files
Merge branch 'master' into pr/3799
2 parents c570302 + 3070e4a commit 321084a

File tree

7 files changed

+262
-16
lines changed

7 files changed

+262
-16
lines changed

examples/hello-world/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"dependencies": {
66
"react": "^18.2.0",
7-
"react-datepicker": "^4.8.0",
7+
"react-datepicker": "^4.9.0",
88
"react-dom": "^18.2.0",
99
"react-scripts": "4.0.3"
1010
},

examples/hello-world/yarn.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10070,17 +10070,17 @@ react-app-polyfill@^2.0.0:
1007010070
regenerator-runtime "^0.13.7"
1007110071
whatwg-fetch "^3.4.1"
1007210072

10073-
react-datepicker@^4.8.0:
10074-
version "4.8.0"
10075-
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.8.0.tgz#11b8918d085a1ce4781eee4c8e4641b3cd592010"
10076-
integrity sha512-u69zXGHMpxAa4LeYR83vucQoUCJQ6m/WBsSxmUMu/M8ahTSVMMyiyQzauHgZA2NUr9y0FUgOAix71hGYUb6tvg==
10073+
react-datepicker@^4.9.0:
10074+
version "4.9.0"
10075+
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.9.0.tgz#e70cc7956e89e22d41dfdca06a6a8bf9676a0cc6"
10076+
integrity sha512-H2TphqnbFpBzbgcYN6B2lhiNt0us0OBdvMOuhmZMXvm7s1Cz7U5nYdORL7aCh7bc41ofPimf3ocg/uw2/xdRUA==
1007710077
dependencies:
1007810078
"@popperjs/core" "^2.9.2"
1007910079
classnames "^2.2.6"
1008010080
date-fns "^2.24.0"
1008110081
prop-types "^15.7.2"
10082-
react-onclickoutside "^6.12.0"
10083-
react-popper "^2.2.5"
10082+
react-onclickoutside "^6.12.2"
10083+
react-popper "^2.3.0"
1008410084

1008510085
react-dev-utils@^11.0.3:
1008610086
version "11.0.4"
@@ -10140,15 +10140,15 @@ react-is@^17.0.1:
1014010140
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
1014110141
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
1014210142

10143-
react-onclickoutside@^6.12.0:
10144-
version "6.12.0"
10145-
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.0.tgz#c63db2e3c2c852b288160cdb6cff443604e28db4"
10146-
integrity sha512-oPlOTYcISLHfpMog2lUZMFSbqOs4LFcA4+vo7fpfevB5v9Z0D5VBDBkfeO5lv+hpEcGoaGk67braLT+QT+eICA==
10143+
react-onclickoutside@^6.12.2:
10144+
version "6.12.2"
10145+
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz#8e6cf80c7d17a79f2c908399918158a7b02dda01"
10146+
integrity sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==
1014710147

10148-
react-popper@^2.2.5:
10149-
version "2.2.5"
10150-
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.5.tgz#1214ef3cec86330a171671a4fbcbeeb65ee58e96"
10151-
integrity sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==
10148+
react-popper@^2.3.0:
10149+
version "2.3.0"
10150+
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
10151+
integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==
1015210152
dependencies:
1015310153
react-fast-compare "^3.0.1"
1015410154
warning "^4.0.2"

src/calendar.jsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
isValid,
4242
getYearsPeriod,
4343
DEFAULT_YEAR_ITEM_NUMBER,
44+
getMonthInLocale,
4445
} from "./date_utils";
4546

4647
const DROPDOWN_FOCUS_CLASSNAMES = [
@@ -210,6 +211,7 @@ export default class Calendar extends React.Component {
210211
date: this.getDateInView(),
211212
selectingDate: null,
212213
monthContainer: null,
214+
isRenderAriaLiveMessage: false,
213215
};
214216
}
215217

@@ -312,6 +314,7 @@ export default class Calendar extends React.Component {
312314
handleYearChange = (date) => {
313315
if (this.props.onYearChange) {
314316
this.props.onYearChange(date);
317+
this.setState({ isRenderAriaLiveMessage: true });
315318
}
316319
if (this.props.adjustDateOnChange) {
317320
if (this.props.onSelect) {
@@ -328,6 +331,7 @@ export default class Calendar extends React.Component {
328331
handleMonthChange = (date) => {
329332
if (this.props.onMonthChange) {
330333
this.props.onMonthChange(date);
334+
this.setState({ isRenderAriaLiveMessage: true });
331335
}
332336
if (this.props.adjustDateOnChange) {
333337
if (this.props.onSelect) {
@@ -984,6 +988,38 @@ export default class Calendar extends React.Component {
984988
}
985989
};
986990

991+
renderAriaLiveRegion = () => {
992+
const { startPeriod, endPeriod } = getYearsPeriod(
993+
this.state.date,
994+
this.props.yearItemNumber
995+
);
996+
let ariaLiveMessage;
997+
998+
if (this.props.showYearPicker) {
999+
ariaLiveMessage = `${startPeriod} - ${endPeriod}`;
1000+
} else if (
1001+
this.props.showMonthYearPicker ||
1002+
this.props.showQuarterYearPicker
1003+
) {
1004+
ariaLiveMessage = getYear(this.state.date);
1005+
} else {
1006+
ariaLiveMessage = `${getMonthInLocale(
1007+
getMonth(this.state.date),
1008+
this.props.locale
1009+
)} ${getYear(this.state.date)}`;
1010+
}
1011+
1012+
return (
1013+
<span
1014+
role="alert"
1015+
aria-live="polite"
1016+
className="react-datepicker__aria-live"
1017+
>
1018+
{this.state.isRenderAriaLiveMessage && ariaLiveMessage}
1019+
</span>
1020+
);
1021+
};
1022+
9871023
renderChildren = () => {
9881024
if (this.props.children) {
9891025
return (
@@ -1005,6 +1041,7 @@ export default class Calendar extends React.Component {
10051041
showPopperArrow={this.props.showPopperArrow}
10061042
arrowProps={this.props.arrowProps}
10071043
>
1044+
{this.renderAriaLiveRegion()}
10081045
{this.renderPreviousButton()}
10091046
{this.renderNextButton()}
10101047
{this.renderMonths()}

src/index.jsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ export default class DatePicker extends React.Component {
376376
// used to focus day in inline version after month has changed, but not on
377377
// initial render
378378
shouldFocusDayInline: false,
379+
isRenderAriaLiveMessage: false,
379380
};
380381
};
381382

@@ -527,6 +528,7 @@ export default class DatePicker extends React.Component {
527528
this.props.onChangeRaw(event);
528529
}
529530
this.setSelected(date, event, false, monthSelectedIn);
531+
this.setState({ isRenderAriaLiveMessage: true });
530532
if (!this.props.shouldCloseOnSelect || this.props.showTimeSelect) {
531533
this.setPreSelection(date);
532534
} else if (!this.props.inline) {
@@ -670,6 +672,9 @@ export default class DatePicker extends React.Component {
670672
if (this.props.showTimeInput) {
671673
this.setOpen(true);
672674
}
675+
if (this.props.showTimeSelectOnly || this.props.showTimeSelect) {
676+
this.setState({ isRenderAriaLiveMessage: true });
677+
}
673678
this.setState({ inputValue: null });
674679
};
675680

@@ -1015,6 +1020,75 @@ export default class DatePicker extends React.Component {
10151020
);
10161021
};
10171022

1023+
renderAriaLiveRegion = () => {
1024+
const { dateFormat, locale } = this.props;
1025+
const isContainsTime =
1026+
this.props.showTimeInput || this.props.showTimeSelect;
1027+
const longDateFormat = isContainsTime ? "PPPPp" : "PPPP";
1028+
let ariaLiveMessage;
1029+
1030+
if (this.props.selectsRange) {
1031+
ariaLiveMessage = `Selected start date: ${safeDateFormat(
1032+
this.props.startDate,
1033+
{
1034+
dateFormat: longDateFormat,
1035+
locale,
1036+
}
1037+
)}. ${
1038+
this.props.endDate
1039+
? "End date: " +
1040+
safeDateFormat(this.props.endDate, {
1041+
dateFormat: longDateFormat,
1042+
locale,
1043+
})
1044+
: ""
1045+
}`;
1046+
} else {
1047+
if (this.props.showTimeSelectOnly) {
1048+
ariaLiveMessage = `Selected time: ${safeDateFormat(
1049+
this.props.selected,
1050+
{ dateFormat, locale }
1051+
)}`;
1052+
} else if (this.props.showYearPicker) {
1053+
ariaLiveMessage = `Selected year: ${safeDateFormat(
1054+
this.props.selected,
1055+
{ dateFormat: "yyyy", locale }
1056+
)}`;
1057+
} else if (this.props.showMonthYearPicker) {
1058+
ariaLiveMessage = `Selected month: ${safeDateFormat(
1059+
this.props.selected,
1060+
{ dateFormat: "MMMM yyyy", locale }
1061+
)}`;
1062+
} else if (this.props.showQuarterYearPicker) {
1063+
ariaLiveMessage = `Selected quarter: ${safeDateFormat(
1064+
this.props.selected,
1065+
{
1066+
dateFormat: "yyyy, QQQ",
1067+
locale,
1068+
}
1069+
)}`;
1070+
} else {
1071+
ariaLiveMessage = `Selected date: ${safeDateFormat(
1072+
this.props.selected,
1073+
{
1074+
dateFormat: longDateFormat,
1075+
locale,
1076+
}
1077+
)}`;
1078+
}
1079+
}
1080+
1081+
return (
1082+
<span
1083+
role="alert"
1084+
aria-live="polite"
1085+
className="react-datepicker__aria-live"
1086+
>
1087+
{this.state.isRenderAriaLiveMessage && ariaLiveMessage}
1088+
</span>
1089+
);
1090+
};
1091+
10181092
renderDateInput = () => {
10191093
const className = classnames(this.props.className, {
10201094
[outsideClickIgnoreClass]: this.state.open,
@@ -1096,6 +1170,7 @@ export default class DatePicker extends React.Component {
10961170
renderInputContainer() {
10971171
return (
10981172
<div className="react-datepicker__input-container">
1173+
{this.renderAriaLiveRegion()}
10991174
{this.renderDateInput()}
11001175
{this.renderClearButton()}
11011176
</div>

src/stylesheets/datepicker.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,3 +689,15 @@
689689
padding-left: 0.2rem;
690690
height: auto;
691691
}
692+
693+
.react-datepicker__aria-live {
694+
position: absolute;
695+
clip-path: circle(0);
696+
border: 0;
697+
height: 1px;
698+
margin: -1px;
699+
overflow: hidden;
700+
padding: 0;
701+
width: 1px;
702+
white-space: nowrap;
703+
}

test/calendar_test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,4 +1741,74 @@ describe("Calendar", function () {
17411741
expect(childrenContainer).to.have.length(0);
17421742
});
17431743
});
1744+
1745+
describe("should render aria live region after month/year change", () => {
1746+
it("should render aria live region after month change", () => {
1747+
const datePicker = TestUtils.renderIntoDocument(
1748+
<DatePicker selected={utils.newDate()} />
1749+
);
1750+
const dateInput = datePicker.input;
1751+
1752+
TestUtils.Simulate.focus(ReactDOM.findDOMNode(dateInput));
1753+
1754+
const calendar = TestUtils.scryRenderedComponentsWithType(
1755+
datePicker.calendar,
1756+
Calendar
1757+
)[0];
1758+
const nextNavigationButton = TestUtils.findRenderedDOMComponentWithClass(
1759+
calendar,
1760+
"react-datepicker__navigation--next"
1761+
);
1762+
nextNavigationButton.click();
1763+
1764+
const currentMonthText = TestUtils.findRenderedDOMComponentWithClass(
1765+
calendar,
1766+
"react-datepicker__current-month"
1767+
).textContent;
1768+
1769+
const ariaLiveMessage = TestUtils.findRenderedDOMComponentWithClass(
1770+
calendar,
1771+
"react-datepicker__aria-live"
1772+
).textContent;
1773+
1774+
expect(currentMonthText).to.equal(ariaLiveMessage);
1775+
});
1776+
1777+
it("should render aria live region after year change", () => {
1778+
const datePicker = TestUtils.renderIntoDocument(
1779+
<DatePicker showYearDropdown selected={utils.newDate()} />
1780+
);
1781+
const dateInput = datePicker.input;
1782+
1783+
TestUtils.Simulate.focus(ReactDOM.findDOMNode(dateInput));
1784+
1785+
const calendar = TestUtils.scryRenderedComponentsWithType(
1786+
datePicker.calendar,
1787+
Calendar
1788+
)[0];
1789+
const yearDropdown = TestUtils.findRenderedDOMComponentWithClass(
1790+
calendar,
1791+
"react-datepicker__year-read-view"
1792+
);
1793+
yearDropdown.click();
1794+
1795+
const option = TestUtils.scryRenderedDOMComponentsWithClass(
1796+
calendar,
1797+
"react-datepicker__year-option"
1798+
)[7];
1799+
option.click();
1800+
1801+
const ariaLiveMessage = TestUtils.findRenderedDOMComponentWithClass(
1802+
calendar,
1803+
"react-datepicker__aria-live"
1804+
).textContent;
1805+
1806+
expect(ariaLiveMessage).to.equal(
1807+
`${utils.getMonthInLocale(
1808+
utils.getMonth(calendar.state.date),
1809+
datePicker.props.locale
1810+
)} ${utils.getYear(calendar.state.date)}`
1811+
);
1812+
});
1813+
});
17441814
});

0 commit comments

Comments
 (0)