@ionic/core 7.7.3-dev.11707927732.13defc00 → 7.7.3-dev.11709159644.114cd8b1
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/components/data.js +23 -38
- package/components/ion-content.js +3 -4
- package/components/ion-datetime-button.js +11 -10
- package/components/ion-datetime.js +69 -7
- package/components/ion-searchbar.js +34 -5
- package/dist/cjs/{data-a5109f09.js → data-4487a815.js} +22 -39
- package/dist/cjs/ion-app_8.cjs.entry.js +3 -4
- package/dist/cjs/ion-datetime-button.cjs.entry.js +11 -10
- package/dist/cjs/ion-datetime_3.cjs.entry.js +66 -7
- package/dist/cjs/ion-searchbar.cjs.entry.js +28 -4
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/content/content.js +3 -4
- package/dist/collection/components/datetime/datetime.js +50 -7
- package/dist/collection/components/datetime/test/basic/datetime.e2e.js +77 -0
- package/dist/collection/components/datetime/test/format.spec.js +65 -15
- package/dist/collection/components/datetime/utils/format.js +24 -37
- package/dist/collection/components/datetime/utils/validate.js +45 -0
- package/dist/collection/components/datetime-button/datetime-button.js +11 -10
- package/dist/collection/components/datetime-button/test/basic/datetime-button.e2e.js +65 -0
- package/dist/collection/components/searchbar/searchbar.js +84 -5
- package/dist/collection/components/searchbar/test/searchbar.spec.js +20 -2
- package/dist/docs.json +113 -1
- package/dist/esm/{data-0f3ab200.js → data-bb424ba8.js} +23 -38
- package/dist/esm/ion-app_8.entry.js +3 -4
- package/dist/esm/ion-datetime-button.entry.js +11 -10
- package/dist/esm/ion-datetime_3.entry.js +66 -7
- package/dist/esm/ion-searchbar.entry.js +29 -5
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm-es5/data-bb424ba8.js +4 -0
- package/dist/esm-es5/ion-app_8.entry.js +1 -1
- package/dist/esm-es5/ion-datetime-button.entry.js +1 -1
- package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
- package/dist/esm-es5/ion-searchbar.entry.js +1 -1
- package/dist/esm-es5/ionic.js +1 -1
- package/dist/esm-es5/loader.js +1 -1
- package/dist/html.html-data.json +12 -0
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-0b34d3a7.system.js +1 -1
- package/dist/ionic/p-2d95cfc5.entry.js +4 -0
- package/dist/ionic/p-31450f73.entry.js +4 -0
- package/dist/ionic/p-3789920e.js +4 -0
- package/dist/ionic/p-8a1b0abb.system.js +4 -0
- package/dist/ionic/p-9f207856.entry.js +4 -0
- package/dist/ionic/p-b18b2148.system.entry.js +4 -0
- package/dist/ionic/p-d8ee0efe.system.entry.js +4 -0
- package/dist/ionic/p-dbd70d97.system.entry.js +4 -0
- package/dist/ionic/p-e942b677.entry.js +4 -0
- package/dist/ionic/p-e9b9a128.system.entry.js +4 -0
- package/dist/types/components/datetime/datetime-interface.d.ts +10 -0
- package/dist/types/components/datetime/datetime.d.ts +9 -1
- package/dist/types/components/datetime/utils/format.d.ts +7 -13
- package/dist/types/components/datetime/utils/validate.d.ts +8 -0
- package/dist/types/components/searchbar/searchbar.d.ts +38 -0
- package/dist/types/components.d.ts +34 -2
- package/hydrate/index.js +130 -60
- package/package.json +1 -1
- package/dist/esm-es5/data-0f3ab200.js +0 -4
- package/dist/ionic/p-0e23c289.system.entry.js +0 -4
- package/dist/ionic/p-206c34f8.system.entry.js +0 -4
- package/dist/ionic/p-2327064c.entry.js +0 -4
- package/dist/ionic/p-23c73017.entry.js +0 -4
- package/dist/ionic/p-68ba8e77.system.entry.js +0 -4
- package/dist/ionic/p-7330c044.system.js +0 -4
- package/dist/ionic/p-8821fd17.entry.js +0 -4
- package/dist/ionic/p-a6e465f5.entry.js +0 -4
- package/dist/ionic/p-e81bf09e.system.entry.js +0 -4
- package/dist/ionic/p-f7dce541.js +0 -4
|
@@ -402,3 +402,80 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
|
|
402
402
|
});
|
|
403
403
|
});
|
|
404
404
|
});
|
|
405
|
+
/**
|
|
406
|
+
* This behavior does not differ across
|
|
407
|
+
* directions.
|
|
408
|
+
*/
|
|
409
|
+
configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
|
410
|
+
test.describe(title('datetime: formatOptions'), () => {
|
|
411
|
+
test('should format header and time button', async ({ page }) => {
|
|
412
|
+
await page.setContent(`
|
|
413
|
+
<ion-datetime value="2022-02-01T16:30:00">
|
|
414
|
+
<span slot="title">Select Date</span>
|
|
415
|
+
</ion-datetime>
|
|
416
|
+
<script>
|
|
417
|
+
const datetime = document.querySelector('ion-datetime');
|
|
418
|
+
datetime.formatOptions = {
|
|
419
|
+
time: { hour: '2-digit', minute: '2-digit' },
|
|
420
|
+
date: { day: '2-digit', month: 'long', era: 'short' },
|
|
421
|
+
}
|
|
422
|
+
</script>
|
|
423
|
+
`, config);
|
|
424
|
+
await page.locator('.datetime-ready').waitFor();
|
|
425
|
+
const headerDate = page.locator('ion-datetime .datetime-selected-date');
|
|
426
|
+
await expect(headerDate).toHaveText('February 01 AD');
|
|
427
|
+
const timeBody = page.locator('ion-datetime .time-body');
|
|
428
|
+
await expect(timeBody).toHaveText('04:30 PM');
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
/**
|
|
433
|
+
* This behavior does not differ across
|
|
434
|
+
* modes/directions.
|
|
435
|
+
*/
|
|
436
|
+
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
|
437
|
+
test.describe(title('datetime: formatOptions misconfiguration errors'), () => {
|
|
438
|
+
test('should log a warning if time zone is provided', async ({ page }) => {
|
|
439
|
+
const logs = [];
|
|
440
|
+
page.on('console', (msg) => {
|
|
441
|
+
if (msg.type() === 'warning') {
|
|
442
|
+
logs.push(msg.text());
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
await page.setContent(`
|
|
446
|
+
<ion-datetime value="2022-02-01T16:30:00" presentation="date">
|
|
447
|
+
<span slot="title">Select Date</span>
|
|
448
|
+
</ion-datetime>
|
|
449
|
+
<script>
|
|
450
|
+
const datetime = document.querySelector('ion-datetime');
|
|
451
|
+
datetime.formatOptions = {
|
|
452
|
+
date: { timeZone: 'UTC' },
|
|
453
|
+
}
|
|
454
|
+
</script>
|
|
455
|
+
`, config);
|
|
456
|
+
await page.locator('.datetime-ready').waitFor();
|
|
457
|
+
expect(logs.length).toBe(1);
|
|
458
|
+
expect(logs[0]).toContain('[Ionic Warning]: Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".');
|
|
459
|
+
});
|
|
460
|
+
test('should log a warning if the required formatOptions are not provided for a presentation', async ({ page }) => {
|
|
461
|
+
const logs = [];
|
|
462
|
+
page.on('console', (msg) => {
|
|
463
|
+
if (msg.type() === 'warning') {
|
|
464
|
+
logs.push(msg.text());
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
await page.setContent(`
|
|
468
|
+
<ion-datetime value="2022-02-01T16:30:00">
|
|
469
|
+
<span slot="title">Select Date</span>
|
|
470
|
+
</ion-datetime>
|
|
471
|
+
<script>
|
|
472
|
+
const datetime = document.querySelector('ion-datetime');
|
|
473
|
+
datetime.formatOptions = {}
|
|
474
|
+
</script>
|
|
475
|
+
`, config);
|
|
476
|
+
await page.locator('.datetime-ready').waitFor();
|
|
477
|
+
expect(logs.length).toBe(1);
|
|
478
|
+
expect(logs[0]).toContain("[Ionic Warning]: Datetime: The 'date-time' presentation requires either a date or time object (or both) in formatOptions.");
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { generateDayAriaLabel,
|
|
4
|
+
import { generateDayAriaLabel, getFormattedHour, addTimePadding, getMonthAndYear, getLocalizedDayPeriod, getLocalizedTime, stripTimeZone, } from "../utils/format";
|
|
5
5
|
describe('generateDayAriaLabel()', () => {
|
|
6
6
|
it('should return Wednesday, May 12', () => {
|
|
7
7
|
const reference = { month: 5, day: 12, year: 2021 };
|
|
@@ -24,20 +24,6 @@ describe('generateDayAriaLabel()', () => {
|
|
|
24
24
|
expect(generateDayAriaLabel('en-US', false, reference)).toEqual('Saturday, April 1');
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
|
-
describe('getMonthAndDay()', () => {
|
|
28
|
-
it('should return Tue, May 11', () => {
|
|
29
|
-
expect(getMonthAndDay('en-US', { month: 5, day: 11, year: 2021 })).toEqual('Tue, May 11');
|
|
30
|
-
});
|
|
31
|
-
it('should return mar, 11 may', () => {
|
|
32
|
-
expect(getMonthAndDay('es-ES', { month: 5, day: 11, year: 2021 })).toEqual('mar, 11 may');
|
|
33
|
-
});
|
|
34
|
-
it('should return Sat, Apr 1', () => {
|
|
35
|
-
expect(getMonthAndDay('en-US', { month: 4, day: 1, year: 2006 })).toEqual('Sat, Apr 1');
|
|
36
|
-
});
|
|
37
|
-
it('should return sáb, 1 abr', () => {
|
|
38
|
-
expect(getMonthAndDay('es-ES', { month: 4, day: 1, year: 2006 })).toEqual('sáb, 1 abr');
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
27
|
describe('getFormattedHour()', () => {
|
|
42
28
|
it('should only add padding if using 24 hour time', () => {
|
|
43
29
|
expect(getFormattedHour(1, 'h11')).toEqual('1');
|
|
@@ -121,4 +107,68 @@ describe('getLocalizedTime', () => {
|
|
|
121
107
|
expect(getLocalizedTime('en-US', datetimeParts, 'h12')).toEqual('10:40 PM');
|
|
122
108
|
expect(getLocalizedTime('en-US', datetimeParts, 'h23')).toEqual('22:40');
|
|
123
109
|
});
|
|
110
|
+
it('should use formatOptions', () => {
|
|
111
|
+
const datetimeParts = {
|
|
112
|
+
day: 1,
|
|
113
|
+
month: 1,
|
|
114
|
+
year: 2022,
|
|
115
|
+
hour: 9,
|
|
116
|
+
minute: 40,
|
|
117
|
+
};
|
|
118
|
+
const formatOptions = {
|
|
119
|
+
hour: '2-digit',
|
|
120
|
+
minute: '2-digit',
|
|
121
|
+
dayPeriod: 'short',
|
|
122
|
+
day: '2-digit',
|
|
123
|
+
};
|
|
124
|
+
// Even though this method is intended to be used for time, the date may be displayed as well when passing formatOptions
|
|
125
|
+
expect(getLocalizedTime('en-US', datetimeParts, 'h12', formatOptions)).toEqual('01, 09:40 in the morning');
|
|
126
|
+
});
|
|
127
|
+
it('should override provided time zone with UTC', () => {
|
|
128
|
+
const datetimeParts = {
|
|
129
|
+
day: 1,
|
|
130
|
+
month: 1,
|
|
131
|
+
year: 2022,
|
|
132
|
+
hour: 9,
|
|
133
|
+
minute: 40,
|
|
134
|
+
};
|
|
135
|
+
const formatOptions = {
|
|
136
|
+
timeZone: 'Australia/Sydney',
|
|
137
|
+
timeZoneName: 'long',
|
|
138
|
+
hour: 'numeric',
|
|
139
|
+
minute: 'numeric',
|
|
140
|
+
};
|
|
141
|
+
expect(getLocalizedTime('en-US', datetimeParts, 'h12', formatOptions)).toEqual('9:40 AM');
|
|
142
|
+
});
|
|
143
|
+
it('should not include time zone name', () => {
|
|
144
|
+
const datetimeParts = {
|
|
145
|
+
day: 1,
|
|
146
|
+
month: 1,
|
|
147
|
+
year: 2022,
|
|
148
|
+
hour: 9,
|
|
149
|
+
minute: 40,
|
|
150
|
+
};
|
|
151
|
+
const formatOptions = {
|
|
152
|
+
timeZone: 'America/Los_Angeles',
|
|
153
|
+
timeZoneName: 'long',
|
|
154
|
+
hour: 'numeric',
|
|
155
|
+
minute: 'numeric',
|
|
156
|
+
};
|
|
157
|
+
expect(getLocalizedTime('en-US', datetimeParts, 'h12', formatOptions)).toEqual('9:40 AM');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('stripTimeZone', () => {
|
|
161
|
+
it('should remove the time zone name from the options and set the time zone to UTC', () => {
|
|
162
|
+
const formatOptions = {
|
|
163
|
+
timeZone: 'America/Los_Angeles',
|
|
164
|
+
timeZoneName: 'long',
|
|
165
|
+
hour: 'numeric',
|
|
166
|
+
minute: 'numeric',
|
|
167
|
+
};
|
|
168
|
+
expect(stripTimeZone(formatOptions)).toEqual({
|
|
169
|
+
timeZone: 'UTC',
|
|
170
|
+
hour: 'numeric',
|
|
171
|
+
minute: 'numeric',
|
|
172
|
+
});
|
|
173
|
+
});
|
|
124
174
|
});
|
|
@@ -9,7 +9,26 @@ const getFormattedDayPeriod = (dayPeriod) => {
|
|
|
9
9
|
}
|
|
10
10
|
return dayPeriod.toUpperCase();
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Including time zone options may lead to the rendered text showing a
|
|
14
|
+
* different time from what was selected in the Datetime, which could cause
|
|
15
|
+
* confusion.
|
|
16
|
+
*/
|
|
17
|
+
export const stripTimeZone = (formatOptions) => {
|
|
18
|
+
return Object.assign(Object.assign({}, formatOptions), {
|
|
19
|
+
/**
|
|
20
|
+
* Setting the time zone to UTC ensures that the value shown is always the
|
|
21
|
+
* same as what was selected and safeguards against older Safari bugs with
|
|
22
|
+
* Intl.DateTimeFormat.
|
|
23
|
+
*/
|
|
24
|
+
timeZone: 'UTC',
|
|
25
|
+
/**
|
|
26
|
+
* We do not want to display the time zone name
|
|
27
|
+
*/
|
|
28
|
+
timeZoneName: undefined
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
export const getLocalizedTime = (locale, refParts, hourCycle, formatOptions = { hour: 'numeric', minute: 'numeric' }) => {
|
|
13
32
|
const timeParts = {
|
|
14
33
|
hour: refParts.hour,
|
|
15
34
|
minute: refParts.minute,
|
|
@@ -17,28 +36,13 @@ export const getLocalizedTime = (locale, refParts, hourCycle) => {
|
|
|
17
36
|
if (timeParts.hour === undefined || timeParts.minute === undefined) {
|
|
18
37
|
return 'Invalid Time';
|
|
19
38
|
}
|
|
20
|
-
return new Intl.DateTimeFormat(locale, {
|
|
21
|
-
hour: 'numeric',
|
|
22
|
-
minute: 'numeric',
|
|
23
|
-
/**
|
|
24
|
-
* Setting the timeZone to UTC prevents
|
|
25
|
-
* new Intl.DatetimeFormat from subtracting
|
|
26
|
-
* the user's current timezone offset
|
|
27
|
-
* when formatting the time.
|
|
28
|
-
*/
|
|
29
|
-
timeZone: 'UTC',
|
|
39
|
+
return new Intl.DateTimeFormat(locale, Object.assign(Object.assign({}, stripTimeZone(formatOptions)), {
|
|
30
40
|
/**
|
|
31
41
|
* We use hourCycle here instead of hour12 due to:
|
|
32
42
|
* https://bugs.chromium.org/p/chromium/issues/detail?id=1347316&q=hour12&can=2
|
|
33
43
|
*/
|
|
34
|
-
hourCycle
|
|
35
|
-
|
|
36
|
-
* Setting Z at the end indicates that this
|
|
37
|
-
* date string is in the UTC time zone. This
|
|
38
|
-
* prevents new Date from adding the time zone
|
|
39
|
-
* offset when getting the ISO string.
|
|
40
|
-
*/
|
|
41
|
-
}).format(new Date(convertDataToISO(Object.assign({
|
|
44
|
+
hourCycle
|
|
45
|
+
})).format(new Date(convertDataToISO(Object.assign({
|
|
42
46
|
/**
|
|
43
47
|
* JS uses a simplified ISO 8601 format which allows for
|
|
44
48
|
* date-only formats and date-time formats, but not
|
|
@@ -125,14 +129,6 @@ export const generateDayAriaLabel = (locale, today, refParts) => {
|
|
|
125
129
|
*/
|
|
126
130
|
return today ? `Today, ${labelString}` : labelString;
|
|
127
131
|
};
|
|
128
|
-
/**
|
|
129
|
-
* Gets the day of the week, month, and day
|
|
130
|
-
* Used for the header in MD mode.
|
|
131
|
-
*/
|
|
132
|
-
export const getMonthAndDay = (locale, refParts) => {
|
|
133
|
-
const date = getNormalizedDate(refParts);
|
|
134
|
-
return new Intl.DateTimeFormat(locale, { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' }).format(date);
|
|
135
|
-
};
|
|
136
132
|
/**
|
|
137
133
|
* Given a locale and a date object,
|
|
138
134
|
* return a formatted string that includes
|
|
@@ -143,15 +139,6 @@ export const getMonthAndYear = (locale, refParts) => {
|
|
|
143
139
|
const date = getNormalizedDate(refParts);
|
|
144
140
|
return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric', timeZone: 'UTC' }).format(date);
|
|
145
141
|
};
|
|
146
|
-
/**
|
|
147
|
-
* Given a locale and a date object,
|
|
148
|
-
* return a formatted string that includes
|
|
149
|
-
* the short month, numeric day, and full year.
|
|
150
|
-
* Example: Apr 22, 2021
|
|
151
|
-
*/
|
|
152
|
-
export const getMonthDayAndYear = (locale, refParts) => {
|
|
153
|
-
return getLocalizedDateTime(locale, refParts, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
154
|
-
};
|
|
155
142
|
/**
|
|
156
143
|
* Given a locale and a date object,
|
|
157
144
|
* return a formatted string that includes
|
|
@@ -201,7 +188,7 @@ export const getNormalizedDate = (refParts) => {
|
|
|
201
188
|
*/
|
|
202
189
|
export const getLocalizedDateTime = (locale, refParts, options) => {
|
|
203
190
|
const date = getNormalizedDate(refParts);
|
|
204
|
-
return getDateTimeFormat(locale, options).format(date);
|
|
191
|
+
return getDateTimeFormat(locale, stripTimeZone(options)).format(date);
|
|
205
192
|
};
|
|
206
193
|
/**
|
|
207
194
|
* Given a locale, DatetimeParts, and options
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
|
+
*/
|
|
4
|
+
import { printIonWarning } from "../../../utils/logging/index";
|
|
5
|
+
/**
|
|
6
|
+
* If a time zone is provided in the format options, the rendered text could
|
|
7
|
+
* differ from what was selected in the Datetime, which could cause
|
|
8
|
+
* confusion.
|
|
9
|
+
*/
|
|
10
|
+
export const warnIfTimeZoneProvided = (el, formatOptions) => {
|
|
11
|
+
var _a, _b, _c, _d;
|
|
12
|
+
if (((_a = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) === null || _a === void 0 ? void 0 : _a.timeZone) ||
|
|
13
|
+
((_b = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) === null || _b === void 0 ? void 0 : _b.timeZoneName) ||
|
|
14
|
+
((_c = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) === null || _c === void 0 ? void 0 : _c.timeZone) ||
|
|
15
|
+
((_d = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) === null || _d === void 0 ? void 0 : _d.timeZoneName)) {
|
|
16
|
+
printIonWarning('Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".', el);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const checkForPresentationFormatMismatch = (el, presentation, formatOptions) => {
|
|
20
|
+
// formatOptions is not required
|
|
21
|
+
if (!formatOptions)
|
|
22
|
+
return;
|
|
23
|
+
// If formatOptions is provided, the date and/or time objects are required, depending on the presentation
|
|
24
|
+
switch (presentation) {
|
|
25
|
+
case 'date':
|
|
26
|
+
case 'month-year':
|
|
27
|
+
case 'month':
|
|
28
|
+
case 'year':
|
|
29
|
+
if (formatOptions.date === undefined) {
|
|
30
|
+
printIonWarning(`Datetime: The '${presentation}' presentation requires a date object in formatOptions.`, el);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
case 'time':
|
|
34
|
+
if (formatOptions.time === undefined) {
|
|
35
|
+
printIonWarning(`Datetime: The 'time' presentation requires a time object in formatOptions.`, el);
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case 'date-time':
|
|
39
|
+
case 'time-date':
|
|
40
|
+
if (formatOptions.date === undefined && formatOptions.time === undefined) {
|
|
41
|
+
printIonWarning(`Datetime: The '${presentation}' presentation requires either a date or time object (or both) in formatOptions.`, el);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -7,7 +7,7 @@ import { printIonError } from "../../utils/logging/index";
|
|
|
7
7
|
import { createColorClasses } from "../../utils/theme";
|
|
8
8
|
import { getIonMode } from "../../global/ionic-global";
|
|
9
9
|
import { getToday } from "../datetime/utils/data";
|
|
10
|
-
import {
|
|
10
|
+
import { getLocalizedDateTime, getLocalizedTime } from "../datetime/utils/format";
|
|
11
11
|
import { getHourCycle } from "../datetime/utils/helpers";
|
|
12
12
|
import { parseDate } from "../datetime/utils/parse";
|
|
13
13
|
/**
|
|
@@ -43,11 +43,12 @@ export class DatetimeButton {
|
|
|
43
43
|
* to the locale specified on ion-datetime.
|
|
44
44
|
*/
|
|
45
45
|
this.setDateTimeText = () => {
|
|
46
|
+
var _a, _b, _c, _d, _e;
|
|
46
47
|
const { datetimeEl, datetimePresentation } = this;
|
|
47
48
|
if (!datetimeEl) {
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
|
-
const { value, locale, hourCycle, preferWheel, multiple, titleSelectedDatesFormatter } = datetimeEl;
|
|
51
|
+
const { value, locale, formatOptions, hourCycle, preferWheel, multiple, titleSelectedDatesFormatter } = datetimeEl;
|
|
51
52
|
const parsedValues = this.getParsedDateValues(value);
|
|
52
53
|
/**
|
|
53
54
|
* Both ion-datetime and ion-datetime-button default
|
|
@@ -70,8 +71,8 @@ export class DatetimeButton {
|
|
|
70
71
|
switch (datetimePresentation) {
|
|
71
72
|
case 'date-time':
|
|
72
73
|
case 'time-date':
|
|
73
|
-
const dateText =
|
|
74
|
-
const timeText = getLocalizedTime(locale, firstParsedDatetime, computedHourCycle);
|
|
74
|
+
const dateText = getLocalizedDateTime(locale, firstParsedDatetime, (_a = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) !== null && _a !== void 0 ? _a : { month: 'short', day: 'numeric', year: 'numeric' });
|
|
75
|
+
const timeText = getLocalizedTime(locale, firstParsedDatetime, computedHourCycle, formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time);
|
|
75
76
|
if (preferWheel) {
|
|
76
77
|
this.dateText = `${dateText} ${timeText}`;
|
|
77
78
|
}
|
|
@@ -94,20 +95,20 @@ export class DatetimeButton {
|
|
|
94
95
|
this.dateText = headerText;
|
|
95
96
|
}
|
|
96
97
|
else {
|
|
97
|
-
this.dateText =
|
|
98
|
+
this.dateText = getLocalizedDateTime(locale, firstParsedDatetime, (_b = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) !== null && _b !== void 0 ? _b : { month: 'short', day: 'numeric', year: 'numeric' });
|
|
98
99
|
}
|
|
99
100
|
break;
|
|
100
101
|
case 'time':
|
|
101
|
-
this.timeText = getLocalizedTime(locale, firstParsedDatetime, computedHourCycle);
|
|
102
|
+
this.timeText = getLocalizedTime(locale, firstParsedDatetime, computedHourCycle, formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time);
|
|
102
103
|
break;
|
|
103
104
|
case 'month-year':
|
|
104
|
-
this.dateText =
|
|
105
|
+
this.dateText = getLocalizedDateTime(locale, firstParsedDatetime, (_c = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) !== null && _c !== void 0 ? _c : { month: 'long', year: 'numeric' });
|
|
105
106
|
break;
|
|
106
107
|
case 'month':
|
|
107
|
-
this.dateText = getLocalizedDateTime(locale, firstParsedDatetime, { month: 'long' });
|
|
108
|
+
this.dateText = getLocalizedDateTime(locale, firstParsedDatetime, (_d = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) !== null && _d !== void 0 ? _d : { month: 'long' });
|
|
108
109
|
break;
|
|
109
110
|
case 'year':
|
|
110
|
-
this.dateText = getLocalizedDateTime(locale, firstParsedDatetime, { year: 'numeric' });
|
|
111
|
+
this.dateText = getLocalizedDateTime(locale, firstParsedDatetime, (_e = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) !== null && _e !== void 0 ? _e : { year: 'numeric' });
|
|
111
112
|
break;
|
|
112
113
|
}
|
|
113
114
|
};
|
|
@@ -336,7 +337,7 @@ export class DatetimeButton {
|
|
|
336
337
|
render() {
|
|
337
338
|
const { color, dateText, timeText, selectedButton, datetimeActive, disabled } = this;
|
|
338
339
|
const mode = getIonMode(this);
|
|
339
|
-
return (h(Host, { key: '
|
|
340
|
+
return (h(Host, { key: '861a08f4a239de771875aee9b505e06ac7064b32', class: createColorClasses(color, {
|
|
340
341
|
[mode]: true,
|
|
341
342
|
[`${selectedButton}-active`]: datetimeActive,
|
|
342
343
|
['datetime-button-disabled']: disabled,
|
|
@@ -174,4 +174,69 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
|
|
174
174
|
await expect(page.locator('#time-button')).not.toBeVisible();
|
|
175
175
|
});
|
|
176
176
|
});
|
|
177
|
+
test.describe(title('datetime-button: formatOptions'), () => {
|
|
178
|
+
test('should include date and time for presentation date-time', async ({ page }) => {
|
|
179
|
+
await page.setContent(`
|
|
180
|
+
<ion-datetime-button datetime="datetime"></ion-datetime-button>
|
|
181
|
+
<ion-datetime id="datetime" presentation="date-time" value="2023-11-02T01:22:00" locale="en-US"></ion-datetime>
|
|
182
|
+
<script>
|
|
183
|
+
const datetime = document.querySelector('ion-datetime');
|
|
184
|
+
datetime.formatOptions = {
|
|
185
|
+
date: {
|
|
186
|
+
weekday: "short",
|
|
187
|
+
month: "long",
|
|
188
|
+
day: "2-digit"
|
|
189
|
+
},
|
|
190
|
+
time: {
|
|
191
|
+
hour: "2-digit",
|
|
192
|
+
minute: "2-digit"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
</script>
|
|
196
|
+
`, config);
|
|
197
|
+
await page.locator('.datetime-ready').waitFor();
|
|
198
|
+
await expect(page.locator('#date-button')).toContainText('Thu, November 02');
|
|
199
|
+
await expect(page.locator('#time-button')).toContainText('01:22 AM');
|
|
200
|
+
});
|
|
201
|
+
test('should include date for presentation date', async ({ page }) => {
|
|
202
|
+
await page.setContent(`
|
|
203
|
+
<ion-datetime-button datetime="datetime"></ion-datetime-button>
|
|
204
|
+
<ion-datetime id="datetime" presentation="date" value="2023-11-02" locale="en-US"></ion-datetime>
|
|
205
|
+
<script>
|
|
206
|
+
const datetime = document.querySelector('ion-datetime');
|
|
207
|
+
datetime.formatOptions = {
|
|
208
|
+
date: {
|
|
209
|
+
weekday: "short",
|
|
210
|
+
month: "long",
|
|
211
|
+
day: "2-digit"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
</script>
|
|
215
|
+
`, config);
|
|
216
|
+
await page.locator('.datetime-ready').waitFor();
|
|
217
|
+
await expect(page.locator('#date-button')).toContainText('Thu, November 02');
|
|
218
|
+
});
|
|
219
|
+
test('should include date and time in same button for preferWheel', async ({ page }) => {
|
|
220
|
+
await page.setContent(`
|
|
221
|
+
<ion-datetime-button datetime="datetime"></ion-datetime-button>
|
|
222
|
+
<ion-datetime id="datetime" presentation="date-time" value="2023-11-02T01:22:00" locale="en-US" prefer-wheel="true"></ion-datetime>
|
|
223
|
+
<script>
|
|
224
|
+
const datetime = document.querySelector('ion-datetime');
|
|
225
|
+
datetime.formatOptions = {
|
|
226
|
+
date: {
|
|
227
|
+
weekday: "short",
|
|
228
|
+
month: "long",
|
|
229
|
+
day: "2-digit"
|
|
230
|
+
},
|
|
231
|
+
time: {
|
|
232
|
+
hour: "2-digit",
|
|
233
|
+
minute: "2-digit"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
</script>
|
|
237
|
+
`, config);
|
|
238
|
+
await page.locator('.datetime-ready').waitFor();
|
|
239
|
+
await expect(page.locator('ion-datetime-button')).toContainText('Thu, November 02 01:22 AM');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
177
242
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { Host, forceUpdate, h } from "@stencil/core";
|
|
5
|
-
import { debounceEvent, raf, componentOnReady } from "../../utils/helpers";
|
|
5
|
+
import { debounceEvent, raf, componentOnReady, inheritAttributes } from "../../utils/helpers";
|
|
6
6
|
import { isRTL } from "../../utils/rtl/index";
|
|
7
7
|
import { createColorClasses } from "../../utils/theme";
|
|
8
8
|
import { arrowBackSharp, closeCircle, closeSharp, searchOutline, searchSharp } from "ionicons/icons";
|
|
@@ -16,6 +16,7 @@ export class Searchbar {
|
|
|
16
16
|
this.isCancelVisible = false;
|
|
17
17
|
this.shouldAlignLeft = true;
|
|
18
18
|
this.inputId = `ion-searchbar-${searchbarIds++}`;
|
|
19
|
+
this.inheritedAttributes = {};
|
|
19
20
|
/**
|
|
20
21
|
* Clears the input field and triggers the control change.
|
|
21
22
|
*/
|
|
@@ -116,6 +117,7 @@ export class Searchbar {
|
|
|
116
117
|
this.noAnimate = true;
|
|
117
118
|
this.color = undefined;
|
|
118
119
|
this.animated = false;
|
|
120
|
+
this.autocapitalize = undefined;
|
|
119
121
|
this.autocomplete = 'off';
|
|
120
122
|
this.autocorrect = 'off';
|
|
121
123
|
this.cancelButtonIcon = config.get('backButtonIcon', arrowBackSharp);
|
|
@@ -125,6 +127,8 @@ export class Searchbar {
|
|
|
125
127
|
this.disabled = false;
|
|
126
128
|
this.inputmode = undefined;
|
|
127
129
|
this.enterkeyhint = undefined;
|
|
130
|
+
this.maxlength = undefined;
|
|
131
|
+
this.minlength = undefined;
|
|
128
132
|
this.name = this.inputId;
|
|
129
133
|
this.placeholder = 'Search';
|
|
130
134
|
this.searchIcon = undefined;
|
|
@@ -134,6 +138,21 @@ export class Searchbar {
|
|
|
134
138
|
this.type = 'search';
|
|
135
139
|
this.value = '';
|
|
136
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* lang and dir are globally enumerated attributes.
|
|
143
|
+
* As a result, creating these as properties
|
|
144
|
+
* can have unintended side effects. Instead, we
|
|
145
|
+
* listen for attribute changes and inherit them
|
|
146
|
+
* to the inner `<input>` element.
|
|
147
|
+
*/
|
|
148
|
+
onLangChanged(newValue) {
|
|
149
|
+
this.inheritedAttributes = Object.assign(Object.assign({}, this.inheritedAttributes), { lang: newValue });
|
|
150
|
+
forceUpdate(this);
|
|
151
|
+
}
|
|
152
|
+
onDirChanged(newValue) {
|
|
153
|
+
this.inheritedAttributes = Object.assign(Object.assign({}, this.inheritedAttributes), { dir: newValue });
|
|
154
|
+
forceUpdate(this);
|
|
155
|
+
}
|
|
137
156
|
debounceChanged() {
|
|
138
157
|
const { ionInput, debounce, originalIonInput } = this;
|
|
139
158
|
/**
|
|
@@ -158,6 +177,9 @@ export class Searchbar {
|
|
|
158
177
|
connectedCallback() {
|
|
159
178
|
this.emitStyle();
|
|
160
179
|
}
|
|
180
|
+
componentWillLoad() {
|
|
181
|
+
this.inheritedAttributes = Object.assign({}, inheritAttributes(this.el, ['lang', 'dir']));
|
|
182
|
+
}
|
|
161
183
|
componentDidLoad() {
|
|
162
184
|
this.originalIonInput = this.ionInput;
|
|
163
185
|
this.positionElements();
|
|
@@ -356,8 +378,8 @@ export class Searchbar {
|
|
|
356
378
|
const clearIcon = this.clearIcon || (mode === 'ios' ? closeCircle : closeSharp);
|
|
357
379
|
const searchIcon = this.searchIcon || (mode === 'ios' ? searchOutline : searchSharp);
|
|
358
380
|
const shouldShowCancelButton = this.shouldShowCancelButton();
|
|
359
|
-
const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: '
|
|
360
|
-
return (h(Host, { key: '
|
|
381
|
+
const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: '1a68689b5c14cde6e071c4095457146b9ac2e7ab', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, h("div", { key: '25ae47914baa4b7e0281efb2b296d1799a56c764', "aria-hidden": "true" }, mode === 'md' ? (h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
|
|
382
|
+
return (h(Host, { key: '2cf545c40874bc4a19b6af022f5ec41b3a913495', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: createColorClasses(this.color, {
|
|
361
383
|
[mode]: true,
|
|
362
384
|
'searchbar-animated': animated,
|
|
363
385
|
'searchbar-disabled': this.disabled,
|
|
@@ -367,14 +389,14 @@ export class Searchbar {
|
|
|
367
389
|
'searchbar-has-focus': this.focused,
|
|
368
390
|
'searchbar-should-show-clear': this.shouldShowClearButton(),
|
|
369
391
|
'searchbar-should-show-cancel': this.shouldShowCancelButton(),
|
|
370
|
-
}) }, h("div", { key: '
|
|
392
|
+
}) }, h("div", { key: '9cf49297278839bbe6e2ef9936de337daa53c08d', class: "searchbar-input-container" }, h("input", Object.assign({ key: 'bae5db413a302e116012251746d599030f7158ce', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, h("ion-icon", { key: '67c593ca842551d9e99e8239115cc7b43261b542', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), h("button", { key: '6901c3f6c74cce2d3b7e46fbff85ee6e6399bdb1', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
|
|
371
393
|
/**
|
|
372
394
|
* This prevents mobile browsers from
|
|
373
395
|
* blurring the input when the clear
|
|
374
396
|
* button is activated.
|
|
375
397
|
*/
|
|
376
398
|
ev.preventDefault();
|
|
377
|
-
}, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: '
|
|
399
|
+
}, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: 'abd45fb79ee807cf8352aa7bf5b0342f2d686227', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
|
|
378
400
|
}
|
|
379
401
|
static get is() { return "ion-searchbar"; }
|
|
380
402
|
static get encapsulation() { return "scoped"; }
|
|
@@ -433,6 +455,23 @@ export class Searchbar {
|
|
|
433
455
|
"reflect": false,
|
|
434
456
|
"defaultValue": "false"
|
|
435
457
|
},
|
|
458
|
+
"autocapitalize": {
|
|
459
|
+
"type": "string",
|
|
460
|
+
"mutable": false,
|
|
461
|
+
"complexType": {
|
|
462
|
+
"original": "string",
|
|
463
|
+
"resolved": "string",
|
|
464
|
+
"references": {}
|
|
465
|
+
},
|
|
466
|
+
"required": true,
|
|
467
|
+
"optional": false,
|
|
468
|
+
"docs": {
|
|
469
|
+
"tags": [],
|
|
470
|
+
"text": "Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.\nAvailable options: `\"off\"`, `\"none\"`, `\"on\"`, `\"sentences\"`, `\"words\"`, `\"characters\"`."
|
|
471
|
+
},
|
|
472
|
+
"attribute": "autocapitalize",
|
|
473
|
+
"reflect": false
|
|
474
|
+
},
|
|
436
475
|
"autocomplete": {
|
|
437
476
|
"type": "string",
|
|
438
477
|
"mutable": false,
|
|
@@ -597,6 +636,40 @@ export class Searchbar {
|
|
|
597
636
|
"attribute": "enterkeyhint",
|
|
598
637
|
"reflect": false
|
|
599
638
|
},
|
|
639
|
+
"maxlength": {
|
|
640
|
+
"type": "number",
|
|
641
|
+
"mutable": false,
|
|
642
|
+
"complexType": {
|
|
643
|
+
"original": "number",
|
|
644
|
+
"resolved": "number | undefined",
|
|
645
|
+
"references": {}
|
|
646
|
+
},
|
|
647
|
+
"required": false,
|
|
648
|
+
"optional": true,
|
|
649
|
+
"docs": {
|
|
650
|
+
"tags": [],
|
|
651
|
+
"text": "This attribute specifies the maximum number of characters that the user can enter."
|
|
652
|
+
},
|
|
653
|
+
"attribute": "maxlength",
|
|
654
|
+
"reflect": false
|
|
655
|
+
},
|
|
656
|
+
"minlength": {
|
|
657
|
+
"type": "number",
|
|
658
|
+
"mutable": false,
|
|
659
|
+
"complexType": {
|
|
660
|
+
"original": "number",
|
|
661
|
+
"resolved": "number | undefined",
|
|
662
|
+
"references": {}
|
|
663
|
+
},
|
|
664
|
+
"required": false,
|
|
665
|
+
"optional": true,
|
|
666
|
+
"docs": {
|
|
667
|
+
"tags": [],
|
|
668
|
+
"text": "This attribute specifies the minimum number of characters that the user can enter."
|
|
669
|
+
},
|
|
670
|
+
"attribute": "minlength",
|
|
671
|
+
"reflect": false
|
|
672
|
+
},
|
|
600
673
|
"name": {
|
|
601
674
|
"type": "string",
|
|
602
675
|
"mutable": false,
|
|
@@ -922,6 +995,12 @@ export class Searchbar {
|
|
|
922
995
|
static get elementRef() { return "el"; }
|
|
923
996
|
static get watchers() {
|
|
924
997
|
return [{
|
|
998
|
+
"propName": "lang",
|
|
999
|
+
"methodName": "onLangChanged"
|
|
1000
|
+
}, {
|
|
1001
|
+
"propName": "dir",
|
|
1002
|
+
"methodName": "onDirChanged"
|
|
1003
|
+
}, {
|
|
925
1004
|
"propName": "debounce",
|
|
926
1005
|
"methodName": "debounceChanged"
|
|
927
1006
|
}, {
|