Skip to content

Commit ecef535

Browse files
smstammSteph Stamm
andauthored
Add localization to DateRangePicker (#988)
* add localization to DateRangePicker * bump version to make breaking change * downgrade version * remove unused comment * small aria update for drawer --------- Co-authored-by: Steph Stamm <stephanie.stamm@mx.com>
1 parent b59464e commit ecef535

7 files changed

Lines changed: 104 additions & 76 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mx-react-components",
3-
"version": "8.5.1",
3+
"version": "8.6.0",
44
"description": "A collection of generic React UI components",
55
"main": "dist/index.js",
66
"files": [

src/components/DateRangePicker.js

Lines changed: 64 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const PropTypes = require('prop-types');
33
const keycode = require('keycode');
44

55
import { withTheme } from './Theme';
6+
import 'moment/locale/es'
7+
import 'moment/locale/fr'
68
const moment = require('moment');
79
const _merge = require('lodash/merge');
810

@@ -32,6 +34,7 @@ class DateRangePicker extends React.Component {
3234
elementRef: PropTypes.func,
3335
focusTrapProps: PropTypes.object,
3436
format: PropTypes.string,
37+
getTranslation: PropTypes.func.isRequired,
3538
isRelative: PropTypes.bool,
3639
locale: PropTypes.string,
3740
minimumDate: PropTypes.number,
@@ -49,46 +52,13 @@ class DateRangePicker extends React.Component {
4952
};
5053

5154
static defaultProps = {
52-
defaultRanges: [
53-
{
54-
displayValue: 'This Month',
55-
getEndDate: () => moment().endOf('month').unix(),
56-
getStartDate: () => moment().startOf('month').unix()
57-
},
58-
{
59-
displayValue: 'Last 30 Days',
60-
getEndDate: () => moment().endOf('day').unix(),
61-
getStartDate: () => moment().subtract(29, 'days').startOf('day').unix()
62-
},
63-
{
64-
displayValue: 'Last Month',
65-
getEndDate: () => moment().subtract(1, 'months').endOf('month').unix(),
66-
getStartDate: () => moment().subtract(1, 'months').startOf('month').unix()
67-
},
68-
{
69-
displayValue: 'Last 90 Days',
70-
getEndDate: () => moment().endOf('day').unix(),
71-
getStartDate: () => moment().subtract(89, 'days').startOf('day').unix()
72-
},
73-
{
74-
displayValue: 'Year To Date',
75-
getEndDate: () => moment().endOf('day').unix(),
76-
getStartDate: () => moment().startOf('year').unix()
77-
},
78-
{
79-
displayValue: 'Last Year',
80-
getEndDate: () => moment().endOf('year').subtract(1, 'y').unix(),
81-
getStartDate: () => moment().startOf('year').subtract(1, 'y').unix()
82-
}
83-
],
8455
focusTrapProps: {},
8556
format: 'MMM D, YYYY',
8657
isRelative: true,
8758
locale: 'en',
8859
onCancel: () => {},
8960
onClose: () => {},
9061
onOpen: () => {},
91-
placeholderText: 'Select A Date Range',
9262
showDefaultRanges: false
9363
};
9464

@@ -106,6 +76,10 @@ class DateRangePicker extends React.Component {
10676
};
10777
}
10878

79+
componentDidMount() {
80+
moment.locale(this.props.locale)
81+
}
82+
10983
UNSAFE_componentWillReceiveProps (newProps) {
11084
const isUpdatedSelectedEndDate = newProps.selectedEndDate && newProps.selectedEndDate !== this.props.selectedEndDate;
11185
const isUpdatedSelectedStartDate = newProps.selectedStartDate && newProps.selectedStartDate !== this.props.selectedStartDate;
@@ -269,6 +243,12 @@ class DateRangePicker extends React.Component {
269243
return isActive;
270244
};
271245

246+
_formatMomentDate = (date, format = 'MMMM Do, YYYY') => {
247+
const d = moment.unix(date).format(format)
248+
249+
return d.charAt(0).toUpperCase() + d.slice(1)
250+
}
251+
272252
_getDateRangePosition = (selectedStart, selectedEnd, active, date) => {
273253
const start = selectedStart || active;
274254
const end = selectedEnd || active;
@@ -306,15 +286,49 @@ class DateRangePicker extends React.Component {
306286
};
307287

308288
render () {
309-
const { children, placeholderText } = this.props;
289+
const { children, getTranslation, placeholderText } = this.props;
310290
const theme = StyleUtils.mergeTheme(this.props.theme);
311291
const isLargeOrMediumWindowSize = this._isLargeOrMediumWindowSize(theme);
312292
const styles = this.styles(theme, isLargeOrMediumWindowSize);
313293
const shouldShowCalendarIcon = StyleUtils.getWindowSize(theme.BreakPoints) !== 'small';
314294
const showCalendar = isLargeOrMediumWindowSize || this.state.showCalendar;
315295
const { selectedDefaultRange, selectedEndDate, selectedStartDate } = this.state;
316-
const selectedEndDateFromPropsAsMoment = moment.unix(this.props.selectedEndDate);
317-
const selectedStartDateFromPropsAsMoment = moment.unix(this.props.selectedStartDate);
296+
297+
const defaultRangesWithCopy = [
298+
{
299+
displayValue: getTranslation('This Month',),
300+
getEndDate: () => moment().endOf('month').unix(),
301+
getStartDate: () => moment().startOf('month').unix()
302+
},
303+
{
304+
displayValue: getTranslation('Last 30 Days',),
305+
getEndDate: () => moment().endOf('day').unix(),
306+
getStartDate: () => moment().subtract(29, 'days').startOf('day').unix()
307+
},
308+
{
309+
displayValue: getTranslation('Last Month',),
310+
getEndDate: () => moment().subtract(1, 'months').endOf('month').unix(),
311+
getStartDate: () => moment().subtract(1, 'months').startOf('month').unix()
312+
},
313+
{
314+
displayValue: getTranslation('Last 90 Days',),
315+
getEndDate: () => moment().endOf('day').unix(),
316+
getStartDate: () => moment().subtract(89, 'days').startOf('day').unix()
317+
},
318+
{
319+
displayValue: getTranslation('Year To Date',),
320+
getEndDate: () => moment().endOf('day').unix(),
321+
getStartDate: () => moment().startOf('year').unix()
322+
},
323+
{
324+
displayValue: getTranslation('Last Year',),
325+
getEndDate: () => moment().endOf('year').subtract(1, 'y').unix(),
326+
getStartDate: () => moment().startOf('year').subtract(1, 'y').unix()
327+
}
328+
]
329+
330+
const weekDayNames = moment.weekdays()
331+
const weekDayObject = moment.weekdaysMin().map((d, index) => ({ label: d.charAt(0).toUpperCase(), value: weekDayNames[index].charAt(0).toUpperCase()}))
318332

319333
const mergedFocusTrapProps = {
320334
focusTrapOptions: {
@@ -329,7 +343,7 @@ class DateRangePicker extends React.Component {
329343
'aria-haspopup': true,
330344
'aria-label': `${placeholderText}${
331345
this.props.selectedStartDate && this.props.selectedEndDate
332-
? `, ${selectedStartDateFromPropsAsMoment.format('MMMM Do, YYYY')} to ${selectedEndDateFromPropsAsMoment.format('MMMM Do, YYYY')} currently selected`
346+
? getTranslation(`%1 to %2 currently selected`, this._formatMomentDate(this.props.selectedStartDate), this._formatMomentDate(this.props.selectedEndDate))
333347
: ''
334348
}`,
335349
onClick: this._toggleSelectionPane,
@@ -355,9 +369,9 @@ class DateRangePicker extends React.Component {
355369
<div className='mx-date-range-picker-selected-date-text' style={styles.selectedDateText}>
356370
{this.props.selectedStartDate && this.props.selectedEndDate ? (
357371
<div>
358-
<span>{selectedStartDateFromPropsAsMoment.format(this._getDateFormat(isLargeOrMediumWindowSize))}</span>
372+
<span>{this._formatMomentDate(this.props.selectedStartDate, this._getDateFormat(isLargeOrMediumWindowSize))}</span>
359373
<span> - </span>
360-
<span>{selectedEndDateFromPropsAsMoment.format(this._getDateFormat(isLargeOrMediumWindowSize))}</span>
374+
<span>{this._formatMomentDate(this.props.selectedEndDate, this._getDateFormat(isLargeOrMediumWindowSize))}</span>
361375
</div>
362376
) : placeholderText}
363377
</div>
@@ -391,9 +405,10 @@ class DateRangePicker extends React.Component {
391405
<div>
392406
{this.props.showDefaultRanges && (
393407
<SelectionPane
394-
defaultRanges={this.props.defaultRanges}
408+
defaultRanges={this.props.defaultRanges || defaultRangesWithCopy}
395409
getFromButtonRef={ref => (this._fromButton = ref)}
396410
getToButtonRef={ref => (this._toButton = ref)}
411+
getTranslation={this.props.getTranslation}
397412
handleDefaultRangeSelection={
398413
this._handleDefaultRangeSelection
399414
}
@@ -420,6 +435,7 @@ class DateRangePicker extends React.Component {
420435
<div className='mx-date-range-picker-calendar-header' style={styles.calendarHeader}>
421436
<MonthSelector
422437
currentDate={this.state.currentDate}
438+
getTranslation={this.props.getTranslation}
423439
setCurrentDate={currentDate =>
424440
this.setState({
425441
currentDate,
@@ -428,6 +444,7 @@ class DateRangePicker extends React.Component {
428444
/>
429445
<YearSelector
430446
currentDate={this.state.currentDate}
447+
getTranslation={this.props.getTranslation}
431448
setCurrentDate={currentDate =>
432449
this.setState({
433450
currentDate,
@@ -436,19 +453,11 @@ class DateRangePicker extends React.Component {
436453
/>
437454
</div>
438455
<div className='mx-date-range-picker-week-label' style={styles.calendarWeek}>
439-
{[
440-
{ label: 'S', value: 'Sunday' },
441-
{ label: 'M', value: 'Monday' },
442-
{ label: 'T', value: 'Tuesday' },
443-
{ label: 'W', value: 'Wednesday' },
444-
{ label: 'T', value: 'Thursday' },
445-
{ label: 'F', value: 'Friday' },
446-
{ label: 'S', value: 'Saturday' }
447-
].map(day => {
456+
{weekDayObject.map((day, index) => {
448457
return (
449458
<div
450459
aria-hidden={true}
451-
key={day.value}
460+
key={`${day.value}-${index}`}
452461
style={styles.calendarWeekDay}
453462
>
454463
{day.label}
@@ -466,6 +475,7 @@ class DateRangePicker extends React.Component {
466475
getDateRangePosition={
467476
this._getDateRangePosition
468477
}
478+
getTranslation={this.props.getTranslation}
469479
handleDateHover={this._handleDateHover}
470480
handleDateSelect={this._handleDateSelect}
471481
handleKeyDown={this._handleDayKeyDown}
@@ -482,18 +492,18 @@ class DateRangePicker extends React.Component {
482492
</div>
483493
<div style={styles.bottomPane}>
484494
<Button
485-
aria-label='Cancel Date Range Selection'
495+
aria-label={getTranslation('Cancel Date Range Selection')}
486496
onClick={() => {
487497
this.props.onCancel()
488498
this._resetToPropValuesAndClose();
489499
}}
490500
theme={this.props.theme}
491501
type='secondary'
492502
>
493-
Cancel
503+
{getTranslation('Cancel')}
494504
</Button>
495505
<Button
496-
aria-label='Apply Date Range Selection'
506+
aria-label={getTranslation('Apply Date Range Selection')}
497507
onClick={() => {
498508
this.props.onDateRangeSelect(
499509
selectedStartDate,
@@ -507,7 +517,7 @@ class DateRangePicker extends React.Component {
507517
theme={this.props.theme}
508518
type={selectedStartDate && selectedEndDate ? 'primary' : 'disabled'}
509519
>
510-
Apply
520+
{getTranslation('Apply')}
511521
</Button>
512522
</div>
513523
</div>

src/components/DateRangePicker/MonthTable.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class MonthTable extends React.Component {
3434
currentDate,
3535
focusedDay,
3636
getDateRangePosition,
37+
getTranslation,
3738
handleDateHover,
3839
handleDateSelect,
3940
handleKeyDown,
@@ -72,20 +73,20 @@ class MonthTable extends React.Component {
7273
* Thursday, April 13th, 2018, selected end date for range.
7374
* */
7475
let ariaLabelStateText = '';
75-
const ariaLabelBeginningText = `Select ${selectedBox === SelectedBox.FROM ? 'start' : 'end'} date for range, `;
76+
const ariaLabelBeginningText = `Select ${selectedBox === SelectedBox.FROM ? 'start' : 'end'} date for range`;
7677
const ariaLabelDateText = moment(startDate).format('dddd, MMMM Do, YYYY');
7778

7879
if (!isSelectedDay && isActiveRange) {
79-
ariaLabelStateText = ', within selected range';
80+
ariaLabelStateText = 'within selected range.';
8081
} else if (isSelectedStartDay) {
81-
ariaLabelStateText = ', selected start date for range.';
82+
ariaLabelStateText = 'selected start date for range.';
8283
} else if (isSelectedEndDay) {
83-
ariaLabelStateText = ', selected end date for range.';
84+
ariaLabelStateText = 'selected end date for range.';
8485
}
8586

8687
const day = (
8788
<a
88-
aria-label={ariaLabelBeginningText + ariaLabelDateText + ariaLabelStateText}
89+
aria-label={getTranslation(`${ariaLabelBeginningText}, %1, ${ariaLabelStateText}`, ariaLabelDateText)}
8990
aria-pressed={isSelectedDay}
9091
className={`${css({
9192
...styles.calendarDay,
@@ -121,6 +122,7 @@ MonthTable.propTypes = {
121122
currentDate: PropTypes.number,
122123
focusedDay: PropTypes.number,
123124
getDateRangePosition: PropTypes.func,
125+
getTranslation: PropTypes.func,
124126
handleDateHover: PropTypes.func,
125127
handleDateSelect: PropTypes.func,
126128
handleKeyDown: PropTypes.func,

src/components/DateRangePicker/SelectionPane.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class SelectionPane extends React.Component {
1616
defaultRanges: PropTypes.array,
1717
getFromButtonRef: PropTypes.func,
1818
getToButtonRef: PropTypes.func,
19+
getTranslation: PropTypes.func,
20+
locale: PropTypes.string,
1921
onDateBoxClick: PropTypes.func,
2022
selectedBox: PropTypes.string,
2123
selectedEndDate: PropTypes.number,
@@ -24,43 +26,53 @@ class SelectionPane extends React.Component {
2426
theme: themeShape
2527
};
2628

29+
componentDidMount() {
30+
moment.locale(this.props.locale)
31+
}
32+
2733
_handleDateBoxClick = (date, selectedBox) => {
2834
this.props.onDateBoxClick(date, selectedBox);
2935
}
3036

37+
_formatDate = (date) => {
38+
const d = moment.unix(date).format('MMM D, YYYY')
39+
40+
return d.charAt(0).toUpperCase() + d.slice(1)
41+
}
42+
3143
render () {
3244
const theme = StyleUtils.mergeTheme(this.props.theme);
3345
const styles = this.styles(theme);
34-
const { selectedStartDate, selectedEndDate } = this.props;
46+
const { getTranslation, selectedStartDate, selectedEndDate } = this.props;
3547

3648
return (
3749
<div className='mx-selection-pane' style={styles.container}>
3850
<div>
39-
<label style={styles.boxLabel}>From</label>
51+
<label style={styles.boxLabel}>{getTranslation('From')}</label>
4052
<button
41-
aria-label={`Select Start Date, ${selectedStartDate ? 'Current start date is ' + moment.unix(selectedStartDate).format('MMM D, YYYY') : ''}`}
53+
aria-label={getTranslation(`Select Start Date${selectedStartDate ? ', Current start date is' : ''} %1`, this._formatDate(selectedStartDate))}
4254
className='mx-selection-pane-from-field'
4355
onClick={() => this._handleDateBoxClick(selectedStartDate, SelectedBox.FROM)}
4456
ref={this.props.getFromButtonRef}
4557
style={{ ...styles.dateSelectBox, ...this.props.selectedBox === SelectedBox.FROM ? styles.selectedDateSelectBox : {}}}
4658
>
47-
{selectedStartDate ? moment.unix(selectedStartDate).format('MMM D, YYYY') : 'Select Start Date'}
59+
{selectedStartDate ? this._formatDate(selectedStartDate) : getTranslation('Select Start Date')}
4860
</button>
4961

50-
<label style={styles.boxLabel}>To</label>
62+
<label style={styles.boxLabel}>{getTranslation('To')}</label>
5163
<button
52-
aria-label={`Select End Date, ${selectedEndDate ? 'Current end date is ' + moment.unix(selectedEndDate).format('MMM D, YYYY') : ''}`}
64+
aria-label={getTranslation(`Select End Date${selectedEndDate ? ', Current end date is' : ''} %1`, this._formatDate(selectedEndDate))}
5365
className='mx-selection-pane-to-field'
5466
onClick={() => this._handleDateBoxClick(selectedEndDate, SelectedBox.TO)}
5567
ref={this.props.getToButtonRef}
5668
style={{ ...styles.dateSelectBox, ...this.props.selectedBox === SelectedBox.TO ? styles.selectedDateSelectBox : {}}}
5769
>
58-
{selectedEndDate ? moment.unix(selectedEndDate).format('MMM D, YYYY') : 'Select End Date'}
70+
{selectedEndDate ? this._formatDate(selectedEndDate) : getTranslation('Select End Date')}
5971
</button>
6072
</div>
6173
<div>
6274
<div style={{ ...styles.defaultRangesTitle, color: theme.Colors.PRIMARY }}>
63-
Select a Range
75+
{getTranslation('Select a Range')}
6476
</div>
6577
<DefaultRanges {...this.props} styles={styles} theme={theme} />
6678
</div>

0 commit comments

Comments
 (0)