diff --git a/core/src/components/datetime/datetime.scss b/core/src/components/datetime/datetime.scss index ce8438b6db1..b03b0c01d9b 100644 --- a/core/src/components/datetime/datetime.scss +++ b/core/src/components/datetime/datetime.scss @@ -48,6 +48,35 @@ opacity: 1; } +/** + * Changing the physical order of the + * picker columns in the DOM is added + * work, so we just use `order` instead. + * + * The picker automatically configures + * the text alignment, so when switching + * the order we need to manually switch + * the text alignment too. + */ +:host .datetime-year .order-month-first .month-column { + order: 1; +} + +:host .datetime-year .order-month-first .year-column { + order: 2; +} + +:host .datetime-year .order-year-first .month-column { + order: 2; + + text-align: end; +} + +:host .datetime-year .order-year-first .year-column { + order: 1; + + text-align: start; +} // Calendar // ----------------------------------- diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index 1f9ef3f15b1..2c71c5e2bdc 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -32,7 +32,8 @@ import { getMonthAndYear } from './utils/format'; import { - is24Hour + is24Hour, + isMonthFirstLocale } from './utils/helpers'; import { calculateHourFromAMPM, @@ -1097,25 +1098,31 @@ export class Datetime implements ComponentInterface { } private renderYearView() { - const { presentation, workingParts } = this; + const { presentation, workingParts, locale } = this; const calendarYears = getCalendarYears(this.todayParts, this.minParts, this.maxParts, this.parsedYearValues); const showMonth = presentation !== 'year'; const showYear = presentation !== 'month'; - const months = getPickerMonths(this.locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues); + const months = getPickerMonths(locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues); const years = calendarYears.map(year => { return { text: `${year}`, value: year } }) + const showMonthFirst = isMonthFirstLocale(locale); + const columnOrder = showMonthFirst ? 'month-first' : 'year-first'; return (
-
+
{ showMonth && { @@ -51,3 +52,18 @@ describe('is24Hour()', () => { expect(is24Hour('en-GB-u-hc-h12')).toBe(false); }) }) + +describe('isMonthFirstLocale()', () => { + it('should return true if the locale shows months first', () => { + expect(isMonthFirstLocale('en-US')).toBe(true); + expect(isMonthFirstLocale('en-GB')).toBe(true); + expect(isMonthFirstLocale('es-ES')).toBe(true); + expect(isMonthFirstLocale('ro-RO')).toBe(true); + }); + + it('should return false if the locale shows years first', () => { + expect(isMonthFirstLocale('zh-CN')).toBe(false); + expect(isMonthFirstLocale('ja-JP')).toBe(false); + expect(isMonthFirstLocale('ko-KR')).toBe(false); + }); +}) diff --git a/core/src/components/datetime/test/locale/e2e.ts b/core/src/components/datetime/test/locale/e2e.ts index 6b16e6b7f74..4eca5af824f 100644 --- a/core/src/components/datetime/test/locale/e2e.ts +++ b/core/src/components/datetime/test/locale/e2e.ts @@ -19,3 +19,53 @@ test('locale', async () => { expect(screenshotCompare).toMatchScreenshot(); } }); + +test('it should render month and year with an en-US locale', async () => { + const page = await newE2EPage({ + url: '/src/components/datetime/test/locale?ionic:_testing=true' + }); + + const screenshotCompares = []; + const datetime = await page.find('ion-datetime'); + + datetime.setProperty('locale', 'en-US'); + await page.waitForChanges(); + + const button = await page.find('ion-datetime >>> .calendar-month-year ion-item'); + await button.click(); + await page.waitForChanges(); + + const yearBody = await page.find('ion-datetime >>> .datetime-year-body'); + expect(yearBody).toHaveClass('order-month-first'); + + screenshotCompares.push(await page.compareScreenshot()); + + for (const screenshotCompare of screenshotCompares) { + expect(screenshotCompare).toMatchScreenshot(); + } +}); + +test('it should render year and month with a ja-JP locale', async () => { + const page = await newE2EPage({ + url: '/src/components/datetime/test/locale?ionic:_testing=true' + }); + + const screenshotCompares = []; + const datetime = await page.find('ion-datetime'); + + datetime.setProperty('locale', 'ja-JP'); + await page.waitForChanges(); + + const button = await page.find('ion-datetime >>> .calendar-month-year ion-item'); + await button.click(); + await page.waitForChanges(); + + const yearBody = await page.find('ion-datetime >>> .datetime-year-body'); + expect(yearBody).toHaveClass('order-year-first'); + + screenshotCompares.push(await page.compareScreenshot()); + + for (const screenshotCompare of screenshotCompares) { + expect(screenshotCompare).toMatchScreenshot(); + } +}); diff --git a/core/src/components/datetime/utils/helpers.ts b/core/src/components/datetime/utils/helpers.ts index a0abffdd5ed..d3eac3c4d7b 100644 --- a/core/src/components/datetime/utils/helpers.ts +++ b/core/src/components/datetime/utils/helpers.ts @@ -62,3 +62,28 @@ export const is24Hour = (locale: string, hourCycle?: 'h23' | 'h12') => { export const getNumDaysInMonth = (month: number, year: number) => { return (month === 4 || month === 6 || month === 9 || month === 11) ? 30 : (month === 2) ? isLeapYear(year) ? 29 : 28 : 31; } + +/** + * Certain locales display month then year while + * others display year then month. + * We can use Intl.DateTimeFormat to determine + * the ordering for each locale. + */ +export const isMonthFirstLocale = (locale: string) => { + + /** + * By setting month and year we guarantee that only + * month, year, and literal (slashes '/', for example) + * values are included in the formatToParts results. + * + * The ordering of the parts will be determined by + * the locale. So if the month is the first value, + * then we know month should be shown first. If the + * year is the first value, then we know year should be shown first. + * + * This ordering can be controlled by customizing the locale property. + */ + const parts = new Intl.DateTimeFormat(locale, { month: 'numeric', year: 'numeric' }).formatToParts(new Date()); + + return parts[0].type === 'month'; +}