diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index db19fc0c2a..915776e762 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,6 +11,8 @@ # In each subsection folders are ordered first by depth, then alphabetically. # This should make it easy to add new rules without breaking existing ones. +* @ionic-team/framework + # Frameworks ## Angular diff --git a/.github/labeler.yml b/.github/labeler.yml index 545025db58..7ea70aaf7f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -6,16 +6,17 @@ # https://github.com/actions/labeler 'package: core': - - core/**/* +- changed-files: + - any-glob-to-any-file: ['core/**/*'] 'package: angular': - - packages/angular/**/* - - packages/angular-*/**/* +- changed-files: + - any-glob-to-any-file: ['packages/angular/**/*', 'packages/angular-*/**/*'] 'package: react': - - packages/react/**/* - - packages/react-*/**/* +- changed-files: + - any-glob-to-any-file: ['packages/react/**/*', 'packages/react-*/**/*'] 'package: vue': - - packages/vue/**/* - - packages/vue-*/**/* +- changed-files: + - any-glob-to-any-file: ['packages/vue/**/*', 'packages/vue-*/**/*'] diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 0241c7fa5b..a4e35060df 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -13,7 +13,7 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: actions/labeler@main + - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e812f94e6..5031d391cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29) + + +### Bug Fixes + +* **alert:** date inputs render correctly in mobile safari ([#28495](https://github.com/ionic-team/ionic-framework/issues/28495)) ([b833f0e](https://github.com/ionic-team/ionic-framework/commit/b833f0e826ddd261230e2e29b70e2dc884d8cb04)), closes [#28494](https://github.com/ionic-team/ionic-framework/issues/28494) +* **datetime:** allow disabling datetime with prefer-wheel ([#28511](https://github.com/ionic-team/ionic-framework/issues/28511)) ([01130e1](https://github.com/ionic-team/ionic-framework/commit/01130e12e1d73bbf558da9d4dffd7122822ff39c)) + + + + + ## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 43aaeb2e54..fc2d3cbd3a 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29) + + +### Bug Fixes + +* **alert:** date inputs render correctly in mobile safari ([#28495](https://github.com/ionic-team/ionic-framework/issues/28495)) ([b833f0e](https://github.com/ionic-team/ionic-framework/commit/b833f0e826ddd261230e2e29b70e2dc884d8cb04)), closes [#28494](https://github.com/ionic-team/ionic-framework/issues/28494) +* **datetime:** allow disabling datetime with prefer-wheel ([#28511](https://github.com/ionic-team/ionic-framework/issues/28511)) ([01130e1](https://github.com/ionic-team/ionic-framework/commit/01130e12e1d73bbf558da9d4dffd7122822ff39c)) + + + + + ## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21) diff --git a/core/api.txt b/core/api.txt index d2b8e7863d..b49c46ee5e 100644 --- a/core/api.txt +++ b/core/api.txt @@ -916,6 +916,7 @@ ion-picker-column,prop,disabled,boolean,false,false,false ion-picker-column,prop,items,PickerColumnItem[],[],false,false ion-picker-column,prop,mode,"ios" | "md",undefined,false,false ion-picker-column,prop,value,number | string | undefined,undefined,false,false +ion-picker-column,method,setFocus,setFocus() => Promise ion-picker-column,event,ionChange,PickerColumnItem,true ion-picker-column-option,shadow diff --git a/core/package-lock.json b/core/package-lock.json index 094268f95c..256d5876d6 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,15 +1,15 @@ { "name": "@ionic/core", - "version": "7.5.6", + "version": "7.5.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@ionic/core", - "version": "7.5.6", + "version": "7.5.7", "license": "MIT", "dependencies": { - "@stencil/core": "^4.7.2", + "@stencil/core": "^4.8.1", "ionicons": "^7.2.1", "tslib": "^2.1.0" }, @@ -1825,9 +1825,9 @@ } }, "node_modules/@stencil/core": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.7.2.tgz", - "integrity": "sha512-sPPDYrXiTbfeUF5CCyfqysXK/yfTHC4xYR1+nHzGkS2vhRSBOLp0oPuB+xkJLKA+K2ZqDJUxpOnDxy1CLWwBXA==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.1.tgz", + "integrity": "sha512-KG1H10j24rlyxIqOI4CG8/h9T7ObTv7giW2H3u1qXV4KKrLykDOpMcLzpqNXqL2Fki3s1QvHyl/oaRvi5waWVw==", "bin": { "stencil": "bin/stencil" }, @@ -12184,9 +12184,9 @@ "requires": {} }, "@stencil/core": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.7.2.tgz", - "integrity": "sha512-sPPDYrXiTbfeUF5CCyfqysXK/yfTHC4xYR1+nHzGkS2vhRSBOLp0oPuB+xkJLKA+K2ZqDJUxpOnDxy1CLWwBXA==" + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.1.tgz", + "integrity": "sha512-KG1H10j24rlyxIqOI4CG8/h9T7ObTv7giW2H3u1qXV4KKrLykDOpMcLzpqNXqL2Fki3s1QvHyl/oaRvi5waWVw==" }, "@stencil/react-output-target": { "version": "0.5.3", diff --git a/core/package.json b/core/package.json index ce1b12b960..79e803f515 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/core", - "version": "7.5.6", + "version": "7.5.7", "description": "Base components for Ionic", "keywords": [ "ionic", @@ -31,7 +31,7 @@ "loader/" ], "dependencies": { - "@stencil/core": "^4.7.2", + "@stencil/core": "^4.8.1", "ionicons": "^7.2.1", "tslib": "^2.1.0" }, diff --git a/core/scripts/testing/themes/dark.css b/core/scripts/testing/themes/dark.css new file mode 100644 index 0000000000..e263803d56 --- /dev/null +++ b/core/scripts/testing/themes/dark.css @@ -0,0 +1,151 @@ +/* + * Dark Colors + * ------------------------------------------- + */ + +:root { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; +} + +/* + * iOS Dark Theme + * ------------------------------------------- + */ + +.ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-toolbar-background: #0d0d0d; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; +} + +/* + * Material Design Dark Theme + * ------------------------------------------- + */ + +.md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; +} diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 22c336a309..a0c7f664ec 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1162,7 +1162,7 @@ export namespace Components { */ "autocorrect": 'on' | 'off'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus": boolean; /** @@ -1274,7 +1274,7 @@ export namespace Components { */ "required": boolean; /** - * Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. + * Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information. */ "setFocus": () => Promise; /** @@ -1977,6 +1977,10 @@ export namespace Components { */ "numericInput": boolean; "scrollActiveItemIntoView": () => Promise; + /** + * Sets focus on the scrollable container within the picker column. Use this method instead of the global `pickerColumn.focus()`. + */ + "setFocus": () => Promise; /** * Sets the value prop and fires the ionChange event. This is used when we need to fire ionChange from user-generated events that cannot be caught with normal input/change event listeners. */ @@ -2615,7 +2619,7 @@ export namespace Components { */ "searchIcon"?: string; /** - * Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. + * Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information. */ "setFocus": () => Promise; /** @@ -2964,7 +2968,7 @@ export namespace Components { */ "autocapitalize": string; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus": boolean; /** @@ -3064,7 +3068,7 @@ export namespace Components { */ "rows"?: number; /** - * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`. + * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`. See [managing focus](/docs/developing/managing-focus) for more information. */ "setFocus": () => Promise; /** @@ -5875,7 +5879,7 @@ declare namespace LocalJSX { */ "autocorrect"?: 'on' | 'off'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus"?: boolean; /** @@ -7734,7 +7738,7 @@ declare namespace LocalJSX { */ "autocapitalize"?: string; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ "autofocus"?: boolean; /** diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index 813e38c7f9..984e24cdaa 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -337,6 +337,17 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } render() { diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index 4568e45a38..90a1c281bb 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -376,6 +376,17 @@ export class Alert implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** diff --git a/core/src/components/datetime/datetime.scss b/core/src/components/datetime/datetime.scss index db00e15c82..babbbe7bdb 100644 --- a/core/src/components/datetime/datetime.scss +++ b/core/src/components/datetime/datetime.scss @@ -25,19 +25,6 @@ overflow: hidden; } -/** - * When using the wheel picker to switch - * between months, sometimes the allowed - * dates may be filtered. As a result, it - * is possible to get a layout shift as - * the picker column will shrink to fit the - * widest item in the column. Setting a minimum - * width avoids this layout shifting. - */ -ion-picker-column { - min-width: 26px; -} - :host(.datetime-size-fixed) { width: auto; height: auto; diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Chrome-linux.png index 26407824fb..9bc2b80193 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Firefox-linux.png index b12a1f8b12..8d81fe1fa6 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Safari-linux.png index af2ff059e1..38f09fb91d 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Chrome-linux.png index 6dc7b71ce3..3731e82d5c 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Firefox-linux.png index ee7bf829ec..a06ae51d4e 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Safari-linux.png index a560454e4d..403c627d5f 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Safari-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-time-picker-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Chrome-linux.png index 56375e49c2..3a56f7a941 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Firefox-linux.png index 877e6eebf3..1517f7c966 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Safari-linux.png index 5e5aca9604..63840441b7 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Chrome-linux.png index 9b4bce97bd..3310836997 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Firefox-linux.png index 5b1d76ba84..84272ed8a7 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Safari-linux.png index 6cc4006fbb..9cf42cc0a9 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-ios-rtl-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Chrome-linux.png index 4524ca6c27..f80559f6c3 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Firefox-linux.png index f2773c86bb..4bbe2da728 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Safari-linux.png index 1b151c9ade..8e19649b19 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Chrome-linux.png index 7f1b2f17d4..5b7a49549a 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Firefox-linux.png index adbc9d1c1f..aaa3cf1ac9 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Safari-linux.png index 43338a9f47..aaae021b26 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-base-md-rtl-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Chrome-linux.png index fb65b78b42..85452e324e 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Firefox-linux.png index 81a0f160e7..598871dc55 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Safari-linux.png index 1caa240c29..7b12da629e 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Chrome-linux.png index 89ec396220..9ed964d3ef 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Firefox-linux.png index 201d760f4e..31d9a87934 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Safari-linux.png index 8b96c75435..9fe1114583 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-ios-rtl-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Chrome-linux.png index 5110e8b679..cfb1f266e7 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Firefox-linux.png index 1f0bd6cc9c..de59c033dc 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Safari-linux.png index 9f202c3224..71775a0e60 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Chrome-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Chrome-linux.png index 62e1d096d1..911b1c90d4 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Chrome-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Firefox-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Firefox-linux.png index 16c4c82ce0..a3d1d1666d 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Safari-linux.png b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Safari-linux.png index e3c68df1bc..b964fcaf54 100644 Binary files a/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Safari-linux.png and b/core/src/components/datetime/test/position/datetime.e2e.ts-snapshots/datetime-position-popover-md-rtl-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/position/index.html b/core/src/components/datetime/test/position/index.html index 3da97aa417..51909e8e21 100644 --- a/core/src/components/datetime/test/position/index.html +++ b/core/src/components/datetime/test/position/index.html @@ -25,7 +25,7 @@ Present Popover - + diff --git a/core/src/components/datetime/test/prefer-wheel/datetime.e2e.ts b/core/src/components/datetime/test/prefer-wheel/datetime.e2e.ts index 79ad3c64a6..7690360de5 100644 --- a/core/src/components/datetime/test/prefer-wheel/datetime.e2e.ts +++ b/core/src/components/datetime/test/prefer-wheel/datetime.e2e.ts @@ -447,6 +447,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await page.waitForSelector('.datetime-ready'); const dateColumn = page.locator('.date-column'); + const dateColumnScrollEl = dateColumn.locator('.picker-opts'); const dateValues = dateColumn.locator('.picker-item:not(.picker-item-empty)'); expect(await dateValues.count()).toBe(90); @@ -455,7 +456,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { * Select 1st item to change the dates rendered */ await expect(dateValues.nth(0)).toHaveAttribute('data-value', '2022-1-1'); - await dateColumn.evaluate((el: HTMLElement) => (el.scrollTop = 0)); + await dateColumnScrollEl.evaluate((el: HTMLElement) => (el.scrollTop = 0)); await page.waitForChanges(); await expect(dateValues.nth(0)).toHaveAttribute('data-value', '2021-12-1'); @@ -632,6 +633,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await page.waitForSelector('.datetime-ready'); const dateColumn = page.locator('.date-column'); + const dateColumnScrollEl = dateColumn.locator('.picker-opts'); const dateValues = dateColumn.locator('.picker-item:not(.picker-item-empty)'); expect(await dateValues.count()).toBe(90); @@ -640,7 +642,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { * Select 1st item to change the dates rendered */ await expect(dateValues.nth(0)).toHaveAttribute('data-value', '2022-1-1'); - await dateColumn.evaluate((el: HTMLElement) => (el.scrollTop = 0)); + await dateColumnScrollEl.evaluate((el: HTMLElement) => (el.scrollTop = 0)); await page.waitForChanges(); await expect(dateValues.nth(0)).toHaveAttribute('data-value', '2021-12-1'); diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 1c54046718..92bf5fdeff 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -95,7 +95,9 @@ export class Input implements ComponentInterface { @Prop() autocorrect: 'on' | 'off' = 'off'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. + * + * This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ @Prop() autofocus = false; @@ -424,6 +426,8 @@ export class Input implements ComponentInterface { * * Developers who wish to focus an input when an overlay is presented * should call `setFocus` after `didPresent` has resolved. + * + * See [managing focus](/docs/developing/managing-focus) for more information. */ @Method() async setFocus() { diff --git a/core/src/components/loading/loading.tsx b/core/src/components/loading/loading.tsx index 0892b082d1..05e40669d9 100644 --- a/core/src/components/loading/loading.tsx +++ b/core/src/components/loading/loading.tsx @@ -225,6 +225,17 @@ export class Loading implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } disconnectedCallback() { diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index 3c8a091901..ef6114b18b 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -368,6 +368,17 @@ export class Modal implements ComponentInterface, OverlayInterface { raf(() => this.present()); } this.breakpointsChanged(this.breakpoints); + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** diff --git a/core/src/components/picker-column/picker-column.scss b/core/src/components/picker-column/picker-column.scss index 3e0a1937ae..d040174459 100644 --- a/core/src/components/picker-column/picker-column.scss +++ b/core/src/components/picker-column/picker-column.scss @@ -4,13 +4,53 @@ // -------------------------------------------------- :host { - @include padding(0px, 16px, 0px, 16px); + display: flex; + + align-items: center; + + justify-content: center; + + max-width: 100%; height: 200px; + font-size: 22px; + + text-align: center; +} + +.picker-opts { + /** + * This padding must be set here and not on the + * host to ensure that the focus highlight on the + * column is not overly narrow. + */ + @include padding(0px, 16px, 0px, 16px); + + /** + * When using reactive data, such as in datetime's + * wheel picker, there may be layout shifts between + * individual columns in a picker as the columns + * shrink to fit the widest item in the column. + * Setting a minimum width avoids this layout shifting. + * + * Note that we need this min-width set on the same + * element as the vertical padding (i.e. not on the + * host). + */ + min-width: 26px; + max-height: 200px; + outline: none; - font-size: 22px; + /** + * Added so that text-align can be set on the host + * from external components. For example, datetime + * changes the alignment of specific columns in its + * wheel pickers. + */ + text-align: inherit; + scroll-snap-type: y mandatory; /** @@ -22,17 +62,45 @@ // Hide scrollbars on Firefox scrollbar-width: none; - - text-align: center; } /** * Hide scrollbars on Chrome and Safari */ -:host::-webkit-scrollbar { +.picker-opts::-webkit-scrollbar { display: none; } +::slotted([slot="prefix"]), ::slotted([slot="suffix"]) { + max-width: 200px; + + text-overflow: ellipsis; + + white-space: nowrap; + + overflow: hidden; +} + +::slotted([slot="prefix"]) { + /** + * This is added in addition to the column's own padding so that there is white + * space between the slot and the column's focus highlight. + */ + @include padding(0, 16px); + + justify-content: end; +} + +::slotted([slot="suffix"]) { + /** + * This is added in addition to the column's own padding so that there is white + * space between the slot and the column's focus highlight. + */ + @include padding(0, 16px); + + justify-content: start; +} + :host .picker-item { @include padding(0); @include margin(0); @@ -93,7 +161,7 @@ } @media (any-hover: hover) { - :host(:focus) { + :host(:focus) .picker-opts { outline: none; background: current-color(base, 0.2); diff --git a/core/src/components/picker-column/picker-column.tsx b/core/src/components/picker-column/picker-column.tsx index f615eed530..a6726cc231 100644 --- a/core/src/components/picker-column/picker-column.tsx +++ b/core/src/components/picker-column/picker-column.tsx @@ -13,6 +13,9 @@ import type { PickerColumnItem } from './picker-column-interfaces'; /** * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use. + * + * @slot prefix - Content to show on the left side of the picker options. + * @slot suffix - Content to show on the right side of the picker options. */ @Component({ tag: 'ion-picker-column', @@ -23,6 +26,7 @@ import type { PickerColumnItem } from './picker-column-interfaces'; shadow: true, }) export class PickerColumn implements ComponentInterface { + private scrollEl?: HTMLDivElement | null; private destroyScrollListener?: () => void; private isScrolling = false; private scrollEndCallback?: () => void; @@ -175,13 +179,25 @@ export class PickerColumn implements ComponentInterface { } } + /** + * Sets focus on the scrollable container within the picker column. + * Use this method instead of the global `pickerColumn.focus()`. + */ + @Method() + async setFocus() { + if (this.scrollEl) { + this.scrollEl.focus(); + } + } + private centerPickerItemInView = (target: HTMLElement, smooth = true, canExitInputMode = true) => { - const { el, isColumnVisible } = this; - if (isColumnVisible) { + const { isColumnVisible, scrollEl } = this; + + if (isColumnVisible && scrollEl) { // (Vertical offset from parent) - (three empty picker rows) + (half the height of the target to ensure the scroll triggers) const top = target.offsetTop - 3 * target.clientHeight + target.clientHeight / 2; - if (el.scrollTop !== top) { + if (scrollEl.scrollTop !== top) { /** * Setting this flag prevents input * mode from exiting in the picker column's @@ -190,7 +206,7 @@ export class PickerColumn implements ComponentInterface { * of these can cause a scroll to occur. */ this.canExitInputMode = canExitInputMode; - el.scroll({ + scrollEl.scroll({ top, left: 0, behavior: smooth ? 'smooth' : undefined, @@ -267,13 +283,15 @@ export class PickerColumn implements ComponentInterface { * be disabled on Android. */ const enableHaptics = isPlatform('ios'); - const { el } = this; + const { el, scrollEl } = this; let timeout: ReturnType | undefined; let activeEl: HTMLElement | null = this.activeItem; const scrollCallback = () => { raf(() => { + if (!scrollEl) return; + if (timeout) { clearTimeout(timeout); timeout = undefined; @@ -288,7 +306,7 @@ export class PickerColumn implements ComponentInterface { * Select item in the center of the column * which is the month/year that we want to select */ - const bbox = el.getBoundingClientRect(); + const bbox = scrollEl.getBoundingClientRect(); const centerX = bbox.x + bbox.width / 2; const centerY = bbox.y + bbox.height / 2; @@ -378,10 +396,12 @@ export class PickerColumn implements ComponentInterface { * does not fire when component is initially shown. */ raf(() => { - el.addEventListener('scroll', scrollCallback); + if (!scrollEl) return; + + scrollEl.addEventListener('scroll', scrollCallback); this.destroyScrollListener = () => { - el.removeEventListener('scroll', scrollCallback); + scrollEl.removeEventListener('scroll', scrollCallback); }; }); }; @@ -434,62 +454,71 @@ export class PickerColumn implements ComponentInterface { - - - - {items.map((item, index) => { - const isItemDisabled = pickerDisabled || item.disabled || false; + +
{ + this.scrollEl = el; + }} + > + + + + {items.map((item, index) => { + const isItemDisabled = pickerDisabled || item.disabled || false; - { - /* - Users should be able to tab - between multiple columns. As a result, - we set tabindex here so that tabbing switches - between columns instead of buttons. Users - can still use arrow keys on the keyboard to - navigate the column up and down. - */ - } - return ( - - ); - })} - - - + ); } diff --git a/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Chrome-linux.png b/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Chrome-linux.png index 6968e26058..5895f3ee25 100644 Binary files a/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Firefox-linux.png b/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Firefox-linux.png index 02bc29af22..03159c4cec 100644 Binary files a/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Safari-linux.png b/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Safari-linux.png index d94af6fdb6..2b52b5bdb9 100644 Binary files a/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Safari-linux.png and b/core/src/components/picker-column/test/disabled/picker-column.e2e.ts-snapshots/picker-disabled-column-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/picker-column/test/slots/index.html b/core/src/components/picker-column/test/slots/index.html new file mode 100644 index 0000000000..57f5b8dac9 --- /dev/null +++ b/core/src/components/picker-column/test/slots/index.html @@ -0,0 +1,50 @@ + + + + + Picker Column - Slots + + + + + + + + + + + + Picker Column - Basic + + + + + +
Prefix
+
Suffix
+
+
+ + +
Prefix
+
Long Suffix Long Suffix Long Suffix
+
+
+
+ +
+ + diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts b/core/src/components/picker-column/test/slots/picker-column.e2e.ts new file mode 100644 index 0000000000..7e4b5ce678 --- /dev/null +++ b/core/src/components/picker-column/test/slots/picker-column.e2e.ts @@ -0,0 +1,58 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('picker-column: slots'), () => { + test('should not have visual regressions with prefix and suffix', async ({ page }) => { + await page.setContent( + ` + + +
Prefix
+
Suffix
+
+
+ + + `, + config + ); + + const picker = page.locator('ion-picker'); + await expect(picker).toHaveScreenshot(screenshot(`picker-prefix-suffix`)); + }); + + test('should not have visual regressions with a long prefix and suffix', async ({ page }) => { + await page.setContent( + ` + + +
Long prefix long prefix long prefix
+
Long suffix long suffix long suffix
+
+
+ + + `, + config + ); + + const picker = page.locator('ion-picker'); + await expect(picker).toHaveScreenshot(screenshot(`picker-long-prefix-suffix`)); + }); + }); +}); diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..bd46589f87 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..7ed8c026bb Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Safari-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..1bd6ef9349 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Chrome-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..58a8fa85ae Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Firefox-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..22385e3b0a Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Safari-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..abc8bdde05 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-long-prefix-suffix-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..35c2a4a7af Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..d00a871ad8 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Safari-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..0a6d49fb68 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Chrome-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..249d08e1d8 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Firefox-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..42dcad8dd7 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Safari-linux.png b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..f0d5decfd0 Binary files /dev/null and b/core/src/components/picker-column/test/slots/picker-column.e2e.ts-snapshots/picker-prefix-suffix-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/picker/test/basic/picker.e2e.ts b/core/src/components/picker/test/basic/picker.e2e.ts index e464a48bed..a0a46952dd 100644 --- a/core/src/components/picker/test/basic/picker.e2e.ts +++ b/core/src/components/picker/test/basic/picker.e2e.ts @@ -131,7 +131,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const firstColumn = page.locator('ion-picker-column#first'); const secondColumn = page.locator('ion-picker-column#second'); - await secondColumn.focus(); + await secondColumn.evaluate((el: HTMLIonPickerColumnElement) => el.setFocus()); await expect(secondColumn).toBeFocused(); await page.waitForChanges(); diff --git a/core/src/components/picker/test/basic/picker.e2e.ts-snapshots/picker-popover-diff-md-ltr-Mobile-Chrome-linux.png b/core/src/components/picker/test/basic/picker.e2e.ts-snapshots/picker-popover-diff-md-ltr-Mobile-Chrome-linux.png index 578e53e8d8..98ea20a8ad 100644 Binary files a/core/src/components/picker/test/basic/picker.e2e.ts-snapshots/picker-popover-diff-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/picker/test/basic/picker.e2e.ts-snapshots/picker-popover-diff-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/picker/test/keyboard-entry/picker.e2e.ts b/core/src/components/picker/test/keyboard-entry/picker.e2e.ts index ae2210e0c0..cee9d192af 100644 --- a/core/src/components/picker/test/keyboard-entry/picker.e2e.ts +++ b/core/src/components/picker/test/keyboard-entry/picker.e2e.ts @@ -32,7 +32,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const column = page.locator('ion-picker-column'); const ionChange = await page.spyOnEvent('ionChange'); - await column.focus(); + await column.evaluate((el: HTMLIonPickerColumnElement) => el.setFocus()); await page.keyboard.press('Digit2'); @@ -126,7 +126,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const column = page.locator('ion-picker-column'); const ionChange = await page.spyOnEvent('ionChange'); - await column.focus(); + await column.evaluate((el: HTMLIonPickerColumnElement) => el.setFocus()); await page.keyboard.press('Digit0'); diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index 75e16ad70d..21dce20a5d 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -370,6 +370,17 @@ export class Popover implements ComponentInterface, PopoverInterface { this.dismiss(undefined, undefined, false); }); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.configureTriggerInteraction(); } /** diff --git a/core/src/components/searchbar/searchbar.tsx b/core/src/components/searchbar/searchbar.tsx index 84ae9b2acc..21fed733d2 100644 --- a/core/src/components/searchbar/searchbar.tsx +++ b/core/src/components/searchbar/searchbar.tsx @@ -257,6 +257,8 @@ export class Searchbar implements ComponentInterface { * * Developers who wish to focus an input when an overlay is presented * should call `setFocus` after `didPresent` has resolved. + * + * See [managing focus](/docs/developing/managing-focus) for more information. */ @Method() async setFocus() { diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 6159f64790..31ec5eb616 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -93,7 +93,9 @@ export class Textarea implements ComponentInterface { @Prop() autocapitalize = 'none'; /** - * This Boolean attribute lets you specify that a form control should have input focus when the page loads. + * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. + * + * This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. */ @Prop() autofocus = false; @@ -372,6 +374,8 @@ export class Textarea implements ComponentInterface { /** * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global * `textarea.focus()`. + * + * See [managing focus](/docs/developing/managing-focus) for more information. */ @Method() async setFocus() { diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index b7945400b5..0a4fc420fc 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -288,6 +288,17 @@ export class Toast implements ComponentInterface, OverlayInterface { if (this.isOpen === true) { raf(() => this.present()); } + + /** + * When binding values in frameworks such as Angular + * it is possible for the value to be set after the Web Component + * initializes but before the value watcher is set up in Stencil. + * As a result, the watcher callback may not be fired. + * We work around this by manually calling the watcher + * callback when the component has loaded and the watcher + * is configured. + */ + this.triggerChanged(); } /** diff --git a/core/src/utils/test/playwright/generator.ts b/core/src/utils/test/playwright/generator.ts index e2cdd940f9..41b2abc183 100644 --- a/core/src/utils/test/playwright/generator.ts +++ b/core/src/utils/test/playwright/generator.ts @@ -1,5 +1,12 @@ export type Mode = 'ios' | 'md'; export type Direction = 'ltr' | 'rtl'; +/** + * The theme to use for the playwright test. + * + * - `light`: The fallback theme values. Theme stylesheet will not be included. + * - `dark`: The dark theme values. + */ +export type Theme = 'light' | 'dark'; export type TitleFn = (title: string) => string; export type ScreenshotFn = (fileName: string) => string; @@ -7,6 +14,7 @@ export type ScreenshotFn = (fileName: string) => string; export interface TestConfig { mode: Mode; direction: Direction; + theme: Theme; } interface TestUtilities { @@ -18,6 +26,7 @@ interface TestUtilities { interface TestConfigOption { modes?: Mode[]; directions?: Direction[]; + themes?: Theme[]; } /** @@ -27,9 +36,19 @@ interface TestConfigOption { * each test title is unique. */ const generateTitle = (title: string, config: TestConfig): string => { - const { mode, direction } = config; + const { mode, direction, theme } = config; - return `${title} - ${mode}/${direction}`; + if (theme === 'light') { + /** + * Ionic has many existing tests that existed prior to + * the introduction of theme testing. To maintain backwards + * compatibility, we will not include the theme in the test + * title if the theme is set to light. + */ + return `${title} - ${mode}/${direction}`; + } + + return `${title} - ${mode}/${direction}/${theme}`; }; /** @@ -37,9 +56,19 @@ const generateTitle = (title: string, config: TestConfig): string => { * and a test config. */ const generateScreenshotName = (fileName: string, config: TestConfig): string => { - const { mode, direction } = config; + const { mode, direction, theme } = config; - return `${fileName}-${mode}-${direction}.png`; + if (theme === 'light') { + /** + * Ionic has many existing tests that existed prior to + * the introduction of theme testing. To maintain backwards + * compatibility, we will not include the theme in the screenshot + * name if the theme is set to light. + */ + return `${fileName}-${mode}-${direction}.png`; + } + + return `${fileName}-${mode}-${direction}-${theme}.png`; }; /** @@ -54,12 +83,15 @@ export const configs = (testConfig: TestConfigOption = DEFAULT_TEST_CONFIG_OPTIO * If certain options are not provided, * fall back to the defaults. */ - const processedMode: Mode[] = modes ?? DEFAULT_MODES; - const processedDirection: Direction[] = directions ?? DEFAULT_DIRECTIONS; + const processedMode = modes ?? DEFAULT_MODES; + const processedDirection = directions ?? DEFAULT_DIRECTIONS; + const processedTheme = testConfig.themes ?? DEFAULT_THEMES; - processedMode.forEach((mode: Mode) => { - processedDirection.forEach((direction: Direction) => { - configs.push({ mode, direction }); + processedMode.forEach((mode) => { + processedDirection.forEach((direction) => { + processedTheme.forEach((theme) => { + configs.push({ mode, direction, theme }); + }); }); }); @@ -74,6 +106,7 @@ export const configs = (testConfig: TestConfigOption = DEFAULT_TEST_CONFIG_OPTIO const DEFAULT_MODES: Mode[] = ['ios', 'md']; const DEFAULT_DIRECTIONS: Direction[] = ['ltr', 'rtl']; +const DEFAULT_THEMES: Theme[] = ['light']; const DEFAULT_TEST_CONFIG_OPTION = { modes: DEFAULT_MODES, diff --git a/core/src/utils/test/playwright/page/utils/set-content.ts b/core/src/utils/test/playwright/page/utils/set-content.ts index 3087789cdf..1f0e6b2a4a 100644 --- a/core/src/utils/test/playwright/page/utils/set-content.ts +++ b/core/src/utils/test/playwright/page/utils/set-content.ts @@ -1,5 +1,5 @@ import type { Page, TestInfo } from '@playwright/test'; -import type { E2EPageOptions, Mode, Direction } from '@utils/test/playwright'; +import type { E2EPageOptions, Mode, Direction, Theme } from '@utils/test/playwright'; /** * Overwrites the default Playwright page.setContent method. @@ -19,25 +19,30 @@ export const setContent = async (page: Page, html: string, testInfo: TestInfo, o let mode: Mode; let direction: Direction; + let theme: Theme; if (options == undefined) { mode = testInfo.project.metadata.mode; direction = testInfo.project.metadata.rtl ? 'rtl' : 'ltr'; + theme = testInfo.project.metadata.theme; } else { mode = options.mode; direction = options.direction; + theme = options.theme; } const baseUrl = process.env.PLAYWRIGHT_TEST_BASE_URL; const output = ` - + + Ionic Playwright Test + ${theme !== 'light' ? `` : ''} diff --git a/packages/vue/test/base/src/components/NavRoot.vue b/packages/vue/test/base/src/components/NavRoot.vue index ec2fe1c513..ffd18e6765 100644 --- a/packages/vue/test/base/src/components/NavRoot.vue +++ b/packages/vue/test/base/src/components/NavRoot.vue @@ -8,11 +8,14 @@ - Go to Nav Child + + Go to Nav Child - diff --git a/packages/vue/test/base/tests/e2e/specs/navigation.cy.js b/packages/vue/test/base/tests/e2e/specs/navigation.cy.js index d8f75e7163..25788bf759 100644 --- a/packages/vue/test/base/tests/e2e/specs/navigation.cy.js +++ b/packages/vue/test/base/tests/e2e/specs/navigation.cy.js @@ -10,4 +10,10 @@ describe('Navigation', () => { cy.get('#nav-child-content').should('have.text', 'Custom Title'); }); + + it('nav should support kebab-case root-params', () => { + cy.get('#open-nav-modal').click(); + + cy.get('#nav-root-params').should('have.text', 'Message: Hello World!'); + }); });