@ionic/core 7.3.2-dev.11693321763.15a54694 → 7.3.2-dev.11693507138.1f9ed625
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/checkbox.js +6 -3
- package/components/data.js +31 -7
- package/components/framework-delegate.js +2 -21
- package/components/ion-datetime-button.js +3 -0
- package/components/ion-datetime.js +106 -81
- package/components/ion-modal.js +1 -1
- package/components/ion-range.js +2 -2
- package/components/ion-toggle.js +6 -3
- package/components/popover.js +1 -1
- package/components/radio.js +6 -3
- package/dist/cjs/{data-967b9c15.js → data-db832785.js} +31 -7
- package/dist/cjs/{framework-delegate-c0873a6f.js → framework-delegate-e8174951.js} +2 -21
- package/dist/cjs/index.cjs.js +2 -2
- package/dist/cjs/ion-action-sheet.cjs.entry.js +2 -2
- package/dist/cjs/ion-alert.cjs.entry.js +2 -2
- package/dist/cjs/ion-app_8.cjs.entry.js +1 -1
- package/dist/cjs/ion-checkbox.cjs.entry.js +5 -3
- package/dist/cjs/ion-datetime-button.cjs.entry.js +4 -1
- package/dist/cjs/ion-datetime_3.cjs.entry.js +108 -84
- package/dist/cjs/ion-loading.cjs.entry.js +2 -2
- package/dist/cjs/ion-menu_3.cjs.entry.js +2 -2
- package/dist/cjs/ion-modal.cjs.entry.js +3 -3
- package/dist/cjs/ion-nav_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-popover.cjs.entry.js +3 -3
- package/dist/cjs/ion-radio_2.cjs.entry.js +5 -3
- package/dist/cjs/ion-range.cjs.entry.js +2 -2
- package/dist/cjs/ion-select_3.cjs.entry.js +2 -2
- package/dist/cjs/ion-tab_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-toast.cjs.entry.js +2 -2
- package/dist/cjs/ion-toggle.cjs.entry.js +5 -3
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{overlays-5c1706ed.js → overlays-0ea06fcb.js} +1 -1
- package/dist/collection/components/checkbox/checkbox.ios.css +35 -0
- package/dist/collection/components/checkbox/checkbox.js +24 -4
- package/dist/collection/components/checkbox/checkbox.md.css +35 -0
- package/dist/collection/components/checkbox/test/item/checkbox.e2e.js +13 -0
- package/dist/collection/components/checkbox/test/label/checkbox.e2e.js +16 -0
- package/dist/collection/components/datetime/datetime.js +107 -84
- package/dist/collection/components/datetime/test/prefer-wheel/datetime.e2e.js +15 -0
- package/dist/collection/components/datetime/test/set-value/datetime.e2e.js +9 -12
- package/dist/collection/components/datetime/utils/data.js +14 -6
- package/dist/collection/components/datetime/utils/parse.js +18 -1
- package/dist/collection/components/datetime-button/datetime-button.js +3 -0
- package/dist/collection/components/modal/modal.js +1 -1
- package/dist/collection/components/popover/popover.js +1 -1
- package/dist/collection/components/radio/radio.ios.css +35 -0
- package/dist/collection/components/radio/radio.js +24 -4
- package/dist/collection/components/radio/radio.md.css +35 -0
- package/dist/collection/components/radio/test/item/radio.e2e.js +15 -0
- package/dist/collection/components/radio/test/label-placement/radio.e2e.js +20 -0
- package/dist/collection/components/range/range.ios.css +41 -1
- package/dist/collection/components/range/range.js +3 -3
- package/dist/collection/components/range/range.md.css +41 -1
- package/dist/collection/components/range/test/label/range.e2e.js +48 -0
- package/dist/collection/components/toggle/test/item/toggle.e2e.js +15 -0
- package/dist/collection/components/toggle/test/label/toggle.e2e.js +16 -0
- package/dist/collection/components/toggle/toggle.ios.css +35 -1
- package/dist/collection/components/toggle/toggle.js +24 -4
- package/dist/collection/components/toggle/toggle.md.css +35 -1
- package/dist/collection/utils/framework-delegate.js +2 -21
- package/dist/docs.json +115 -18
- package/dist/esm/{data-4b448e3a.js → data-009dbf15.js} +31 -7
- package/dist/esm/{framework-delegate-aa433dea.js → framework-delegate-2b76c681.js} +2 -21
- package/dist/esm/index.js +2 -2
- package/dist/esm/ion-action-sheet.entry.js +2 -2
- package/dist/esm/ion-alert.entry.js +2 -2
- package/dist/esm/ion-app_8.entry.js +1 -1
- package/dist/esm/ion-checkbox.entry.js +5 -3
- package/dist/esm/ion-datetime-button.entry.js +4 -1
- package/dist/esm/ion-datetime_3.entry.js +108 -84
- package/dist/esm/ion-loading.entry.js +2 -2
- package/dist/esm/ion-menu_3.entry.js +2 -2
- package/dist/esm/ion-modal.entry.js +3 -3
- package/dist/esm/ion-nav_2.entry.js +1 -1
- package/dist/esm/ion-popover.entry.js +3 -3
- package/dist/esm/ion-radio_2.entry.js +5 -3
- package/dist/esm/ion-range.entry.js +2 -2
- package/dist/esm/ion-select_3.entry.js +2 -2
- package/dist/esm/ion-tab_2.entry.js +1 -1
- package/dist/esm/ion-toast.entry.js +2 -2
- package/dist/esm/ion-toggle.entry.js +5 -3
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{overlays-e1dadfe6.js → overlays-c3387ec0.js} +1 -1
- package/dist/esm-es5/data-009dbf15.js +4 -0
- package/dist/esm-es5/framework-delegate-2b76c681.js +4 -0
- package/dist/esm-es5/index.js +1 -1
- package/dist/esm-es5/ion-action-sheet.entry.js +1 -1
- package/dist/esm-es5/ion-alert.entry.js +1 -1
- package/dist/esm-es5/ion-app_8.entry.js +1 -1
- package/dist/esm-es5/ion-checkbox.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-loading.entry.js +1 -1
- package/dist/esm-es5/ion-menu_3.entry.js +1 -1
- package/dist/esm-es5/ion-modal.entry.js +1 -1
- package/dist/esm-es5/ion-nav_2.entry.js +1 -1
- package/dist/esm-es5/ion-popover.entry.js +1 -1
- package/dist/esm-es5/ion-radio_2.entry.js +1 -1
- package/dist/esm-es5/ion-range.entry.js +1 -1
- package/dist/esm-es5/ion-select_3.entry.js +1 -1
- package/dist/esm-es5/ion-tab_2.entry.js +1 -1
- package/dist/esm-es5/ion-toast.entry.js +1 -1
- package/dist/esm-es5/ion-toggle.entry.js +1 -1
- package/dist/esm-es5/ionic.js +1 -1
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/{overlays-e1dadfe6.js → overlays-c3387ec0.js} +1 -1
- package/dist/html.html-data.json +52 -4
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/{p-84a95033.system.entry.js → p-06f1f560.system.entry.js} +1 -1
- package/dist/ionic/p-1f4adf06.system.entry.js +4 -0
- package/dist/ionic/{p-c759a704.entry.js → p-20087c90.entry.js} +1 -1
- package/dist/ionic/{p-98ebc376.entry.js → p-229df0d8.entry.js} +1 -1
- package/dist/ionic/{p-51669f1e.system.entry.js → p-2936ec7f.system.entry.js} +1 -1
- package/dist/ionic/{p-e44233f5.entry.js → p-29b3e70a.entry.js} +1 -1
- package/dist/ionic/{p-2bff71d5.system.entry.js → p-2bc49e5d.system.entry.js} +1 -1
- package/dist/ionic/{p-f2c667c9.system.entry.js → p-2da3e015.system.entry.js} +1 -1
- package/dist/ionic/p-2f8bea16.entry.js +4 -0
- package/dist/ionic/p-40138199.entry.js +4 -0
- package/dist/ionic/{p-7c9dd24d.entry.js → p-423d5a4d.entry.js} +1 -1
- package/dist/ionic/p-44ef7224.system.js +4 -0
- package/dist/ionic/p-4ca1ce6b.system.entry.js +4 -0
- package/dist/ionic/{p-f7faa32d.entry.js → p-5c96dc4e.entry.js} +1 -1
- package/dist/ionic/{p-582dc7c1.js → p-5ef91692.js} +1 -1
- package/dist/ionic/{p-f9708a54.entry.js → p-61769039.entry.js} +1 -1
- package/dist/ionic/{p-d3b8aebf.entry.js → p-67b891af.entry.js} +1 -1
- package/dist/ionic/{p-d1d3b740.system.entry.js → p-6b9bf14c.system.entry.js} +1 -1
- package/dist/ionic/p-6da27ab8.system.entry.js +4 -0
- package/dist/ionic/p-721933ff.system.js +1 -1
- package/dist/ionic/{p-f789b7fa.entry.js → p-74723f41.entry.js} +1 -1
- package/dist/ionic/{p-28f5715a.system.entry.js → p-74bd26b8.system.entry.js} +1 -1
- package/dist/ionic/p-7cfd253c.js +4 -0
- package/dist/ionic/{p-45ee211b.system.entry.js → p-8074bdd7.system.entry.js} +1 -1
- package/dist/ionic/{p-b32742f3.system.js → p-81b46714.system.js} +1 -1
- package/dist/ionic/p-86c5c320.entry.js +4 -0
- package/dist/ionic/{p-613d4042.system.js → p-8d9475b5.system.js} +1 -1
- package/dist/ionic/{p-d693e765.system.entry.js → p-8fa43732.system.entry.js} +1 -1
- package/dist/ionic/p-90ec5785.system.entry.js +4 -0
- package/dist/ionic/{p-091cd81e.entry.js → p-958bde76.entry.js} +1 -1
- package/dist/ionic/{p-b3a58769.entry.js → p-9b3c3564.entry.js} +1 -1
- package/dist/ionic/p-af986299.system.entry.js +4 -0
- package/dist/ionic/p-b321c999.entry.js +4 -0
- package/dist/ionic/p-b34c1ae1.js +4 -0
- package/dist/ionic/{p-5c17f888.entry.js → p-c0d1e5a1.entry.js} +1 -1
- package/dist/ionic/{p-dab7745f.system.entry.js → p-ccea9adc.system.entry.js} +1 -1
- package/dist/ionic/{p-0b68c41d.system.entry.js → p-d1356034.system.entry.js} +1 -1
- package/dist/ionic/{p-d9eb4632.system.entry.js → p-d5db0834.system.entry.js} +1 -1
- package/dist/ionic/{p-79e3f0b6.entry.js → p-d8538837.entry.js} +1 -1
- package/dist/ionic/p-d8a0267b.system.entry.js +4 -0
- package/dist/ionic/{p-8843b046.system.js → p-db46fc66.system.js} +1 -1
- package/dist/ionic/p-f5c19164.entry.js +4 -0
- package/dist/types/components/checkbox/checkbox.d.ts +8 -1
- package/dist/types/components/datetime/datetime.d.ts +17 -10
- package/dist/types/components/datetime/utils/data.d.ts +1 -1
- package/dist/types/components/datetime/utils/parse.d.ts +3 -3
- package/dist/types/components/radio/radio.d.ts +8 -1
- package/dist/types/components/range/range.d.ts +2 -1
- package/dist/types/components/toast/toast-interface.d.ts +3 -0
- package/dist/types/components/toggle/toggle.d.ts +8 -1
- package/dist/types/components.d.ts +40 -16
- package/hydrate/index.js +164 -122
- package/package.json +1 -1
- package/dist/collection/components/popover/test/async/popover.e2e.js +0 -20
- package/dist/esm-es5/data-4b448e3a.js +0 -4
- package/dist/esm-es5/framework-delegate-aa433dea.js +0 -4
- package/dist/ionic/p-0053fe81.system.entry.js +0 -4
- package/dist/ionic/p-2cdcd6f2.entry.js +0 -4
- package/dist/ionic/p-3565c8a0.entry.js +0 -4
- package/dist/ionic/p-5b88f780.entry.js +0 -4
- package/dist/ionic/p-601fd53c.js +0 -4
- package/dist/ionic/p-98b618ce.system.entry.js +0 -4
- package/dist/ionic/p-b1e8f045.entry.js +0 -4
- package/dist/ionic/p-b287ab05.js +0 -4
- package/dist/ionic/p-b3cfc6e1.system.entry.js +0 -4
- package/dist/ionic/p-b54a1fcb.system.entry.js +0 -4
- package/dist/ionic/p-cc8910e7.system.js +0 -4
- package/dist/ionic/p-e53b8af4.system.entry.js +0 -4
- package/dist/ionic/p-f78e60a5.entry.js +0 -4
- package/dist/ionic/p-fd42994b.system.entry.js +0 -4
|
@@ -55,6 +55,7 @@ export class Checkbox {
|
|
|
55
55
|
this.value = 'on';
|
|
56
56
|
this.labelPlacement = 'start';
|
|
57
57
|
this.justify = 'space-between';
|
|
58
|
+
this.alignment = 'center';
|
|
58
59
|
this.legacy = undefined;
|
|
59
60
|
}
|
|
60
61
|
// TODO(FW-3100): remove this
|
|
@@ -92,7 +93,7 @@ export class Checkbox {
|
|
|
92
93
|
return legacyFormController.hasLegacyControl() ? this.renderLegacyCheckbox() : this.renderCheckbox();
|
|
93
94
|
}
|
|
94
95
|
renderCheckbox() {
|
|
95
|
-
const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, } = this;
|
|
96
|
+
const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, } = this;
|
|
96
97
|
const mode = getIonMode(this);
|
|
97
98
|
const path = getSVGPath(mode, indeterminate);
|
|
98
99
|
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
|
@@ -104,6 +105,7 @@ export class Checkbox {
|
|
|
104
105
|
'checkbox-indeterminate': indeterminate,
|
|
105
106
|
interactive: true,
|
|
106
107
|
[`checkbox-justify-${justify}`]: true,
|
|
108
|
+
[`checkbox-alignment-${alignment}`]: true,
|
|
107
109
|
[`checkbox-label-placement-${labelPlacement}`]: true,
|
|
108
110
|
}) }, h("label", { class: "checkbox-wrapper" }, h("input", Object.assign({ type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, onFocus: () => this.onFocus(), onBlur: () => this.onBlur(), ref: (focusEl) => (this.focusEl = focusEl) }, inheritedAttributes)), h("div", { class: {
|
|
109
111
|
'label-text-wrapper': true,
|
|
@@ -280,15 +282,15 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
|
|
|
280
282
|
"type": "string",
|
|
281
283
|
"mutable": false,
|
|
282
284
|
"complexType": {
|
|
283
|
-
"original": "'start' | 'end' | 'fixed'",
|
|
284
|
-
"resolved": "\"end\" | \"fixed\" | \"start\"",
|
|
285
|
+
"original": "'start' | 'end' | 'fixed' | 'stacked'",
|
|
286
|
+
"resolved": "\"end\" | \"fixed\" | \"stacked\" | \"start\"",
|
|
285
287
|
"references": {}
|
|
286
288
|
},
|
|
287
289
|
"required": false,
|
|
288
290
|
"optional": false,
|
|
289
291
|
"docs": {
|
|
290
292
|
"tags": [],
|
|
291
|
-
"text": "Where to place the label relative to the checkbox.\n`\"start\"`: The label will appear to the left of the checkbox in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the checkbox in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\")."
|
|
293
|
+
"text": "Where to place the label relative to the checkbox.\n`\"start\"`: The label will appear to the left of the checkbox in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the checkbox in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\").\n`\"stacked\"`: The label will appear above the checkbox regardless of the direction. The alignment of the label can be controlled with the `alignment` property."
|
|
292
294
|
},
|
|
293
295
|
"attribute": "label-placement",
|
|
294
296
|
"reflect": false,
|
|
@@ -312,6 +314,24 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
|
|
|
312
314
|
"reflect": false,
|
|
313
315
|
"defaultValue": "'space-between'"
|
|
314
316
|
},
|
|
317
|
+
"alignment": {
|
|
318
|
+
"type": "string",
|
|
319
|
+
"mutable": false,
|
|
320
|
+
"complexType": {
|
|
321
|
+
"original": "'start' | 'center'",
|
|
322
|
+
"resolved": "\"center\" | \"start\"",
|
|
323
|
+
"references": {}
|
|
324
|
+
},
|
|
325
|
+
"required": false,
|
|
326
|
+
"optional": false,
|
|
327
|
+
"docs": {
|
|
328
|
+
"tags": [],
|
|
329
|
+
"text": "How to control the alignment of the checkbox and label on the cross axis.\n`\"start\"`: The label and control will appear on the left of the cross axis in LTR, and on the right side in RTL.\n`\"center\"`: The label and control will appear at the center of the cross axis in both LTR and RTL."
|
|
330
|
+
},
|
|
331
|
+
"attribute": "alignment",
|
|
332
|
+
"reflect": false,
|
|
333
|
+
"defaultValue": "'center'"
|
|
334
|
+
},
|
|
315
335
|
"legacy": {
|
|
316
336
|
"type": "boolean",
|
|
317
337
|
"mutable": false,
|
|
@@ -124,6 +124,15 @@
|
|
|
124
124
|
margin-bottom: 10px;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
:host(.in-item.checkbox-label-placement-stacked) .label-text-wrapper {
|
|
128
|
+
margin-top: 10px;
|
|
129
|
+
margin-bottom: 16px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
:host(.in-item.checkbox-label-placement-stacked) .native-wrapper {
|
|
133
|
+
margin-bottom: 10px;
|
|
134
|
+
}
|
|
135
|
+
|
|
127
136
|
/**
|
|
128
137
|
* If no label text is placed into the slot
|
|
129
138
|
* then the element should be hidden otherwise
|
|
@@ -198,6 +207,14 @@ input {
|
|
|
198
207
|
justify-content: end;
|
|
199
208
|
}
|
|
200
209
|
|
|
210
|
+
:host(.checkbox-alignment-start) .checkbox-wrapper {
|
|
211
|
+
align-items: start;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
:host(.checkbox-alignment-center) .checkbox-wrapper {
|
|
215
|
+
align-items: center;
|
|
216
|
+
}
|
|
217
|
+
|
|
201
218
|
/**
|
|
202
219
|
* Label is on the left of the checkbox in LTR and
|
|
203
220
|
* on the right in RTL.
|
|
@@ -261,6 +278,24 @@ input {
|
|
|
261
278
|
max-width: 200px;
|
|
262
279
|
}
|
|
263
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Label is on top of the checkbox.
|
|
283
|
+
*/
|
|
284
|
+
:host(.checkbox-label-placement-stacked) .checkbox-wrapper {
|
|
285
|
+
flex-direction: column;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
:host(.checkbox-label-placement-stacked) .label-text-wrapper {
|
|
289
|
+
/**
|
|
290
|
+
* The margin between the label and
|
|
291
|
+
* the checkbox should be on the bottom
|
|
292
|
+
* when the label sits at the top.
|
|
293
|
+
*/
|
|
294
|
+
margin-left: 0;
|
|
295
|
+
margin-right: 0;
|
|
296
|
+
margin-bottom: 16px;
|
|
297
|
+
}
|
|
298
|
+
|
|
264
299
|
:host(.checkbox-checked) .checkbox-icon,
|
|
265
300
|
:host(.checkbox-indeterminate) .checkbox-icon {
|
|
266
301
|
border-color: var(--border-color-checked);
|
|
@@ -58,4 +58,17 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
|
|
|
58
58
|
expect(await list.screenshot()).toMatchSnapshot(screenshot(`checkbox-long-label-in-item`));
|
|
59
59
|
});
|
|
60
60
|
});
|
|
61
|
+
test.describe(title('checkbox: stacked label in item'), () => {
|
|
62
|
+
test('should render margins correctly when using stacked label in item', async ({ page }) => {
|
|
63
|
+
await page.setContent(`
|
|
64
|
+
<ion-list>
|
|
65
|
+
<ion-item>
|
|
66
|
+
<ion-checkbox label-placement="stacked">Enable Notifications</ion-checkbox>
|
|
67
|
+
</ion-item>
|
|
68
|
+
</ion-list>
|
|
69
|
+
`, config);
|
|
70
|
+
const list = page.locator('ion-list');
|
|
71
|
+
await expect(list).toHaveScreenshot(screenshot(`checkbox-stacked-label-in-item`));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
61
74
|
});
|
|
@@ -91,5 +91,21 @@ configs().forEach(({ title, screenshot, config }) => {
|
|
|
91
91
|
expect(await checkbox.screenshot()).toMatchSnapshot(screenshot(`checkbox-label-fixed-justify-space-between`));
|
|
92
92
|
});
|
|
93
93
|
});
|
|
94
|
+
test.describe('checkbox: stacked placement', () => {
|
|
95
|
+
test('should align the label to the start of the container in the stacked position', async ({ page }) => {
|
|
96
|
+
await page.setContent(`
|
|
97
|
+
<ion-checkbox label-placement="stacked" alignment="start" style="width: 200px">This is a long label</ion-checkbox>
|
|
98
|
+
`, config);
|
|
99
|
+
const checkbox = page.locator('ion-checkbox');
|
|
100
|
+
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-label-stacked-align-start`));
|
|
101
|
+
});
|
|
102
|
+
test('should align the label to the center of the container in the stacked position', async ({ page }) => {
|
|
103
|
+
await page.setContent(`
|
|
104
|
+
<ion-checkbox label-placement="stacked" alignment="center" style="width: 200px">This is a long label</ion-checkbox>
|
|
105
|
+
`, config);
|
|
106
|
+
const checkbox = page.locator('ion-checkbox');
|
|
107
|
+
await expect(checkbox).toHaveScreenshot(screenshot(`checkbox-label-stacked-align-center`));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
94
110
|
});
|
|
95
111
|
});
|
|
@@ -38,11 +38,6 @@ export class Datetime {
|
|
|
38
38
|
constructor() {
|
|
39
39
|
this.inputId = `ion-dt-${datetimeIds++}`;
|
|
40
40
|
this.prevPresentation = null;
|
|
41
|
-
/**
|
|
42
|
-
* Duplicate reference to `activeParts` that does not trigger a re-render of the component.
|
|
43
|
-
* Allows caching an instance of the `activeParts` in between render cycles.
|
|
44
|
-
*/
|
|
45
|
-
this.activePartsClone = [];
|
|
46
41
|
this.warnIfIncorrectValueUsage = () => {
|
|
47
42
|
const { multiple, value } = this;
|
|
48
43
|
if (!multiple && Array.isArray(value)) {
|
|
@@ -70,9 +65,9 @@ export class Datetime {
|
|
|
70
65
|
* data. This should be used when rendering an
|
|
71
66
|
* interface in an environment where the `value`
|
|
72
67
|
* may not be set. This function works
|
|
73
|
-
* by returning the first selected date
|
|
74
|
-
*
|
|
75
|
-
*
|
|
68
|
+
* by returning the first selected date and then
|
|
69
|
+
* falling back to defaultParts if no active date
|
|
70
|
+
* is selected.
|
|
76
71
|
*/
|
|
77
72
|
this.getActivePartsWithFallback = () => {
|
|
78
73
|
var _a;
|
|
@@ -80,8 +75,8 @@ export class Datetime {
|
|
|
80
75
|
return (_a = this.getActivePart()) !== null && _a !== void 0 ? _a : defaultParts;
|
|
81
76
|
};
|
|
82
77
|
this.getActivePart = () => {
|
|
83
|
-
const {
|
|
84
|
-
return Array.isArray(
|
|
78
|
+
const { activeParts } = this;
|
|
79
|
+
return Array.isArray(activeParts) ? activeParts[0] : activeParts;
|
|
85
80
|
};
|
|
86
81
|
this.closeParentOverlay = () => {
|
|
87
82
|
const popoverOrModal = this.el.closest('ion-modal, ion-popover');
|
|
@@ -93,7 +88,7 @@ export class Datetime {
|
|
|
93
88
|
this.workingParts = Object.assign({}, parts);
|
|
94
89
|
};
|
|
95
90
|
this.setActiveParts = (parts, removeDate = false) => {
|
|
96
|
-
const { multiple, minParts, maxParts,
|
|
91
|
+
const { multiple, minParts, maxParts, activeParts } = this;
|
|
97
92
|
/**
|
|
98
93
|
* When setting the active parts, it is possible
|
|
99
94
|
* to set invalid data. For example,
|
|
@@ -107,16 +102,7 @@ export class Datetime {
|
|
|
107
102
|
const validatedParts = validateParts(parts, minParts, maxParts);
|
|
108
103
|
this.setWorkingParts(validatedParts);
|
|
109
104
|
if (multiple) {
|
|
110
|
-
|
|
111
|
-
* We read from activePartsClone here because valueChanged() only updates that,
|
|
112
|
-
* so it's the more reliable source of truth. If we read from activeParts, then
|
|
113
|
-
* if you click July 1, manually set the value to July 2, and then click July 3,
|
|
114
|
-
* the new value would be [July 1, July 3], ignoring the value set.
|
|
115
|
-
*
|
|
116
|
-
* We can then pass the new value to activeParts (rather than activePartsClone)
|
|
117
|
-
* since the clone will be updated automatically by activePartsChanged().
|
|
118
|
-
*/
|
|
119
|
-
const activePartsArray = Array.isArray(activePartsClone) ? activePartsClone : [activePartsClone];
|
|
105
|
+
const activePartsArray = Array.isArray(activeParts) ? activeParts : [activeParts];
|
|
120
106
|
if (removeDate) {
|
|
121
107
|
this.activeParts = activePartsArray.filter((p) => !isSameDay(p, validatedParts));
|
|
122
108
|
}
|
|
@@ -333,6 +319,19 @@ export class Datetime {
|
|
|
333
319
|
const monthBox = month.getBoundingClientRect();
|
|
334
320
|
if (Math.abs(monthBox.x - box.x) > 2)
|
|
335
321
|
return;
|
|
322
|
+
/**
|
|
323
|
+
* If we're force-rendering a month, assume we've
|
|
324
|
+
* scrolled to that and return it.
|
|
325
|
+
*
|
|
326
|
+
* If forceRenderDate is ever used in a context where the
|
|
327
|
+
* forced month is not immediately auto-scrolled to, this
|
|
328
|
+
* should be updated to also check whether `month` has the
|
|
329
|
+
* same month and year as the forced date.
|
|
330
|
+
*/
|
|
331
|
+
const { forceRenderDate } = this;
|
|
332
|
+
if (forceRenderDate !== undefined) {
|
|
333
|
+
return { month: forceRenderDate.month, year: forceRenderDate.year, day: forceRenderDate.day };
|
|
334
|
+
}
|
|
336
335
|
/**
|
|
337
336
|
* From here, we can determine if the start
|
|
338
337
|
* month or the end month was scrolled into view.
|
|
@@ -387,6 +386,9 @@ export class Datetime {
|
|
|
387
386
|
this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), { month, day: day, year }));
|
|
388
387
|
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
|
|
389
388
|
calendarBodyRef.style.removeProperty('overflow');
|
|
389
|
+
if (this.resolveForceDateScrolling) {
|
|
390
|
+
this.resolveForceDateScrolling();
|
|
391
|
+
}
|
|
390
392
|
});
|
|
391
393
|
};
|
|
392
394
|
/**
|
|
@@ -441,10 +443,17 @@ export class Datetime {
|
|
|
441
443
|
}
|
|
442
444
|
};
|
|
443
445
|
this.processValue = (value) => {
|
|
444
|
-
const hasValue = value !== null && value !== undefined;
|
|
446
|
+
const hasValue = value !== null && value !== undefined && (!Array.isArray(value) || value.length > 0);
|
|
445
447
|
const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
|
|
446
|
-
const { minParts, maxParts } = this;
|
|
448
|
+
const { minParts, maxParts, workingParts, el } = this;
|
|
447
449
|
this.warnIfIncorrectValueUsage();
|
|
450
|
+
/**
|
|
451
|
+
* Return early if the value wasn't parsed correctly, such as
|
|
452
|
+
* if an improperly formatted date string was provided.
|
|
453
|
+
*/
|
|
454
|
+
if (!valueToProcess) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
448
457
|
/**
|
|
449
458
|
* Datetime should only warn of out of bounds values
|
|
450
459
|
* if set by the user. If the `value` is undefined,
|
|
@@ -462,16 +471,9 @@ export class Datetime {
|
|
|
462
471
|
* that the values don't necessarily have to be in order.
|
|
463
472
|
*/
|
|
464
473
|
const singleValue = Array.isArray(valueToProcess) ? valueToProcess[0] : valueToProcess;
|
|
465
|
-
const
|
|
474
|
+
const targetValue = clampDate(singleValue, minParts, maxParts);
|
|
475
|
+
const { month, day, year, hour, minute } = targetValue;
|
|
466
476
|
const ampm = parseAmPm(hour);
|
|
467
|
-
this.setWorkingParts({
|
|
468
|
-
month,
|
|
469
|
-
day,
|
|
470
|
-
year,
|
|
471
|
-
hour,
|
|
472
|
-
minute,
|
|
473
|
-
ampm,
|
|
474
|
-
});
|
|
475
477
|
/**
|
|
476
478
|
* Since `activeParts` indicates a value that
|
|
477
479
|
* been explicitly selected either by the
|
|
@@ -501,6 +503,62 @@ export class Datetime {
|
|
|
501
503
|
*/
|
|
502
504
|
this.activeParts = [];
|
|
503
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Only animate if:
|
|
508
|
+
* 1. We're using grid style (wheel style pickers should just jump to new value)
|
|
509
|
+
* 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
|
|
510
|
+
* 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
|
|
511
|
+
* 4. The month/year picker is not open (since you wouldn't see the animation anyway)
|
|
512
|
+
*/
|
|
513
|
+
const didChangeMonth = (month !== undefined && month !== workingParts.month) || (year !== undefined && year !== workingParts.year);
|
|
514
|
+
const bodyIsVisible = el.classList.contains('datetime-ready');
|
|
515
|
+
const { isGridStyle, showMonthAndYear } = this;
|
|
516
|
+
if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
|
|
517
|
+
this.animateToDate(targetValue);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
/**
|
|
521
|
+
* We only need to do this if we didn't just animate to a new month,
|
|
522
|
+
* since that calls prevMonth/nextMonth which calls setWorkingParts for us.
|
|
523
|
+
*/
|
|
524
|
+
this.setWorkingParts({
|
|
525
|
+
month,
|
|
526
|
+
day,
|
|
527
|
+
year,
|
|
528
|
+
hour,
|
|
529
|
+
minute,
|
|
530
|
+
ampm,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
this.animateToDate = async (targetValue) => {
|
|
535
|
+
const { workingParts } = this;
|
|
536
|
+
/**
|
|
537
|
+
* Tell other render functions that we need to force the
|
|
538
|
+
* target month to appear in place of the actual next/prev month.
|
|
539
|
+
* Because this is a State variable, a rerender will be triggered
|
|
540
|
+
* automatically, updating the rendered months.
|
|
541
|
+
*/
|
|
542
|
+
this.forceRenderDate = targetValue;
|
|
543
|
+
/**
|
|
544
|
+
* Flag that we've started scrolling to the forced date.
|
|
545
|
+
* The resolve function will be called by the datetime's
|
|
546
|
+
* scroll listener when it's done updating everything.
|
|
547
|
+
* This is a replacement for making prev/nextMonth async,
|
|
548
|
+
* since the logic we're waiting on is in a listener.
|
|
549
|
+
*/
|
|
550
|
+
const forceDateScrollingPromise = new Promise((resolve) => {
|
|
551
|
+
this.resolveForceDateScrolling = resolve;
|
|
552
|
+
});
|
|
553
|
+
/**
|
|
554
|
+
* Animate smoothly to the forced month. This will also update
|
|
555
|
+
* workingParts and correct the surrounding months for us.
|
|
556
|
+
*/
|
|
557
|
+
const targetMonthIsBefore = isBefore(targetValue, workingParts);
|
|
558
|
+
targetMonthIsBefore ? this.prevMonth() : this.nextMonth();
|
|
559
|
+
await forceDateScrollingPromise;
|
|
560
|
+
this.resolveForceDateScrolling = undefined;
|
|
561
|
+
this.forceRenderDate = undefined;
|
|
504
562
|
};
|
|
505
563
|
this.onFocus = () => {
|
|
506
564
|
this.ionFocus.emit();
|
|
@@ -556,6 +614,7 @@ export class Datetime {
|
|
|
556
614
|
ampm: 'pm',
|
|
557
615
|
};
|
|
558
616
|
this.isTimePopoverOpen = false;
|
|
617
|
+
this.forceRenderDate = undefined;
|
|
559
618
|
this.color = 'primary';
|
|
560
619
|
this.name = this.inputId;
|
|
561
620
|
this.disabled = false;
|
|
@@ -595,6 +654,11 @@ export class Datetime {
|
|
|
595
654
|
maxChanged() {
|
|
596
655
|
this.processMaxParts();
|
|
597
656
|
}
|
|
657
|
+
get isGridStyle() {
|
|
658
|
+
const { presentation, preferWheel } = this;
|
|
659
|
+
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
660
|
+
return hasDatePresentation && !preferWheel;
|
|
661
|
+
}
|
|
598
662
|
yearValuesChanged() {
|
|
599
663
|
this.parsedYearValues = convertToArrayOfNumbers(this.yearValues);
|
|
600
664
|
}
|
|
@@ -610,51 +674,13 @@ export class Datetime {
|
|
|
610
674
|
minuteValuesChanged() {
|
|
611
675
|
this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues);
|
|
612
676
|
}
|
|
613
|
-
activePartsChanged() {
|
|
614
|
-
this.activePartsClone = this.activeParts;
|
|
615
|
-
}
|
|
616
677
|
/**
|
|
617
678
|
* Update the datetime value when the value changes
|
|
618
679
|
*/
|
|
619
|
-
valueChanged() {
|
|
620
|
-
const { value
|
|
680
|
+
async valueChanged() {
|
|
681
|
+
const { value } = this;
|
|
621
682
|
if (this.hasValue()) {
|
|
622
|
-
this.
|
|
623
|
-
/**
|
|
624
|
-
* Clones the value of the `activeParts` to the private clone, to update
|
|
625
|
-
* the date display on the current render cycle without causing another render.
|
|
626
|
-
*
|
|
627
|
-
* This allows us to update the current value's date/time display without
|
|
628
|
-
* refocusing or shifting the user's display (leaves the user in place).
|
|
629
|
-
*/
|
|
630
|
-
const valueDateParts = parseDate(value);
|
|
631
|
-
if (valueDateParts) {
|
|
632
|
-
warnIfValueOutOfBounds(valueDateParts, minParts, maxParts);
|
|
633
|
-
if (Array.isArray(valueDateParts)) {
|
|
634
|
-
this.activePartsClone = [...valueDateParts];
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
const { month, day, year, hour, minute } = valueDateParts;
|
|
638
|
-
const ampm = hour != null ? (hour >= 12 ? 'pm' : 'am') : undefined;
|
|
639
|
-
this.activePartsClone = Object.assign(Object.assign({}, this.activeParts), { month,
|
|
640
|
-
day,
|
|
641
|
-
year,
|
|
642
|
-
hour,
|
|
643
|
-
minute,
|
|
644
|
-
ampm });
|
|
645
|
-
/**
|
|
646
|
-
* The working parts am/pm value must be updated when the value changes, to
|
|
647
|
-
* ensure the time picker hour column values are generated correctly.
|
|
648
|
-
*
|
|
649
|
-
* Note that we don't need to do this if valueDateParts is an array, since
|
|
650
|
-
* multiple="true" does not apply to time pickers.
|
|
651
|
-
*/
|
|
652
|
-
this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm }));
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
else {
|
|
656
|
-
printIonWarning(`Unable to parse date string: ${value}. Please provide a valid ISO 8601 datetime string.`);
|
|
657
|
-
}
|
|
683
|
+
this.processValue(value);
|
|
658
684
|
}
|
|
659
685
|
this.emitStyle();
|
|
660
686
|
this.ionValueChange.emit({ value });
|
|
@@ -857,8 +883,6 @@ export class Datetime {
|
|
|
857
883
|
printIonWarning('The highlightedDates property is not supported with preferWheel="true".', el);
|
|
858
884
|
}
|
|
859
885
|
}
|
|
860
|
-
this.processMinParts();
|
|
861
|
-
this.processMaxParts();
|
|
862
886
|
const hourValues = (this.parsedHourValues = convertToArrayOfNumbers(this.hourValues));
|
|
863
887
|
const minuteValues = (this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues));
|
|
864
888
|
const monthValues = (this.parsedMonthValues = convertToArrayOfNumbers(this.monthValues));
|
|
@@ -866,6 +890,8 @@ export class Datetime {
|
|
|
866
890
|
const dayValues = (this.parsedDayValues = convertToArrayOfNumbers(this.dayValues));
|
|
867
891
|
const todayParts = (this.todayParts = parseDate(getToday()));
|
|
868
892
|
this.defaultParts = getClosestValidDate(todayParts, monthValues, dayValues, yearValues, hourValues, minuteValues);
|
|
893
|
+
this.processMinParts();
|
|
894
|
+
this.processMaxParts();
|
|
869
895
|
this.processValue(this.value);
|
|
870
896
|
this.emitStyle();
|
|
871
897
|
}
|
|
@@ -1250,7 +1276,7 @@ export class Datetime {
|
|
|
1250
1276
|
const { el, highlightedDates, isDateEnabled, multiple } = this;
|
|
1251
1277
|
const referenceParts = { month, day, year };
|
|
1252
1278
|
const isCalendarPadding = day === null;
|
|
1253
|
-
const { isActive, isToday, ariaLabel, ariaSelected, disabled, text } = getCalendarDayState(this.locale, referenceParts, this.
|
|
1279
|
+
const { isActive, isToday, ariaLabel, ariaSelected, disabled, text } = getCalendarDayState(this.locale, referenceParts, this.activeParts, this.todayParts, this.minParts, this.maxParts, this.parsedDayValues);
|
|
1254
1280
|
const dateIsoString = convertDataToISO(referenceParts);
|
|
1255
1281
|
let isCalDayDisabled = isCalMonthDisabled || disabled;
|
|
1256
1282
|
if (!isCalDayDisabled && isDateEnabled !== undefined) {
|
|
@@ -1307,7 +1333,7 @@ export class Datetime {
|
|
|
1307
1333
|
}))));
|
|
1308
1334
|
}
|
|
1309
1335
|
renderCalendarBody() {
|
|
1310
|
-
return (h("div", { class: "calendar-body ion-focusable", ref: (el) => (this.calendarBodyRef = el), tabindex: "0" }, generateMonths(this.workingParts).map(({ month, year }) => {
|
|
1336
|
+
return (h("div", { class: "calendar-body ion-focusable", ref: (el) => (this.calendarBodyRef = el), tabindex: "0" }, generateMonths(this.workingParts, this.forceRenderDate).map(({ month, year }) => {
|
|
1311
1337
|
return this.renderMonth(month, year);
|
|
1312
1338
|
})));
|
|
1313
1339
|
}
|
|
@@ -1462,14 +1488,13 @@ export class Datetime {
|
|
|
1462
1488
|
}
|
|
1463
1489
|
}
|
|
1464
1490
|
render() {
|
|
1465
|
-
const { name, value, disabled, el, color, readonly, showMonthAndYear, preferWheel, presentation, size } = this;
|
|
1491
|
+
const { name, value, disabled, el, color, readonly, showMonthAndYear, preferWheel, presentation, size, isGridStyle, } = this;
|
|
1466
1492
|
const mode = getIonMode(this);
|
|
1467
1493
|
const isMonthAndYearPresentation = presentation === 'year' || presentation === 'month' || presentation === 'month-year';
|
|
1468
1494
|
const shouldShowMonthAndYear = showMonthAndYear || isMonthAndYearPresentation;
|
|
1469
1495
|
const monthYearPickerOpen = showMonthAndYear && !isMonthAndYearPresentation;
|
|
1470
1496
|
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
1471
1497
|
const hasWheelVariant = hasDatePresentation && preferWheel;
|
|
1472
|
-
const hasGrid = hasDatePresentation && !preferWheel;
|
|
1473
1498
|
renderHiddenInput(true, el, name, formatValue(value), disabled);
|
|
1474
1499
|
return (h(Host, { "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
|
|
1475
1500
|
[mode]: true,
|
|
@@ -1480,7 +1505,7 @@ export class Datetime {
|
|
|
1480
1505
|
[`datetime-presentation-${presentation}`]: true,
|
|
1481
1506
|
[`datetime-size-${size}`]: true,
|
|
1482
1507
|
[`datetime-prefer-wheel`]: hasWheelVariant,
|
|
1483
|
-
[`datetime-grid`]:
|
|
1508
|
+
[`datetime-grid`]: isGridStyle,
|
|
1484
1509
|
})) }, this.renderDatetime(mode)));
|
|
1485
1510
|
}
|
|
1486
1511
|
static get is() { return "ion-datetime"; }
|
|
@@ -2039,7 +2064,8 @@ export class Datetime {
|
|
|
2039
2064
|
"showMonthAndYear": {},
|
|
2040
2065
|
"activeParts": {},
|
|
2041
2066
|
"workingParts": {},
|
|
2042
|
-
"isTimePopoverOpen": {}
|
|
2067
|
+
"isTimePopoverOpen": {},
|
|
2068
|
+
"forceRenderDate": {}
|
|
2043
2069
|
};
|
|
2044
2070
|
}
|
|
2045
2071
|
static get events() {
|
|
@@ -2267,9 +2293,6 @@ export class Datetime {
|
|
|
2267
2293
|
}, {
|
|
2268
2294
|
"propName": "minuteValues",
|
|
2269
2295
|
"methodName": "minuteValuesChanged"
|
|
2270
|
-
}, {
|
|
2271
|
-
"propName": "activeParts",
|
|
2272
|
-
"methodName": "activePartsChanged"
|
|
2273
2296
|
}, {
|
|
2274
2297
|
"propName": "value",
|
|
2275
2298
|
"methodName": "valueChanged"
|
|
@@ -184,6 +184,21 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
|
|
184
184
|
await yearValues.nth(10).click();
|
|
185
185
|
await ionChange.next();
|
|
186
186
|
});
|
|
187
|
+
test('should jump to selected date when programmatically updating value', async ({ page }) => {
|
|
188
|
+
await page.setContent(`
|
|
189
|
+
<ion-datetime presentation="date" prefer-wheel="true" min="2019-05-05" max="2023-10-01" value="2019-05-30"></ion-datetime>
|
|
190
|
+
`, config);
|
|
191
|
+
await page.waitForSelector('.datetime-ready');
|
|
192
|
+
const datetime = page.locator('ion-datetime');
|
|
193
|
+
await datetime.evaluate((el) => (el.value = '2021-05-25T12:40:00.000Z'));
|
|
194
|
+
await page.waitForChanges();
|
|
195
|
+
const selectedMonth = datetime.locator('.month-column .picker-item-active');
|
|
196
|
+
const selectedDay = datetime.locator('.day-column .picker-item-active');
|
|
197
|
+
const selectedYear = datetime.locator('.year-column .picker-item-active');
|
|
198
|
+
await expect(selectedMonth).toHaveText(/May/);
|
|
199
|
+
await expect(selectedDay).toHaveText(/25/);
|
|
200
|
+
await expect(selectedYear).toHaveText(/2021/);
|
|
201
|
+
});
|
|
187
202
|
test.describe('datetime: date wheel localization', () => {
|
|
188
203
|
test('should correctly localize the date data', async ({ page }) => {
|
|
189
204
|
await page.setContent(`
|
|
@@ -30,24 +30,21 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
|
|
30
30
|
await page.waitForSelector('.datetime-ready');
|
|
31
31
|
const datetime = page.locator('ion-datetime');
|
|
32
32
|
const activeDayButton = page.locator('.calendar-day-active');
|
|
33
|
-
const monthYearButton = page.locator('.calendar-month-year');
|
|
34
|
-
const monthColumn = page.locator('.month-column');
|
|
35
|
-
const ionChange = await page.spyOnEvent('ionChange');
|
|
36
33
|
await datetime.evaluate((el) => (el.value = '2021-10-05'));
|
|
37
|
-
// Open month/year picker
|
|
38
|
-
await monthYearButton.click();
|
|
39
|
-
await page.waitForChanges();
|
|
40
|
-
// Select October 2021
|
|
41
|
-
// The year will automatically switch to 2021 when selecting 10
|
|
42
|
-
await monthColumn.locator('.picker-item[data-value="10"]').click();
|
|
43
|
-
await ionChange.next();
|
|
44
|
-
// Close month/year picker
|
|
45
|
-
await monthYearButton.click();
|
|
46
34
|
await page.waitForChanges();
|
|
47
35
|
// Check that correct day is highlighted
|
|
48
36
|
await expect(activeDayButton).toHaveAttribute('data-day', '5');
|
|
49
37
|
await expect(activeDayButton).toHaveAttribute('data-month', '10');
|
|
50
38
|
await expect(activeDayButton).toHaveAttribute('data-year', '2021');
|
|
51
39
|
});
|
|
40
|
+
test('should scroll to new month when value is initially set and then updated', async ({ page }) => {
|
|
41
|
+
await page.goto('/src/components/datetime/test/set-value', config);
|
|
42
|
+
await page.waitForSelector('.datetime-ready');
|
|
43
|
+
const datetime = page.locator('ion-datetime');
|
|
44
|
+
await datetime.evaluate((el) => (el.value = '2021-05-25T12:40:00.000Z'));
|
|
45
|
+
await page.waitForChanges();
|
|
46
|
+
const calendarHeader = datetime.locator('.calendar-month-year');
|
|
47
|
+
await expect(calendarHeader).toHaveText(/May 2021/);
|
|
48
|
+
});
|
|
52
49
|
});
|
|
53
50
|
});
|
|
@@ -220,12 +220,20 @@ export const generateTime = (refParts, hourCycle = 'h12', minParts, maxParts, ho
|
|
|
220
220
|
* Given DatetimeParts, generate the previous,
|
|
221
221
|
* current, and and next months.
|
|
222
222
|
*/
|
|
223
|
-
export const generateMonths = (refParts) => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
export const generateMonths = (refParts, forcedDate) => {
|
|
224
|
+
const current = { month: refParts.month, year: refParts.year, day: refParts.day };
|
|
225
|
+
/**
|
|
226
|
+
* If we're forcing a month to appear, and it's different from the current month,
|
|
227
|
+
* ensure it appears by replacing the next or previous month as appropriate.
|
|
228
|
+
*/
|
|
229
|
+
if (forcedDate !== undefined && (refParts.month !== forcedDate.month || refParts.year !== forcedDate.year)) {
|
|
230
|
+
const forced = { month: forcedDate.month, year: forcedDate.year, day: forcedDate.day };
|
|
231
|
+
const forcedMonthIsBefore = isBefore(forced, current);
|
|
232
|
+
return forcedMonthIsBefore
|
|
233
|
+
? [forced, current, getNextMonth(refParts)]
|
|
234
|
+
: [getPreviousMonth(refParts), current, forced];
|
|
235
|
+
}
|
|
236
|
+
return [getPreviousMonth(refParts), current, getNextMonth(refParts)];
|
|
229
237
|
};
|
|
230
238
|
export const getMonthColumnData = (locale, refParts, minParts, maxParts, monthValues, formatOptions = {
|
|
231
239
|
month: 'long',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
+
import { printIonWarning } from "../../../utils/logging/index";
|
|
4
5
|
import { isAfter, isBefore } from "./comparison";
|
|
5
6
|
import { getNumDaysInMonth } from "./helpers";
|
|
6
7
|
const ISO_8601_REGEXP =
|
|
@@ -47,7 +48,22 @@ export const getPartsFromCalendarDay = (el) => {
|
|
|
47
48
|
};
|
|
48
49
|
export function parseDate(val) {
|
|
49
50
|
if (Array.isArray(val)) {
|
|
50
|
-
|
|
51
|
+
const parsedArray = [];
|
|
52
|
+
for (const valStr of val) {
|
|
53
|
+
const parsedVal = parseDate(valStr);
|
|
54
|
+
/**
|
|
55
|
+
* If any of the values weren't parsed correctly, consider
|
|
56
|
+
* the entire batch incorrect. This simplifies the type
|
|
57
|
+
* signatures by having "undefined" be a general error case
|
|
58
|
+
* instead of returning (Datetime | undefined)[], which is
|
|
59
|
+
* harder for TS to perform type narrowing on.
|
|
60
|
+
*/
|
|
61
|
+
if (!parsedVal) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
parsedArray.push(parsedVal);
|
|
65
|
+
}
|
|
66
|
+
return parsedArray;
|
|
51
67
|
}
|
|
52
68
|
// manually parse IS0 cuz Date.parse cannot be trusted
|
|
53
69
|
// ISO 8601 format: 1994-12-15T13:47:20Z
|
|
@@ -67,6 +83,7 @@ export function parseDate(val) {
|
|
|
67
83
|
}
|
|
68
84
|
if (parse === null) {
|
|
69
85
|
// wasn't able to parse the ISO datetime
|
|
86
|
+
printIonWarning(`Unable to parse date string: ${val}. Please provide a valid ISO 8601 datetime string.`);
|
|
70
87
|
return undefined;
|
|
71
88
|
}
|
|
72
89
|
// ensure all the parse values exist with at least 0
|
|
@@ -54,6 +54,9 @@ export class DatetimeButton {
|
|
|
54
54
|
* to today's date and time if no value is set.
|
|
55
55
|
*/
|
|
56
56
|
const parsedDatetimes = parseDate(parsedValues.length > 0 ? parsedValues : [getToday()]);
|
|
57
|
+
if (!parsedDatetimes) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
57
60
|
/**
|
|
58
61
|
* If developers incorrectly use multiple="true"
|
|
59
62
|
* with non "date" datetimes, then just select
|
|
@@ -217,8 +217,8 @@ export class Modal {
|
|
|
217
217
|
*/
|
|
218
218
|
this.currentBreakpoint = this.initialBreakpoint;
|
|
219
219
|
const { inline, delegate } = this.getDelegate(true);
|
|
220
|
-
this.ionMount.emit();
|
|
221
220
|
this.usersElement = await attachComponent(delegate, el, this.component, ['ion-page'], this.componentProps, inline);
|
|
221
|
+
this.ionMount.emit();
|
|
222
222
|
/**
|
|
223
223
|
* When using the lazy loaded build of Stencil, we need to wait
|
|
224
224
|
* for every Stencil component instance to be ready before presenting
|