mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 19:57:22 +08:00
fix(datetime): ensure that default month shown is always in bounds (#25351)
Resolves #25320
This commit is contained in:
@ -37,7 +37,7 @@ import {
|
||||
getPreviousYear,
|
||||
getStartOfWeek,
|
||||
} from './utils/manipulation';
|
||||
import { convertToArrayOfNumbers, getPartsFromCalendarDay, parseDate } from './utils/parse';
|
||||
import { clampDate, convertToArrayOfNumbers, getPartsFromCalendarDay, parseDate } from './utils/parse';
|
||||
import {
|
||||
getCalendarDayState,
|
||||
isDayDisabled,
|
||||
@ -472,7 +472,7 @@ export class Datetime implements ComponentInterface {
|
||||
/**
|
||||
* Resets the internal state of the datetime but does not update the value.
|
||||
* Passing a valid ISO-8601 string will reset the state of the component to the provided date.
|
||||
* If no value is provided, the internal state will be reset to today.
|
||||
* If no value is provided, the internal state will be reset to the clamped value of the min, max and today.
|
||||
*/
|
||||
@Method()
|
||||
async reset(startDate?: string) {
|
||||
@ -1083,8 +1083,8 @@ export class Datetime implements ComponentInterface {
|
||||
|
||||
private processValue = (value?: string | null) => {
|
||||
this.highlightActiveParts = !!value;
|
||||
const valueToProcess = value || getToday();
|
||||
const { month, day, year, hour, minute, tzOffset } = parseDate(valueToProcess);
|
||||
const valueToProcess = parseDate(value || getToday());
|
||||
const { month, day, year, hour, minute, tzOffset } = clampDate(valueToProcess, this.minParts, this.maxParts);
|
||||
|
||||
this.setWorkingParts({
|
||||
month,
|
||||
@ -1093,7 +1093,7 @@ export class Datetime implements ComponentInterface {
|
||||
hour,
|
||||
minute,
|
||||
tzOffset,
|
||||
ampm: hour >= 12 ? 'pm' : 'am',
|
||||
ampm: hour! >= 12 ? 'pm' : 'am',
|
||||
});
|
||||
|
||||
this.activeParts = {
|
||||
@ -1103,7 +1103,7 @@ export class Datetime implements ComponentInterface {
|
||||
hour,
|
||||
minute,
|
||||
tzOffset,
|
||||
ampm: hour >= 12 ? 'pm' : 'am',
|
||||
ampm: hour! >= 12 ? 'pm' : 'am',
|
||||
};
|
||||
};
|
||||
|
||||
@ -1676,8 +1676,8 @@ export class Datetime implements ComponentInterface {
|
||||
const { hours, minutes, am, pm } = generateTime(
|
||||
workingParts,
|
||||
use24Hour ? 'h23' : 'h12',
|
||||
this.minParts,
|
||||
this.maxParts,
|
||||
this.value ? this.minParts : undefined,
|
||||
this.value ? this.maxParts : undefined,
|
||||
this.parsedHourValues,
|
||||
this.parsedMinuteValues
|
||||
);
|
||||
|
@ -47,4 +47,33 @@ test.describe('datetime: minmax', () => {
|
||||
expect(nextButton).toBeDisabled();
|
||||
expect(prevButton).toBeEnabled();
|
||||
});
|
||||
|
||||
test.describe('when the datetime does not have a value', () => {
|
||||
test('all time values should be available for selection', async ({ page }) => {
|
||||
/**
|
||||
* When the datetime does not have an initial value and today falls outside of
|
||||
* the specified min and max values, all times values should be available for selection.
|
||||
*/
|
||||
await page.setContent(`
|
||||
<ion-datetime min="2022-04-22T04:10:00" max="2022-05-21T21:30:00"></ion-datetime>
|
||||
`);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
|
||||
|
||||
await page.click('.time-body');
|
||||
await ionPopoverDidPresent.next();
|
||||
|
||||
const hours = page.locator(
|
||||
'ion-popover ion-picker-column-internal:nth-child(1) .picker-item:not(.picker-item-empty)'
|
||||
);
|
||||
const minutes = page.locator(
|
||||
'ion-popover ion-picker-column-internal:nth-child(2) .picker-item:not(.picker-item-empty)'
|
||||
);
|
||||
|
||||
expect(await hours.count()).toBe(12);
|
||||
expect(await minutes.count()).toBe(60);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getPartsFromCalendarDay } from '../utils/parse';
|
||||
import { clampDate, getPartsFromCalendarDay } from '../utils/parse';
|
||||
|
||||
describe('getPartsFromCalendarDay()', () => {
|
||||
it('should extract DatetimeParts from a calendar day element', () => {
|
||||
@ -18,3 +18,46 @@ describe('getPartsFromCalendarDay()', () => {
|
||||
});
|
||||
|
||||
// TODO: parseDate()
|
||||
|
||||
describe('clampDate()', () => {
|
||||
const minParts = {
|
||||
year: 2021,
|
||||
month: 6,
|
||||
day: 5,
|
||||
};
|
||||
|
||||
const maxParts = {
|
||||
year: 2021,
|
||||
month: 8,
|
||||
day: 19,
|
||||
};
|
||||
it('should return the max month when the value is greater than the max', () => {
|
||||
const dateParts = {
|
||||
year: 2022,
|
||||
month: 5,
|
||||
day: 24,
|
||||
};
|
||||
const value = clampDate(dateParts, minParts, maxParts);
|
||||
expect(value).toStrictEqual(maxParts);
|
||||
});
|
||||
|
||||
it('should return the min month when the value is less than the min', () => {
|
||||
const dateParts = {
|
||||
year: 2020,
|
||||
month: 5,
|
||||
day: 24,
|
||||
};
|
||||
const value = clampDate(dateParts, minParts, maxParts);
|
||||
expect(value).toStrictEqual(minParts);
|
||||
});
|
||||
|
||||
it('should return the value when the value is greater than the min and less than the max', () => {
|
||||
const dateParts = {
|
||||
year: 2021,
|
||||
month: 7,
|
||||
day: 10,
|
||||
};
|
||||
const value = clampDate(dateParts, minParts, maxParts);
|
||||
expect(value).toStrictEqual(dateParts);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,7 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
|
||||
import { isAfter, isBefore } from './comparison';
|
||||
|
||||
const ISO_8601_REGEXP =
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
|
||||
@ -106,3 +108,16 @@ export const parseDate = (val: string | undefined | null): any | undefined => {
|
||||
tzOffset,
|
||||
};
|
||||
};
|
||||
|
||||
export const clampDate = (
|
||||
dateParts: DatetimeParts,
|
||||
minParts?: DatetimeParts,
|
||||
maxParts?: DatetimeParts
|
||||
): DatetimeParts => {
|
||||
if (minParts && isBefore(dateParts, minParts)) {
|
||||
return minParts;
|
||||
} else if (maxParts && isAfter(dateParts, maxParts)) {
|
||||
return maxParts;
|
||||
}
|
||||
return dateParts;
|
||||
};
|
||||
|
Reference in New Issue
Block a user