@instructure/ui-calendar 8.52.1-snapshot-7 → 8.52.1-snapshot-11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,9 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [8.52.1-snapshot-7](https://github.com/instructure/instructure-ui/compare/v8.52.0...v8.52.1-snapshot-7) (2024-02-07)
6
+ ## [8.52.1-snapshot-11](https://github.com/instructure/instructure-ui/compare/v8.52.0...v8.52.1-snapshot-11) (2024-02-08)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-calendar
8
+
9
+ ### Features
10
+
11
+ * **many:** provide defaults to dateInput for easier setup and use ([dcefc4e](https://github.com/instructure/instructure-ui/commit/dcefc4e259118264d76dfd248b01682a7c2ef661))
9
12
 
10
13
 
11
14
 
@@ -59,10 +59,13 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
59
59
  this.handleRef = el => {
60
60
  this.ref = el;
61
61
  };
62
- this.calculateState = (locale, timezone, currentDate) => ({
63
- renderedDate: currentDate ? DateTime.parse(currentDate, locale, timezone) : DateTime.now(locale, timezone),
64
- today: currentDate ? DateTime.parse(currentDate, locale, timezone) : DateTime.now(locale, timezone)
65
- });
62
+ this.calculateState = (locale, timezone, currentDate) => {
63
+ const visibleMonth = this.props.visibleMonth || currentDate;
64
+ return {
65
+ visibleMonth: visibleMonth ? DateTime.parse(visibleMonth, locale, timezone) : DateTime.now(locale, timezone),
66
+ today: currentDate ? DateTime.parse(currentDate, locale, timezone) : DateTime.now(locale, timezone)
67
+ };
68
+ };
66
69
  this.renderMonthNavigationButtons = () => {
67
70
  const _this$props = this.props,
68
71
  renderNextMonthButton = _this$props.renderNextMonthButton,
@@ -92,11 +95,13 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
92
95
  const _this$props2 = this.props,
93
96
  onRequestRenderNextMonth = _this$props2.onRequestRenderNextMonth,
94
97
  onRequestRenderPrevMonth = _this$props2.onRequestRenderPrevMonth;
95
- const renderedDate = this.state.renderedDate;
96
- const newDate = renderedDate.clone();
98
+ const visibleMonth = this.state.visibleMonth;
99
+ const newDate = visibleMonth.clone();
97
100
  if (direction === 'prev') {
98
101
  if (onRequestRenderPrevMonth) {
99
- onRequestRenderPrevMonth(e);
102
+ onRequestRenderPrevMonth(e, newDate.subtract({
103
+ months: 1
104
+ }).format('YYYY-MM'));
100
105
  return;
101
106
  }
102
107
  newDate.subtract({
@@ -104,7 +109,9 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
104
109
  });
105
110
  } else {
106
111
  if (onRequestRenderNextMonth) {
107
- onRequestRenderNextMonth(e);
112
+ onRequestRenderNextMonth(e, newDate.add({
113
+ months: 1
114
+ }).format('YYYY-MM'));
108
115
  return;
109
116
  }
110
117
  newDate.add({
@@ -112,7 +119,7 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
112
119
  });
113
120
  }
114
121
  this.setState({
115
- renderedDate: newDate
122
+ visibleMonth: newDate
116
123
  });
117
124
  };
118
125
  // date is returned es a ISO string, like 2021-09-14T22:00:00.000Z
@@ -133,7 +140,7 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
133
140
  componentDidUpdate(prevProps) {
134
141
  var _this$props$makeStyle2, _this$props4;
135
142
  (_this$props$makeStyle2 = (_this$props4 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props4);
136
- const isUpdated = prevProps.locale !== this.props.locale || prevProps.timezone !== this.props.timezone;
143
+ const isUpdated = prevProps.locale !== this.props.locale || prevProps.timezone !== this.props.timezone || prevProps.visibleMonth !== this.props.visibleMonth;
137
144
  if (isUpdated) {
138
145
  this.setState(() => {
139
146
  return {
@@ -149,7 +156,7 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
149
156
  const _this$props5 = this.props,
150
157
  renderNavigationLabel = _this$props5.renderNavigationLabel,
151
158
  styles = _this$props5.styles;
152
- const renderedDate = this.state.renderedDate;
159
+ const visibleMonth = this.state.visibleMonth;
153
160
  const _this$renderMonthNavi = this.renderMonthNavigationButtons(),
154
161
  prevButton = _this$renderMonthNavi.prevButton,
155
162
  nextButton = _this$renderMonthNavi.nextButton;
@@ -159,7 +166,7 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
159
166
  const style = [styles === null || styles === void 0 ? void 0 : styles.navigation, ...(prevButton || nextButton ? [styles === null || styles === void 0 ? void 0 : styles.navigationWithButtons] : [])];
160
167
  return jsx("div", {
161
168
  css: style
162
- }, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? callRenderProp(renderNavigationLabel) : jsx("span", null, jsx("div", null, renderedDate.format('MMMM')), jsx("div", null, renderedDate.format('YYYY'))), nextButton && cloneButton(nextButton, this.handleMonthChange('next')));
169
+ }, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? callRenderProp(renderNavigationLabel) : jsx("span", null, jsx("div", null, visibleMonth.format('MMMM')), jsx("div", null, visibleMonth.format('YYYY'))), nextButton && cloneButton(nextButton, this.handleMonthChange('next')));
163
170
  }
164
171
  renderBody() {
165
172
  return jsx("table", {
@@ -261,12 +268,12 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
261
268
  renderDefaultdays() {
262
269
  const selectedDate = this.props.selectedDate;
263
270
  const _this$state = this.state,
264
- renderedDate = _this$state.renderedDate,
271
+ visibleMonth = _this$state.visibleMonth,
265
272
  today = _this$state.today;
266
273
  // Sets it to the first local day of the week counting back from the start of the month.
267
274
  // Note that first day depends on the locale, e.g. it's Sunday in the US and
268
275
  // Monday in most of the EU.
269
- const currDate = DateTime.getFirstDayOfWeek(renderedDate.clone().startOf('month'));
276
+ const currDate = DateTime.getFirstDayOfWeek(visibleMonth.clone().startOf('month'));
270
277
  const arr = [];
271
278
  for (let i = 0; i < Calendar.DAY_COUNT; i++) {
272
279
  arr.push(currDate.clone());
@@ -281,7 +288,7 @@ let Calendar = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 =
281
288
  date: dateStr,
282
289
  isSelected: selectedDate ? date.isSame(selectedDate, 'day') : false,
283
290
  isToday: date.isSame(today, 'day'),
284
- isOutsideMonth: !date.isSame(renderedDate, 'month'),
291
+ isOutsideMonth: !date.isSame(visibleMonth, 'month'),
285
292
  label: date.format('D MMMM YYYY') // used by screen readers
286
293
  ,
287
294
  onClick: this.handleDayClick,
@@ -40,7 +40,8 @@ const propTypes = {
40
40
  renderWeekdayLabels: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.func])),
41
41
  role: PropTypes.oneOf(['table', 'listbox']),
42
42
  selectedDate: PropTypes.string,
43
- timezone: PropTypes.string
43
+ timezone: PropTypes.string,
44
+ visibleMonth: PropTypes.string
44
45
  };
45
- const allowedProps = ['as', 'children', 'currentDate', 'disabledDates', 'locale', 'onDateSelected', 'onRequestRenderNextMonth', 'onRequestRenderPrevMonth', 'renderNavigationLabel', 'renderNextMonthButton', 'renderPrevMonthButton', 'renderWeekdayLabels', 'role', 'selectedDate', 'timezone'];
46
+ const allowedProps = ['as', 'children', 'currentDate', 'disabledDates', 'locale', 'onDateSelected', 'onRequestRenderNextMonth', 'onRequestRenderPrevMonth', 'renderNavigationLabel', 'renderNextMonthButton', 'renderPrevMonthButton', 'renderWeekdayLabels', 'role', 'selectedDate', 'timezone', 'visibleMonth'];
46
47
  export { propTypes, allowedProps };
@@ -70,10 +70,13 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
70
70
  this.handleRef = el => {
71
71
  this.ref = el;
72
72
  };
73
- this.calculateState = (locale, timezone, currentDate) => ({
74
- renderedDate: currentDate ? _DateTime.DateTime.parse(currentDate, locale, timezone) : _DateTime.DateTime.now(locale, timezone),
75
- today: currentDate ? _DateTime.DateTime.parse(currentDate, locale, timezone) : _DateTime.DateTime.now(locale, timezone)
76
- });
73
+ this.calculateState = (locale, timezone, currentDate) => {
74
+ const visibleMonth = this.props.visibleMonth || currentDate;
75
+ return {
76
+ visibleMonth: visibleMonth ? _DateTime.DateTime.parse(visibleMonth, locale, timezone) : _DateTime.DateTime.now(locale, timezone),
77
+ today: currentDate ? _DateTime.DateTime.parse(currentDate, locale, timezone) : _DateTime.DateTime.now(locale, timezone)
78
+ };
79
+ };
77
80
  this.renderMonthNavigationButtons = () => {
78
81
  const _this$props = this.props,
79
82
  renderNextMonthButton = _this$props.renderNextMonthButton,
@@ -103,11 +106,13 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
103
106
  const _this$props2 = this.props,
104
107
  onRequestRenderNextMonth = _this$props2.onRequestRenderNextMonth,
105
108
  onRequestRenderPrevMonth = _this$props2.onRequestRenderPrevMonth;
106
- const renderedDate = this.state.renderedDate;
107
- const newDate = renderedDate.clone();
109
+ const visibleMonth = this.state.visibleMonth;
110
+ const newDate = visibleMonth.clone();
108
111
  if (direction === 'prev') {
109
112
  if (onRequestRenderPrevMonth) {
110
- onRequestRenderPrevMonth(e);
113
+ onRequestRenderPrevMonth(e, newDate.subtract({
114
+ months: 1
115
+ }).format('YYYY-MM'));
111
116
  return;
112
117
  }
113
118
  newDate.subtract({
@@ -115,7 +120,9 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
115
120
  });
116
121
  } else {
117
122
  if (onRequestRenderNextMonth) {
118
- onRequestRenderNextMonth(e);
123
+ onRequestRenderNextMonth(e, newDate.add({
124
+ months: 1
125
+ }).format('YYYY-MM'));
119
126
  return;
120
127
  }
121
128
  newDate.add({
@@ -123,7 +130,7 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
123
130
  });
124
131
  }
125
132
  this.setState({
126
- renderedDate: newDate
133
+ visibleMonth: newDate
127
134
  });
128
135
  };
129
136
  // date is returned es a ISO string, like 2021-09-14T22:00:00.000Z
@@ -144,7 +151,7 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
144
151
  componentDidUpdate(prevProps) {
145
152
  var _this$props$makeStyle2, _this$props4;
146
153
  (_this$props$makeStyle2 = (_this$props4 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props4);
147
- const isUpdated = prevProps.locale !== this.props.locale || prevProps.timezone !== this.props.timezone;
154
+ const isUpdated = prevProps.locale !== this.props.locale || prevProps.timezone !== this.props.timezone || prevProps.visibleMonth !== this.props.visibleMonth;
148
155
  if (isUpdated) {
149
156
  this.setState(() => {
150
157
  return {
@@ -160,7 +167,7 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
160
167
  const _this$props5 = this.props,
161
168
  renderNavigationLabel = _this$props5.renderNavigationLabel,
162
169
  styles = _this$props5.styles;
163
- const renderedDate = this.state.renderedDate;
170
+ const visibleMonth = this.state.visibleMonth;
164
171
  const _this$renderMonthNavi = this.renderMonthNavigationButtons(),
165
172
  prevButton = _this$renderMonthNavi.prevButton,
166
173
  nextButton = _this$renderMonthNavi.nextButton;
@@ -170,7 +177,7 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
170
177
  const style = [styles === null || styles === void 0 ? void 0 : styles.navigation, ...(prevButton || nextButton ? [styles === null || styles === void 0 ? void 0 : styles.navigationWithButtons] : [])];
171
178
  return (0, _emotion.jsx)("div", {
172
179
  css: style
173
- }, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? (0, _callRenderProp.callRenderProp)(renderNavigationLabel) : (0, _emotion.jsx)("span", null, (0, _emotion.jsx)("div", null, renderedDate.format('MMMM')), (0, _emotion.jsx)("div", null, renderedDate.format('YYYY'))), nextButton && cloneButton(nextButton, this.handleMonthChange('next')));
180
+ }, prevButton && cloneButton(prevButton, this.handleMonthChange('prev')), renderNavigationLabel ? (0, _callRenderProp.callRenderProp)(renderNavigationLabel) : (0, _emotion.jsx)("span", null, (0, _emotion.jsx)("div", null, visibleMonth.format('MMMM')), (0, _emotion.jsx)("div", null, visibleMonth.format('YYYY'))), nextButton && cloneButton(nextButton, this.handleMonthChange('next')));
174
181
  }
175
182
  renderBody() {
176
183
  return (0, _emotion.jsx)("table", {
@@ -272,12 +279,12 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
272
279
  renderDefaultdays() {
273
280
  const selectedDate = this.props.selectedDate;
274
281
  const _this$state = this.state,
275
- renderedDate = _this$state.renderedDate,
282
+ visibleMonth = _this$state.visibleMonth,
276
283
  today = _this$state.today;
277
284
  // Sets it to the first local day of the week counting back from the start of the month.
278
285
  // Note that first day depends on the locale, e.g. it's Sunday in the US and
279
286
  // Monday in most of the EU.
280
- const currDate = _DateTime.DateTime.getFirstDayOfWeek(renderedDate.clone().startOf('month'));
287
+ const currDate = _DateTime.DateTime.getFirstDayOfWeek(visibleMonth.clone().startOf('month'));
281
288
  const arr = [];
282
289
  for (let i = 0; i < Calendar.DAY_COUNT; i++) {
283
290
  arr.push(currDate.clone());
@@ -292,7 +299,7 @@ let Calendar = exports.Calendar = (_dec = (0, _emotion.withStyle)(_styles.defaul
292
299
  date: dateStr,
293
300
  isSelected: selectedDate ? date.isSame(selectedDate, 'day') : false,
294
301
  isToday: date.isSame(today, 'day'),
295
- isOutsideMonth: !date.isSame(renderedDate, 'month'),
302
+ isOutsideMonth: !date.isSame(visibleMonth, 'month'),
296
303
  label: date.format('D MMMM YYYY') // used by screen readers
297
304
  ,
298
305
  onClick: this.handleDayClick,
@@ -47,6 +47,7 @@ const propTypes = exports.propTypes = {
47
47
  renderWeekdayLabels: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.func])),
48
48
  role: _propTypes.default.oneOf(['table', 'listbox']),
49
49
  selectedDate: _propTypes.default.string,
50
- timezone: _propTypes.default.string
50
+ timezone: _propTypes.default.string,
51
+ visibleMonth: _propTypes.default.string
51
52
  };
52
- const allowedProps = exports.allowedProps = ['as', 'children', 'currentDate', 'disabledDates', 'locale', 'onDateSelected', 'onRequestRenderNextMonth', 'onRequestRenderPrevMonth', 'renderNavigationLabel', 'renderNextMonthButton', 'renderPrevMonthButton', 'renderWeekdayLabels', 'role', 'selectedDate', 'timezone'];
53
+ const allowedProps = exports.allowedProps = ['as', 'children', 'currentDate', 'disabledDates', 'locale', 'onDateSelected', 'onRequestRenderNextMonth', 'onRequestRenderPrevMonth', 'renderNavigationLabel', 'renderNextMonthButton', 'renderPrevMonthButton', 'renderWeekdayLabels', 'role', 'selectedDate', 'timezone', 'visibleMonth'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-calendar",
3
- "version": "8.52.1-snapshot-7",
3
+ "version": "8.52.1-snapshot-11",
4
4
  "description": "A calendar component.",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,29 +23,29 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-babel-preset": "8.52.1-snapshot-7",
27
- "@instructure/ui-color-utils": "8.52.1-snapshot-7",
28
- "@instructure/ui-test-locator": "8.52.1-snapshot-7",
29
- "@instructure/ui-test-utils": "8.52.1-snapshot-7",
30
- "@instructure/ui-themes": "8.52.1-snapshot-7",
26
+ "@instructure/ui-babel-preset": "8.52.1-snapshot-11",
27
+ "@instructure/ui-color-utils": "8.52.1-snapshot-11",
28
+ "@instructure/ui-test-locator": "8.52.1-snapshot-11",
29
+ "@instructure/ui-test-utils": "8.52.1-snapshot-11",
30
+ "@instructure/ui-themes": "8.52.1-snapshot-11",
31
31
  "@testing-library/jest-dom": "^6.1.4",
32
32
  "@testing-library/react": "^14.0.0"
33
33
  },
34
34
  "dependencies": {
35
35
  "@babel/runtime": "^7.23.2",
36
- "@instructure/console": "8.52.1-snapshot-7",
37
- "@instructure/emotion": "8.52.1-snapshot-7",
38
- "@instructure/shared-types": "8.52.1-snapshot-7",
39
- "@instructure/ui-a11y-content": "8.52.1-snapshot-7",
40
- "@instructure/ui-buttons": "8.52.1-snapshot-7",
41
- "@instructure/ui-i18n": "8.52.1-snapshot-7",
42
- "@instructure/ui-icons": "8.52.1-snapshot-7",
43
- "@instructure/ui-prop-types": "8.52.1-snapshot-7",
44
- "@instructure/ui-react-utils": "8.52.1-snapshot-7",
45
- "@instructure/ui-testable": "8.52.1-snapshot-7",
46
- "@instructure/ui-utils": "8.52.1-snapshot-7",
47
- "@instructure/ui-view": "8.52.1-snapshot-7",
48
- "@instructure/uid": "8.52.1-snapshot-7",
36
+ "@instructure/console": "8.52.1-snapshot-11",
37
+ "@instructure/emotion": "8.52.1-snapshot-11",
38
+ "@instructure/shared-types": "8.52.1-snapshot-11",
39
+ "@instructure/ui-a11y-content": "8.52.1-snapshot-11",
40
+ "@instructure/ui-buttons": "8.52.1-snapshot-11",
41
+ "@instructure/ui-i18n": "8.52.1-snapshot-11",
42
+ "@instructure/ui-icons": "8.52.1-snapshot-11",
43
+ "@instructure/ui-prop-types": "8.52.1-snapshot-11",
44
+ "@instructure/ui-react-utils": "8.52.1-snapshot-11",
45
+ "@instructure/ui-testable": "8.52.1-snapshot-11",
46
+ "@instructure/ui-utils": "8.52.1-snapshot-11",
47
+ "@instructure/ui-view": "8.52.1-snapshot-11",
48
+ "@instructure/uid": "8.52.1-snapshot-11",
49
49
  "prop-types": "^15.8.1"
50
50
  },
51
51
  "peerDependencies": {
@@ -14,46 +14,39 @@ to the `Calendar` component.
14
14
  ---
15
15
  type: example
16
16
  ---
17
- <Calendar locale="hu"/>
17
+ <Calendar/>
18
18
  ```
19
19
 
20
20
  ### Default config with additional props
21
21
 
22
- - ```js
23
- class Example extends React.Component {
24
- state = {
25
- selectedDate: ''
22
+ ```js
23
+ ---
24
+ type: example
25
+ ---
26
+ class Example extends React.Component {
27
+ state = {
28
+ selectedDate: "",
29
+ visibleMonth:"2025-05"
26
30
  }
27
- render = () => (
28
- <Calendar
29
- currentDate="2023-12-15"
30
- disabledDates={['2023-12-22', '2023-12-12', '2023-12-11']}
31
- selectedDate={this.state.selectedDate}
32
- onDateSelected={(date) => {
33
- this.setState({ selectedDate: date })
34
- }}
35
- />
36
- )
37
- }
38
- render(<Example />)
39
- ```
40
31
 
41
- - ```js
42
- const Example = () => {
43
- const [selectedDate, setSelectedDate] = useState('')
44
- return (
45
- <Calendar
46
- currentDate="2023-12-15"
47
- disabledDates={['2023-12-22', '2023-12-12', '2023-12-11']}
48
- selectedDate={selectedDate}
49
- onDateSelected={(date) => {
50
- setSelectedDate(date)
51
- }}
52
- />
53
- )
32
+ setVisibleMonth = () => {
33
+ this.setState({visibleMonth: "2028-09"})
54
34
  }
55
- render(<Example />)
56
- ```
35
+ render = () =>
36
+ <Calendar
37
+ visibleMonth={this.state.visibleMonth}
38
+ currentDate="2023-12-15"
39
+ disabledDates={['2023-12-22', '2023-12-12', '2023-12-11']}
40
+ selectedDate={this.state.selectedDate}
41
+ onRequestRenderNextMonth={(_e, requestedMonth) => this.setState({visibleMonth: requestedMonth})}
42
+ onRequestRenderPrevMonth={(_e, requestedMonth) => this.setState({visibleMonth: requestedMonth})}
43
+ onDateSelected={(date)=>{
44
+ this.setState({selectedDate: date})
45
+ }}
46
+ />
47
+ }
48
+ render(<Example />)
49
+ ```
57
50
 
58
51
  ### Composing a Calendar in your Application
59
52
 
@@ -109,7 +109,8 @@ class Calendar extends Component<CalendarProps, CalendarState> {
109
109
 
110
110
  const isUpdated =
111
111
  prevProps.locale !== this.props.locale ||
112
- prevProps.timezone !== this.props.timezone
112
+ prevProps.timezone !== this.props.timezone ||
113
+ prevProps.visibleMonth !== this.props.visibleMonth
113
114
 
114
115
  if (isUpdated) {
115
116
  this.setState(() => {
@@ -124,18 +125,18 @@ class Calendar extends Component<CalendarProps, CalendarState> {
124
125
  }
125
126
  }
126
127
 
127
- calculateState = (
128
- locale: string,
129
- timezone: string,
130
- currentDate?: string
131
- ) => ({
132
- renderedDate: currentDate
133
- ? DateTime.parse(currentDate, locale, timezone)
134
- : DateTime.now(locale, timezone),
135
- today: currentDate
136
- ? DateTime.parse(currentDate, locale, timezone)
137
- : DateTime.now(locale, timezone)
138
- })
128
+ calculateState = (locale: string, timezone: string, currentDate?: string) => {
129
+ const visibleMonth = this.props.visibleMonth || currentDate
130
+
131
+ return {
132
+ visibleMonth: visibleMonth
133
+ ? DateTime.parse(visibleMonth, locale, timezone)
134
+ : DateTime.now(locale, timezone),
135
+ today: currentDate
136
+ ? DateTime.parse(currentDate, locale, timezone)
137
+ : DateTime.now(locale, timezone)
138
+ }
139
+ }
139
140
 
140
141
  get role() {
141
142
  return this.props.role === 'listbox' ? this.props.role : undefined
@@ -172,27 +173,33 @@ class Calendar extends Component<CalendarProps, CalendarState> {
172
173
 
173
174
  handleMonthChange = (direction: 'prev' | 'next') => (e: React.MouseEvent) => {
174
175
  const { onRequestRenderNextMonth, onRequestRenderPrevMonth } = this.props
175
- const { renderedDate } = this.state
176
- const newDate = renderedDate.clone()
176
+ const { visibleMonth } = this.state
177
+ const newDate = visibleMonth.clone()
177
178
  if (direction === 'prev') {
178
179
  if (onRequestRenderPrevMonth) {
179
- onRequestRenderPrevMonth(e)
180
+ onRequestRenderPrevMonth(
181
+ e,
182
+ newDate.subtract({ months: 1 }).format('YYYY-MM')
183
+ )
180
184
  return
181
185
  }
182
186
  newDate.subtract({ months: 1 })
183
187
  } else {
184
188
  if (onRequestRenderNextMonth) {
185
- onRequestRenderNextMonth(e)
189
+ onRequestRenderNextMonth(
190
+ e,
191
+ newDate.add({ months: 1 }).format('YYYY-MM')
192
+ )
186
193
  return
187
194
  }
188
195
  newDate.add({ months: 1 })
189
196
  }
190
- this.setState({ renderedDate: newDate })
197
+ this.setState({ visibleMonth: newDate })
191
198
  }
192
199
 
193
200
  renderHeader() {
194
201
  const { renderNavigationLabel, styles } = this.props
195
- const { renderedDate } = this.state
202
+ const { visibleMonth } = this.state
196
203
  const { prevButton, nextButton } = this.renderMonthNavigationButtons()
197
204
 
198
205
  const cloneButton = (
@@ -218,8 +225,8 @@ class Calendar extends Component<CalendarProps, CalendarState> {
218
225
  callRenderProp(renderNavigationLabel)
219
226
  ) : (
220
227
  <span>
221
- <div>{renderedDate.format('MMMM')}</div>
222
- <div>{renderedDate.format('YYYY')}</div>
228
+ <div>{visibleMonth.format('MMMM')}</div>
229
+ <div>{visibleMonth.format('YYYY')}</div>
223
230
  </span>
224
231
  )}
225
232
  {nextButton && cloneButton(nextButton, this.handleMonthChange('next'))}
@@ -376,12 +383,12 @@ class Calendar extends Component<CalendarProps, CalendarState> {
376
383
 
377
384
  renderDefaultdays() {
378
385
  const { selectedDate } = this.props
379
- const { renderedDate, today } = this.state
386
+ const { visibleMonth, today } = this.state
380
387
  // Sets it to the first local day of the week counting back from the start of the month.
381
388
  // Note that first day depends on the locale, e.g. it's Sunday in the US and
382
389
  // Monday in most of the EU.
383
390
  const currDate = DateTime.getFirstDayOfWeek(
384
- renderedDate.clone().startOf('month')
391
+ visibleMonth.clone().startOf('month')
385
392
  )
386
393
  const arr: Moment[] = []
387
394
  for (let i = 0; i < Calendar.DAY_COUNT; i++) {
@@ -396,7 +403,7 @@ class Calendar extends Component<CalendarProps, CalendarState> {
396
403
  date={dateStr}
397
404
  isSelected={selectedDate ? date.isSame(selectedDate, 'day') : false}
398
405
  isToday={date.isSame(today, 'day')}
399
- isOutsideMonth={!date.isSame(renderedDate, 'month')}
406
+ isOutsideMonth={!date.isSame(visibleMonth, 'month')}
400
407
  label={date.format('D MMMM YYYY')} // used by screen readers
401
408
  onClick={this.handleDayClick}
402
409
  interaction={this.isDisabledDate(date) ? 'disabled' : 'enabled'}
@@ -85,12 +85,18 @@ type CalendarOwnProps = {
85
85
  * Callback fired when the next month button is clicked in the navigation
86
86
  * header, requesting to render the next month.
87
87
  */
88
- onRequestRenderNextMonth?: (e: React.MouseEvent) => void
88
+ onRequestRenderNextMonth?: (
89
+ e: React.MouseEvent,
90
+ requestedMonth: string
91
+ ) => void
89
92
  /**
90
93
  * Callback fired when the previous month button is clicked in the navigation
91
94
  * header, requesting to render the previous month.
92
95
  */
93
- onRequestRenderPrevMonth?: (e: React.MouseEvent) => void
96
+ onRequestRenderPrevMonth?: (
97
+ e: React.MouseEvent,
98
+ requestedMonth: string
99
+ ) => void
94
100
  /**
95
101
  * Content to render in the navigation header. The recommendation is to include
96
102
  * the name of the current rendered month along with the year.
@@ -142,6 +148,10 @@ type CalendarOwnProps = {
142
148
  * property or a context property.
143
149
  */
144
150
  timezone?: string
151
+ /**
152
+ * Visible month for the rendered calendar. Formatted as an ISO date string.
153
+ */
154
+ visibleMonth?: string
145
155
  }
146
156
 
147
157
  type PropKeys = keyof CalendarOwnProps
@@ -173,7 +183,8 @@ const propTypes: PropValidators<PropKeys> = {
173
183
  ),
174
184
  role: PropTypes.oneOf(['table', 'listbox']),
175
185
  selectedDate: PropTypes.string,
176
- timezone: PropTypes.string
186
+ timezone: PropTypes.string,
187
+ visibleMonth: PropTypes.string
177
188
  }
178
189
 
179
190
  const allowedProps: AllowedPropKeys = [
@@ -191,11 +202,12 @@ const allowedProps: AllowedPropKeys = [
191
202
  'renderWeekdayLabels',
192
203
  'role',
193
204
  'selectedDate',
194
- 'timezone'
205
+ 'timezone',
206
+ 'visibleMonth'
195
207
  ]
196
208
 
197
209
  type CalendarState = {
198
- renderedDate: Moment
210
+ visibleMonth: Moment
199
211
  today: Moment
200
212
  }
201
213