@ionic/core 7.7.5 → 7.8.1-nightly.20240314

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.
Files changed (71) hide show
  1. package/components/data.js +23 -38
  2. package/components/ion-datetime-button.js +11 -10
  3. package/components/ion-datetime.js +69 -7
  4. package/components/ion-searchbar.js +34 -5
  5. package/components/picker-column-internal.js +8 -4
  6. package/dist/cjs/{data-a5109f09.js → data-4487a815.js} +22 -39
  7. package/dist/cjs/ion-datetime-button.cjs.entry.js +11 -10
  8. package/dist/cjs/ion-datetime_3.cjs.entry.js +66 -7
  9. package/dist/cjs/ion-picker-column-internal.cjs.entry.js +8 -4
  10. package/dist/cjs/ion-searchbar.cjs.entry.js +28 -4
  11. package/dist/cjs/ionic.cjs.js +1 -1
  12. package/dist/cjs/loader.cjs.js +1 -1
  13. package/dist/collection/components/alert/test/a11y/alert.e2e.js +4 -0
  14. package/dist/collection/components/datetime/datetime.js +50 -7
  15. package/dist/collection/components/datetime/test/basic/datetime.e2e.js +77 -0
  16. package/dist/collection/components/datetime/test/format.spec.js +65 -15
  17. package/dist/collection/components/datetime/utils/format.js +24 -37
  18. package/dist/collection/components/datetime/utils/validate.js +45 -0
  19. package/dist/collection/components/datetime-button/datetime-button.js +11 -10
  20. package/dist/collection/components/datetime-button/test/basic/datetime-button.e2e.js +65 -0
  21. package/dist/collection/components/picker-column-internal/picker-column-internal.js +8 -4
  22. package/dist/collection/components/searchbar/searchbar.js +84 -5
  23. package/dist/collection/components/searchbar/test/searchbar.spec.js +20 -2
  24. package/dist/collection/components/toggle/toggle.js +1 -1
  25. package/dist/docs.json +114 -2
  26. package/dist/esm/{data-0f3ab200.js → data-bb424ba8.js} +23 -38
  27. package/dist/esm/ion-datetime-button.entry.js +11 -10
  28. package/dist/esm/ion-datetime_3.entry.js +66 -7
  29. package/dist/esm/ion-picker-column-internal.entry.js +8 -4
  30. package/dist/esm/ion-searchbar.entry.js +29 -5
  31. package/dist/esm/ionic.js +1 -1
  32. package/dist/esm/loader.js +1 -1
  33. package/dist/esm-es5/data-bb424ba8.js +4 -0
  34. package/dist/esm-es5/ion-datetime-button.entry.js +1 -1
  35. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  36. package/dist/esm-es5/ion-picker-column-internal.entry.js +1 -1
  37. package/dist/esm-es5/ion-searchbar.entry.js +1 -1
  38. package/dist/esm-es5/ionic.js +1 -1
  39. package/dist/esm-es5/loader.js +1 -1
  40. package/dist/html.html-data.json +13 -1
  41. package/dist/ionic/ionic.esm.js +1 -1
  42. package/dist/ionic/p-2b77099e.system.entry.js +4 -0
  43. package/dist/ionic/p-3789920e.js +4 -0
  44. package/dist/ionic/p-4abb3f9e.entry.js +4 -0
  45. package/dist/ionic/p-60c9fd9e.system.js +1 -1
  46. package/dist/ionic/p-8a1b0abb.system.js +4 -0
  47. package/dist/ionic/p-8a5c2121.entry.js +4 -0
  48. package/dist/ionic/p-a722d78f.system.entry.js +4 -0
  49. package/dist/ionic/p-bc56a728.entry.js +4 -0
  50. package/dist/ionic/{p-0e7c8334.system.entry.js → p-c2a9829c.system.entry.js} +1 -1
  51. package/dist/ionic/p-cfd31f45.system.entry.js +4 -0
  52. package/dist/ionic/p-e02681a6.entry.js +4 -0
  53. package/dist/types/components/datetime/datetime-interface.d.ts +10 -0
  54. package/dist/types/components/datetime/datetime.d.ts +9 -1
  55. package/dist/types/components/datetime/utils/format.d.ts +7 -13
  56. package/dist/types/components/datetime/utils/validate.d.ts +8 -0
  57. package/dist/types/components/searchbar/searchbar.d.ts +38 -0
  58. package/dist/types/components/toggle/toggle.d.ts +1 -1
  59. package/dist/types/components.d.ts +36 -4
  60. package/hydrate/index.js +135 -60
  61. package/package.json +1 -1
  62. package/dist/esm-es5/data-0f3ab200.js +0 -4
  63. package/dist/ionic/p-6f5b32c7.system.entry.js +0 -4
  64. package/dist/ionic/p-7330c044.system.js +0 -4
  65. package/dist/ionic/p-76afa95f.entry.js +0 -4
  66. package/dist/ionic/p-81f18ed1.system.entry.js +0 -4
  67. package/dist/ionic/p-c05202a7.entry.js +0 -4
  68. package/dist/ionic/p-c67c9644.entry.js +0 -4
  69. package/dist/ionic/p-e447e552.entry.js +0 -4
  70. package/dist/ionic/p-ef269a56.system.entry.js +0 -4
  71. package/dist/ionic/p-f7dce541.js +0 -4
@@ -404,3 +404,80 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
404
404
  });
405
405
  });
406
406
  });
407
+ /**
408
+ * This behavior does not differ across
409
+ * directions.
410
+ */
411
+ configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
412
+ test.describe(title('datetime: formatOptions'), () => {
413
+ test('should format header and time button', async ({ page }) => {
414
+ await page.setContent(`
415
+ <ion-datetime value="2022-02-01T16:30:00">
416
+ <span slot="title">Select Date</span>
417
+ </ion-datetime>
418
+ <script>
419
+ const datetime = document.querySelector('ion-datetime');
420
+ datetime.formatOptions = {
421
+ time: { hour: '2-digit', minute: '2-digit' },
422
+ date: { day: '2-digit', month: 'long', era: 'short' },
423
+ }
424
+ </script>
425
+ `, config);
426
+ await page.locator('.datetime-ready').waitFor();
427
+ const headerDate = page.locator('ion-datetime .datetime-selected-date');
428
+ await expect(headerDate).toHaveText('February 01 AD');
429
+ const timeBody = page.locator('ion-datetime .time-body');
430
+ await expect(timeBody).toHaveText('04:30 PM');
431
+ });
432
+ });
433
+ });
434
+ /**
435
+ * This behavior does not differ across
436
+ * modes/directions.
437
+ */
438
+ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
439
+ test.describe(title('datetime: formatOptions misconfiguration errors'), () => {
440
+ test('should log a warning if time zone is provided', async ({ page }) => {
441
+ const logs = [];
442
+ page.on('console', (msg) => {
443
+ if (msg.type() === 'warning') {
444
+ logs.push(msg.text());
445
+ }
446
+ });
447
+ await page.setContent(`
448
+ <ion-datetime value="2022-02-01T16:30:00" presentation="date">
449
+ <span slot="title">Select Date</span>
450
+ </ion-datetime>
451
+ <script>
452
+ const datetime = document.querySelector('ion-datetime');
453
+ datetime.formatOptions = {
454
+ date: { timeZone: 'UTC' },
455
+ }
456
+ </script>
457
+ `, config);
458
+ await page.locator('.datetime-ready').waitFor();
459
+ expect(logs.length).toBe(1);
460
+ expect(logs[0]).toContain('[Ionic Warning]: Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".');
461
+ });
462
+ test('should log a warning if the required formatOptions are not provided for a presentation', async ({ page }) => {
463
+ const logs = [];
464
+ page.on('console', (msg) => {
465
+ if (msg.type() === 'warning') {
466
+ logs.push(msg.text());
467
+ }
468
+ });
469
+ await page.setContent(`
470
+ <ion-datetime value="2022-02-01T16:30:00">
471
+ <span slot="title">Select Date</span>
472
+ </ion-datetime>
473
+ <script>
474
+ const datetime = document.querySelector('ion-datetime');
475
+ datetime.formatOptions = {}
476
+ </script>
477
+ `, config);
478
+ await page.locator('.datetime-ready').waitFor();
479
+ expect(logs.length).toBe(1);
480
+ expect(logs[0]).toContain("[Ionic Warning]: Datetime: The 'date-time' presentation requires either a date or time object (or both) in formatOptions.");
481
+ });
482
+ });
483
+ });
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { generateDayAriaLabel, getMonthAndDay, getFormattedHour, addTimePadding, getMonthAndYear, getLocalizedDayPeriod, getLocalizedTime, } from "../utils/format";
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
- export const getLocalizedTime = (locale, refParts, hourCycle) => {
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 { getMonthAndYear, getMonthDayAndYear, getLocalizedDateTime, getLocalizedTime } from "../datetime/utils/format";
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 = getMonthDayAndYear(locale, firstParsedDatetime);
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 = getMonthDayAndYear(locale, firstParsedDatetime);
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 = getMonthAndYear(locale, firstParsedDatetime);
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: '36fe34d4225940611d74bdef5b954d54749d2dca', class: createColorClasses(color, {
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
  });
@@ -244,7 +244,11 @@ export class PickerColumnInternal {
244
244
  */
245
245
  componentWillLoad() {
246
246
  const visibleCallback = (entries) => {
247
- const ev = entries[0];
247
+ /**
248
+ * Browsers will sometimes group multiple IO events into a single callback.
249
+ * As a result, we want to grab the last/most recent event in case there are multiple events.
250
+ */
251
+ const ev = entries[entries.length - 1];
248
252
  if (ev.isIntersecting) {
249
253
  const { activeItem, el } = this;
250
254
  this.isColumnVisible = true;
@@ -333,11 +337,11 @@ export class PickerColumnInternal {
333
337
  * the attribute can be moved to datetime.tsx and set on every
334
338
  * instance of ion-picker-column-internal there instead.
335
339
  */
336
- return (h(Host, { key: 'd0e3818aa4ab06aefd988d1b42ae754244727b8f', exportparts: `${PICKER_ITEM_PART}, ${PICKER_ITEM_ACTIVE_PART}`, disabled: pickerDisabled, tabindex: pickerDisabled ? null : 0, class: createColorClasses(color, {
340
+ return (h(Host, { key: '2cf182b9d88219e7ab1feb66c90992c4b9bc6b8e', exportparts: `${PICKER_ITEM_PART}, ${PICKER_ITEM_ACTIVE_PART}`, disabled: pickerDisabled, tabindex: pickerDisabled ? null : 0, class: createColorClasses(color, {
337
341
  [mode]: true,
338
342
  ['picker-column-active']: isActive,
339
343
  ['picker-column-numeric-input']: numericInput,
340
- }) }, h("div", { key: 'ec3b16415db5f64e3fab37fda023560ebcdc9423', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '40c78c9a1277807572d73e23048c2730cab88f5a', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: 'c51fa817f6999356cf3568a46098e954936b45d5', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), items.map((item, index) => {
344
+ }) }, h("div", { key: '86fa9a1464c03dd25fbe4880b91a597228e0b9c6', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '3bcc466879ea06355c4132d1b7dd469f5aeb193b', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '3aa4e3a12f2fa7d05692c1758648817b19fa6e12', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), items.map((item, index) => {
341
345
  const isItemDisabled = pickerDisabled || item.disabled || false;
342
346
  {
343
347
  /*
@@ -354,7 +358,7 @@ export class PickerColumnInternal {
354
358
  }, "data-value": item.value, "data-index": index, onClick: (ev) => {
355
359
  this.centerPickerItemInView(ev.target, true);
356
360
  }, disabled: isItemDisabled, part: PICKER_ITEM_PART }, item.text));
357
- }), h("div", { key: '80a7461dfe571f46466219b05007b021711aed32', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: 'fed82263295f58c51e2b753d0c06c7d71872b8a7', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: 'e805dff4122cc4e5fb4d7dcb96054fee8ad3bd07', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0")));
361
+ }), h("div", { key: '0400a6791070b947db3ca692cf780190f7798d88', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '48c4c5e7d2534fd394a3cf6f608eb9998e795bcc', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '2881a12b028923f6e6f140ed963de8646cadf0aa', class: "picker-item picker-item-empty", "aria-hidden": "true" }, "\u00A0")));
358
362
  }
359
363
  static get is() { return "ion-picker-column-internal"; }
360
364
  static get encapsulation() { return "shadow"; }