-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[DatePicker] Add locale prop for Date Picker i18n #874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,6 @@ import { | |
Year, | ||
isDateAfter, | ||
isDateBefore, | ||
getNextDisplayYear, | ||
getNextDisplayMonth, | ||
getPreviousDisplayYear, | ||
getPreviousDisplayMonth, | ||
Weekdays, | ||
isSameDay, | ||
} from '@shopify/javascript-utilities/dates'; | ||
|
@@ -42,6 +38,8 @@ export interface BaseProps { | |
multiMonth?: boolean; | ||
/** First day of week. Sunday by default */ | ||
weekStartsOn?: Weekdays; | ||
/** Locale for date formatting. 'en' by default */ | ||
locale?: string; | ||
/** Callback when date is selected. */ | ||
onChange?(date: Range): void; | ||
/** Callback when month is changed. */ | ||
|
@@ -84,32 +82,22 @@ export class DatePicker extends React.PureComponent<CombinedProps, State> { | |
disableDatesBefore, | ||
disableDatesAfter, | ||
weekStartsOn = Weekdays.Sunday, | ||
polaris: {intl}, | ||
locale = 'en', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we get the locale at the provider level or something like that? Or maybe not? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, this is more for the Polaris team to answer, but is this a pattern they want to have in their library for their components? |
||
} = this.props; | ||
|
||
const {hoverDate, focusDate} = this.state; | ||
|
||
const showNextYear = getNextDisplayYear(month, year); | ||
const showNextMonth = getNextDisplayMonth(month); | ||
|
||
const showNextToNextYear = getNextDisplayYear(showNextMonth, showNextYear); | ||
const showNextToNextMonth = getNextDisplayMonth(showNextMonth); | ||
|
||
const showPreviousYear = getPreviousDisplayYear(month, year); | ||
const showPreviousMonth = getPreviousDisplayMonth(month); | ||
|
||
const previousMonthName = Months[showPreviousMonth]; | ||
const nextMonth = multiMonth | ||
? Months[showNextToNextMonth] | ||
: Months[showNextMonth]; | ||
const nextYear = multiMonth ? showNextToNextYear : showNextYear; | ||
const visibleMonth = new Date(year, month); | ||
const previousVisibleMonth = new Date(year, month - 1); | ||
const nextVisibleMonth = new Date(year, month + 1); | ||
const nextToNextVisibleMonth = new Date(year, month + 2); | ||
|
||
const secondDatePicker = multiMonth ? ( | ||
<Month | ||
onFocus={this.handleFocus} | ||
focusedDate={focusDate} | ||
month={showNextMonth} | ||
year={showNextYear} | ||
month={nextVisibleMonth.getMonth()} | ||
year={nextVisibleMonth.getFullYear()} | ||
selected={deriveRange(selected)} | ||
hoverDate={hoverDate} | ||
onChange={this.handleDateSelection} | ||
|
@@ -118,6 +106,7 @@ export class DatePicker extends React.PureComponent<CombinedProps, State> { | |
disableDatesAfter={disableDatesAfter} | ||
allowRange={allowRange} | ||
weekStartsOn={weekStartsOn} | ||
locale={locale} | ||
/> | ||
) : null; | ||
|
||
|
@@ -132,41 +121,39 @@ export class DatePicker extends React.PureComponent<CombinedProps, State> { | |
<Button | ||
plain | ||
icon="arrowLeft" | ||
accessibilityLabel={intl.translate( | ||
'Polaris.DatePicker.previousMonth', | ||
{ | ||
previousMonthName, | ||
showPreviousYear, | ||
}, | ||
)} | ||
accessibilityLabel={Intl.DateTimeFormat(locale, { | ||
month: 'long', | ||
year: 'numeric', | ||
}).format(previousVisibleMonth)} | ||
// eslint-disable-next-line react/jsx-no-bind | ||
onClick={this.handleMonthChangeClick.bind( | ||
null, | ||
showPreviousMonth, | ||
showPreviousYear, | ||
previousVisibleMonth.getMonth(), | ||
previousVisibleMonth.getFullYear(), | ||
)} | ||
/> | ||
<Button | ||
plain | ||
icon="arrowRight" | ||
accessibilityLabel={intl.translate('Polaris.DatePicker.nextMonth', { | ||
nextMonth, | ||
nextYear, | ||
})} | ||
accessibilityLabel={Intl.DateTimeFormat(locale, { | ||
month: 'long', | ||
year: 'numeric', | ||
}).format(multiMonth ? nextToNextVisibleMonth : nextVisibleMonth)} | ||
// eslint-disable-next-line react/jsx-no-bind | ||
onClick={this.handleMonthChangeClick.bind( | ||
null, | ||
showNextMonth, | ||
showNextYear, | ||
nextVisibleMonth.getMonth(), | ||
nextVisibleMonth.getFullYear(), | ||
)} | ||
/> | ||
</div> | ||
<div className={styles.MonthContainer}> | ||
<Month | ||
locale={locale} | ||
onFocus={this.handleFocus} | ||
focusedDate={focusDate} | ||
month={month} | ||
year={year} | ||
month={visibleMonth.getMonth()} | ||
year={visibleMonth.getFullYear()} | ||
selected={deriveRange(selected)} | ||
hoverDate={hoverDate} | ||
onChange={this.handleDateSelection} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,13 @@ import * as React from 'react'; | |
import {classNames} from '@shopify/react-utilities/styles'; | ||
import {noop} from '@shopify/javascript-utilities/other'; | ||
import {autobind} from '@shopify/javascript-utilities/decorators'; | ||
import {Months, isSameDay} from '@shopify/javascript-utilities/dates'; | ||
import {isSameDay} from '@shopify/javascript-utilities/dates'; | ||
import {withAppProvider, WithAppProviderProps} from '../../../AppProvider'; | ||
|
||
import * as styles from '../../DatePicker.scss'; | ||
|
||
export interface Props { | ||
locale?: string; | ||
focused?: boolean; | ||
day?: Date; | ||
selected?: boolean; | ||
|
@@ -42,6 +43,7 @@ export class Day extends React.PureComponent<CombinedProps, never> { | |
inHoveringRange, | ||
disabled, | ||
polaris: {intl}, | ||
locale = 'en', | ||
} = this.props; | ||
|
||
const handleHover = onHover.bind(null, day); | ||
|
@@ -57,14 +59,16 @@ export class Day extends React.PureComponent<CombinedProps, never> { | |
today && styles['Day-today'], | ||
(inRange || inHoveringRange) && !disabled && styles['Day-inRange'], | ||
); | ||
const date = day.getDate(); | ||
const firstDay = day.getDate() === 1; | ||
const tabIndex = | ||
(focused || selected || today || date === 1) && !disabled ? 0 : -1; | ||
(focused || selected || today || firstDay) && !disabled ? 0 : -1; | ||
const ariaLabel = [ | ||
`${today ? intl.translate('Polaris.DatePicker.today') : ''}`, | ||
`${Months[day.getMonth()]} `, | ||
`${date} `, | ||
`${day.getFullYear()}`, | ||
today ? intl.translate('Polaris.DatePicker.today') : '', | ||
Intl.DateTimeFormat(locale, { | ||
year: 'numeric', | ||
month: 'long', | ||
day: 'numeric', | ||
}).format(day), | ||
].join(''); | ||
|
||
return ( | ||
|
@@ -83,7 +87,7 @@ export class Day extends React.PureComponent<CombinedProps, never> { | |
aria-disabled={disabled} | ||
role="gridcell" | ||
> | ||
{date} | ||
{Intl.DateTimeFormat(locale, {day: 'numeric'}).format(day)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
</button> | ||
); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ import { | |
dateIsInRange, | ||
dateIsSelected, | ||
getNewRange, | ||
abbreviationForWeekday, | ||
} from '@shopify/javascript-utilities/dates'; | ||
import {noop} from '@shopify/javascript-utilities/other'; | ||
import {classNames} from '@shopify/react-utilities/styles'; | ||
|
@@ -20,6 +19,7 @@ import Day from '../Day'; | |
import Weekday from '../Weekday'; | ||
|
||
export interface Props { | ||
locale?: string; | ||
focusedDate?: Date; | ||
selected?: Range; | ||
hoverDate?: Date; | ||
|
@@ -32,8 +32,6 @@ export interface Props { | |
onChange?(date: Range): void; | ||
onHover?(hoverEnd: Date): void; | ||
onFocus?(date: Date): void; | ||
monthName?(month: Months): string; | ||
weekdayName?(weekday: Weekdays): string; | ||
} | ||
|
||
const WEEKDAYS = [ | ||
|
@@ -47,6 +45,7 @@ const WEEKDAYS = [ | |
]; | ||
|
||
export default function Month({ | ||
locale = 'en', | ||
focusedDate, | ||
selected, | ||
hoverDate, | ||
|
@@ -67,26 +66,39 @@ export default function Month({ | |
styles.Title, | ||
current && styles['Month-current'], | ||
); | ||
|
||
const weeks = getWeeksForMonth(month, year, weekStartsOn); | ||
const weekdays = getWeekdaysOrdered(weekStartsOn).map((weekday) => ( | ||
<Weekday | ||
key={weekday} | ||
title={abbreviationForWeekday(weekday)} | ||
current={current && new Date().getDay() === weekday} | ||
label={weekday} | ||
/> | ||
)); | ||
|
||
const weekdayFormat = Intl.DateTimeFormat(locale, {weekday: 'short'}); | ||
|
||
const weekdays = getWeekdaysOrdered(weekStartsOn).map((weekday) => { | ||
// October 1, 2017 is a Sunday | ||
const arbitraryWeekdayDate = new Date(2017, 9, weekday + 1); | ||
|
||
return ( | ||
<Weekday | ||
key={weekday} | ||
title={weekdayFormat.format(arbitraryWeekdayDate)} | ||
current={current && new Date().getDay() === weekday} | ||
label={weekday} | ||
/> | ||
); | ||
}); | ||
|
||
function handleDateClick(selectedDate: Date) { | ||
onChange(getNewRange(allowRange && selected, selectedDate)); | ||
} | ||
|
||
function renderWeek(day: Date, dayIndex: number) { | ||
if (day == null) { | ||
const lastDayOfMonth = new Date(year, (month as number) + 1, 0); | ||
const lastDayOfMonth = new Date(year, month + 1, 0); | ||
return ( | ||
// eslint-disable-next-line react/jsx-no-bind | ||
<Day key={dayIndex} onHover={onHover.bind(null, lastDayOfMonth)} /> | ||
<Day | ||
key={dayIndex} | ||
// eslint-disable-next-line react/jsx-no-bind | ||
onHover={onHover.bind(null, lastDayOfMonth)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if we still need these |
||
locale={locale} | ||
/> | ||
); | ||
} | ||
|
||
|
@@ -96,6 +108,7 @@ export default function Month({ | |
|
||
return ( | ||
<Day | ||
locale={locale} | ||
focused={focusedDate != null && isSameDay(day, focusedDate)} | ||
day={day} | ||
key={dayIndex} | ||
|
@@ -123,7 +136,9 @@ export default function Month({ | |
return ( | ||
<div role="grid" className={styles.Month}> | ||
<div className={className}> | ||
{Months[month]} {year} | ||
{Intl.DateTimeFormat(locale, {month: 'long', year: 'numeric'}).format( | ||
new Date(year, month), | ||
)} | ||
</div> | ||
<div role="rowheader" className={styles.WeekHeadings}> | ||
{weekdays} | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new dependency is to enable Intl to be tested with different locales in jest, where prior it would just default to english no matter what locale you specify in the test..