diff --git a/.github/workflows/actions/build-angular-server/action.yml b/.github/workflows/actions/build-angular-server/action.yml index cda8c10736d..b530d300788 100644 --- a/.github/workflows/actions/build-angular-server/action.yml +++ b/.github/workflows/actions/build-angular-server/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/build-angular/action.yml b/.github/workflows/actions/build-angular/action.yml index b573af298c4..80da1c353d7 100644 --- a/.github/workflows/actions/build-angular/action.yml +++ b/.github/workflows/actions/build-angular/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/build-core-stencil-prerelease/action.yml b/.github/workflows/actions/build-core-stencil-prerelease/action.yml index 3e31ea6c26a..de2a0ee98c5 100644 --- a/.github/workflows/actions/build-core-stencil-prerelease/action.yml +++ b/.github/workflows/actions/build-core-stencil-prerelease/action.yml @@ -11,7 +11,7 @@ runs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - name: Install Dependencies run: npm ci diff --git a/.github/workflows/actions/build-core/action.yml b/.github/workflows/actions/build-core/action.yml index 3a7ff82b61e..28006742feb 100644 --- a/.github/workflows/actions/build-core/action.yml +++ b/.github/workflows/actions/build-core/action.yml @@ -11,7 +11,7 @@ runs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - name: Install Dependencies run: npm install working-directory: ./core diff --git a/.github/workflows/actions/build-react-router/action.yml b/.github/workflows/actions/build-react-router/action.yml index 25945e6dc53..390378cb12d 100644 --- a/.github/workflows/actions/build-react-router/action.yml +++ b/.github/workflows/actions/build-react-router/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/build-react/action.yml b/.github/workflows/actions/build-react/action.yml index a3a9c95a81c..3ea565d62e3 100644 --- a/.github/workflows/actions/build-react/action.yml +++ b/.github/workflows/actions/build-react/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/build-vue-router/action.yml b/.github/workflows/actions/build-vue-router/action.yml index f5630acdee5..623bdc4c7a1 100644 --- a/.github/workflows/actions/build-vue-router/action.yml +++ b/.github/workflows/actions/build-vue-router/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/build-vue/action.yml b/.github/workflows/actions/build-vue/action.yml index 29e7966fc99..f2be91e1090 100644 --- a/.github/workflows/actions/build-vue/action.yml +++ b/.github/workflows/actions/build-vue/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/publish-npm/action.yml b/.github/workflows/actions/publish-npm/action.yml index 81ce77a0bd2..6bd557db3b9 100644 --- a/.github/workflows/actions/publish-npm/action.yml +++ b/.github/workflows/actions/publish-npm/action.yml @@ -21,7 +21,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x # Provenance requires npm 9.5.0+ - name: Install latest npm run: npm install -g npm@latest diff --git a/.github/workflows/actions/test-core-clean-build/action.yml b/.github/workflows/actions/test-core-clean-build/action.yml index 5039f5fa81a..d822e69468d 100644 --- a/.github/workflows/actions/test-core-clean-build/action.yml +++ b/.github/workflows/actions/test-core-clean-build/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: diff --git a/.github/workflows/actions/test-core-lint/action.yml b/.github/workflows/actions/test-core-lint/action.yml index 9bea162758f..a4298c2c0a1 100644 --- a/.github/workflows/actions/test-core-lint/action.yml +++ b/.github/workflows/actions/test-core-lint/action.yml @@ -5,7 +5,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - name: Install Dependencies run: npm ci working-directory: ./core diff --git a/.github/workflows/actions/test-core-screenshot/action.yml b/.github/workflows/actions/test-core-screenshot/action.yml index 2afdf38d8d4..588c310b462 100644 --- a/.github/workflows/actions/test-core-screenshot/action.yml +++ b/.github/workflows/actions/test-core-screenshot/action.yml @@ -15,7 +15,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/test-core-spec/action.yml b/.github/workflows/actions/test-core-spec/action.yml index 34055327e89..f6246664066 100644 --- a/.github/workflows/actions/test-core-spec/action.yml +++ b/.github/workflows/actions/test-core-spec/action.yml @@ -8,7 +8,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - name: Install Dependencies run: npm ci working-directory: ./core diff --git a/.github/workflows/actions/test-react-e2e/action.yml b/.github/workflows/actions/test-react-e2e/action.yml index dd7b4293fdd..ab056ac667b 100644 --- a/.github/workflows/actions/test-react-e2e/action.yml +++ b/.github/workflows/actions/test-react-e2e/action.yml @@ -8,7 +8,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/test-react-router-e2e/action.yml b/.github/workflows/actions/test-react-router-e2e/action.yml index e3e5be56d34..cf71e4da5aa 100644 --- a/.github/workflows/actions/test-react-router-e2e/action.yml +++ b/.github/workflows/actions/test-react-router-e2e/action.yml @@ -8,7 +8,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/test-vue-e2e/action.yml b/.github/workflows/actions/test-vue-e2e/action.yml index 26b713c3447..93a21db7855 100644 --- a/.github/workflows/actions/test-vue-e2e/action.yml +++ b/.github/workflows/actions/test-vue-e2e/action.yml @@ -8,7 +8,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: ./.github/workflows/actions/download-archive with: name: ionic-core diff --git a/.github/workflows/actions/update-reference-screenshots/action.yml b/.github/workflows/actions/update-reference-screenshots/action.yml index 0afcdbb7467..256676fe484 100644 --- a/.github/workflows/actions/update-reference-screenshots/action.yml +++ b/.github/workflows/actions/update-reference-screenshots/action.yml @@ -9,7 +9,7 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - uses: actions/download-artifact@v4 with: path: ./artifacts diff --git a/CHANGELOG.md b/CHANGELOG.md index a4d23adbdcd..c8ead904454 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.5.5](https://github.com/ionic-team/ionic-framework/compare/v8.5.4...v8.5.5) (2025-04-16) + + +### Bug Fixes + +* **config:** allow LogLevel to work with isolatedModules and update all warns and errors to respect logLevel ([#30350](https://github.com/ionic-team/ionic-framework/issues/30350)) ([d52fca0](https://github.com/ionic-team/ionic-framework/commit/d52fca084cf5a9924d0f6a6c4d9ece0373b83213)) +* **modal:** add expandToScroll property to ModalOptions ([#30357](https://github.com/ionic-team/ionic-framework/issues/30357)) ([8dd566b](https://github.com/ionic-team/ionic-framework/commit/8dd566b5c1241022e26cc91c0f415de20c0d0f34)), closes [#30356](https://github.com/ionic-team/ionic-framework/issues/30356) +* **select:** update icon color and use correct focused class ([#30342](https://github.com/ionic-team/ionic-framework/issues/30342)) ([cad1c61](https://github.com/ionic-team/ionic-framework/commit/cad1c61528c52a53b2164f2ea46f49144d3b46ad)) +* **toggle:** ensure proper visual selection when navigating via VoiceOver in Safari ([#30349](https://github.com/ionic-team/ionic-framework/issues/30349)) ([b1bc58f](https://github.com/ionic-team/ionic-framework/commit/b1bc58f1c8ffdc859e3f4349040bb1ad6e383d1e)) + + + + + ## [8.5.4](https://github.com/ionic-team/ionic-framework/compare/v8.5.3...v8.5.4) (2025-04-09) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 1975017a0b6..21165dea9ea 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.5.5](https://github.com/ionic-team/ionic-framework/compare/v8.5.4...v8.5.5) (2025-04-16) + + +### Bug Fixes + +* **config:** allow LogLevel to work with isolatedModules and update all warns and errors to respect logLevel ([#30350](https://github.com/ionic-team/ionic-framework/issues/30350)) ([d52fca0](https://github.com/ionic-team/ionic-framework/commit/d52fca084cf5a9924d0f6a6c4d9ece0373b83213)) +* **modal:** add expandToScroll property to ModalOptions ([#30357](https://github.com/ionic-team/ionic-framework/issues/30357)) ([8dd566b](https://github.com/ionic-team/ionic-framework/commit/8dd566b5c1241022e26cc91c0f415de20c0d0f34)), closes [#30356](https://github.com/ionic-team/ionic-framework/issues/30356) +* **select:** update icon color and use correct focused class ([#30342](https://github.com/ionic-team/ionic-framework/issues/30342)) ([cad1c61](https://github.com/ionic-team/ionic-framework/commit/cad1c61528c52a53b2164f2ea46f49144d3b46ad)) +* **toggle:** ensure proper visual selection when navigating via VoiceOver in Safari ([#30349](https://github.com/ionic-team/ionic-framework/issues/30349)) ([b1bc58f](https://github.com/ionic-team/ionic-framework/commit/b1bc58f1c8ffdc859e3f4349040bb1ad6e383d1e)) + + + + + ## [8.5.4](https://github.com/ionic-team/ionic-framework/compare/v8.5.3...v8.5.4) (2025-04-09) diff --git a/core/package-lock.json b/core/package-lock.json index 56b47dc724b..23bd8b3e077 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ionic/core", - "version": "8.5.4", + "version": "8.5.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@ionic/core", - "version": "8.5.4", + "version": "8.5.5", "license": "MIT", "dependencies": { "@stencil/core": "4.20.0", @@ -663,9 +663,9 @@ "dev": true }, "node_modules/@capacitor/core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.1.0.tgz", - "integrity": "sha512-I0a4C8gux5sx+HDamJjCiWHEWRdJU3hejwURFOSwJjUmAMkfkrm4hOsI0dgd+S0eCkKKKYKz9WNm7DAIvhm2zw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.2.0.tgz", + "integrity": "sha512-2zCnA6RJeZ9ec4470o8QMZEQTWpekw9FNoqm5TLc10jeCrhvHVI8MPgxdZVc3mOdFlyieYu4AS1fNxSqbS57Pw==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -702,9 +702,9 @@ } }, "node_modules/@clack/core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.4.1.tgz", - "integrity": "sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.4.2.tgz", + "integrity": "sha512-NYQfcEy8MWIxrT5Fj8nIVchfRFA26yYKJcvBS7WlUIlw2OmQOY9DhGGXMovyI5J5PpxrCPGkgUi207EBrjpBvg==", "dev": true, "dependencies": { "picocolors": "^1.0.0", @@ -712,12 +712,12 @@ } }, "node_modules/@clack/prompts": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.10.0.tgz", - "integrity": "sha512-H3rCl6CwW1NdQt9rE3n373t7o5cthPv7yUoxF2ytZvyvlJv89C5RYMJu83Hed8ODgys5vpBU0GKxIRG83jd8NQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.10.1.tgz", + "integrity": "sha512-Q0T02vx8ZM9XSv9/Yde0jTmmBQufZhPJfYAg2XrrrxWWaZgq1rr8nU8Hv710BQ1dhoP8rtY7YUdpGej2Qza/cw==", "dev": true, "dependencies": { - "@clack/core": "0.4.1", + "@clack/core": "0.4.2", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } @@ -11004,9 +11004,9 @@ "dev": true }, "@capacitor/core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.1.0.tgz", - "integrity": "sha512-I0a4C8gux5sx+HDamJjCiWHEWRdJU3hejwURFOSwJjUmAMkfkrm4hOsI0dgd+S0eCkKKKYKz9WNm7DAIvhm2zw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.2.0.tgz", + "integrity": "sha512-2zCnA6RJeZ9ec4470o8QMZEQTWpekw9FNoqm5TLc10jeCrhvHVI8MPgxdZVc3mOdFlyieYu4AS1fNxSqbS57Pw==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -11034,9 +11034,9 @@ "requires": {} }, "@clack/core": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.4.1.tgz", - "integrity": "sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.4.2.tgz", + "integrity": "sha512-NYQfcEy8MWIxrT5Fj8nIVchfRFA26yYKJcvBS7WlUIlw2OmQOY9DhGGXMovyI5J5PpxrCPGkgUi207EBrjpBvg==", "dev": true, "requires": { "picocolors": "^1.0.0", @@ -11044,12 +11044,12 @@ } }, "@clack/prompts": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.10.0.tgz", - "integrity": "sha512-H3rCl6CwW1NdQt9rE3n373t7o5cthPv7yUoxF2ytZvyvlJv89C5RYMJu83Hed8ODgys5vpBU0GKxIRG83jd8NQ==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.10.1.tgz", + "integrity": "sha512-Q0T02vx8ZM9XSv9/Yde0jTmmBQufZhPJfYAg2XrrrxWWaZgq1rr8nU8Hv710BQ1dhoP8rtY7YUdpGej2Qza/cw==", "dev": true, "requires": { - "@clack/core": "0.4.1", + "@clack/core": "0.4.2", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } diff --git a/core/package.json b/core/package.json index fb54e386129..575b4ddaeb2 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/core", - "version": "8.5.4", + "version": "8.5.5", "description": "Base components for Ionic", "keywords": [ "ionic", diff --git a/core/src/components/accordion-group/accordion-group.tsx b/core/src/components/accordion-group/accordion-group.tsx index b84d75b67a5..ba5b110c8b6 100644 --- a/core/src/components/accordion-group/accordion-group.tsx +++ b/core/src/components/accordion-group/accordion-group.tsx @@ -87,7 +87,7 @@ export class AccordionGroup implements ComponentInterface { * Custom behavior: ['a', 'b'] */ printIonWarning( - `ion-accordion-group was passed an array of values, but multiple="false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". + `[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". Value Passed: [${value.map((v) => `'${v}'`).join(', ')}] `, diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index dc598fbdf0a..c126d9b0d2a 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -5,6 +5,7 @@ import type { Gesture } from '@utils/gesture'; import { createButtonActiveGesture } from '@utils/gesture/button-active'; import { raf } from '@utils/helpers'; import { createLockController } from '@utils/lock-controller'; +import { printIonWarning } from '@utils/logging'; import { createDelegateController, createTriggerController, @@ -318,8 +319,8 @@ export class Alert implements ComponentInterface, OverlayInterface { // checkboxes and inputs are all accepted, but they cannot be mixed. const inputTypes = new Set(inputs.map((i) => i.type)); if (inputTypes.has('checkbox') && inputTypes.has('radio')) { - console.warn( - `Alert cannot mix input types: ${Array.from(inputTypes.values()).join( + printIonWarning( + `[ion-alert] - Alert cannot mix input types: ${Array.from(inputTypes.values()).join( '/' )}. Please see alert docs for more info.` ); diff --git a/core/src/components/app/app.tsx b/core/src/components/app/app.tsx index fadfed3f04b..4f4919667f4 100644 --- a/core/src/components/app/app.tsx +++ b/core/src/components/app/app.tsx @@ -46,7 +46,7 @@ export class App implements ComponentInterface { */ if (shouldUseCloseWatcher()) { printIonWarning( - 'experimentalCloseWatcher was set to `true`, but hardwareBackButton was set to `false`. Both config options must be `true` for the Close Watcher API to be used.' + '[ion-app] - experimentalCloseWatcher was set to `true`, but hardwareBackButton was set to `false`. Both config options must be `true` for the Close Watcher API to be used.' ); } diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index b3dda48877e..4e63b8413ea 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -235,7 +235,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * element with that id is not a form element. */ printIonWarning( - `Form with selector: "#${form}" could not be found. Verify that the id is attached to a
element.`, + `[ion-button] - Form with selector: "#${form}" could not be found. Verify that the id is attached to a element.`, this.el ); return null; @@ -246,7 +246,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * element with that id could not be found in the DOM. */ printIonWarning( - `Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, + `[ion-button] - Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, this.el ); return null; @@ -260,7 +260,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * as the form attribute. */ printIonWarning( - `The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, + `[ion-button] - The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, this.el ); return null; diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index 0686b112982..5710f52da67 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -169,7 +169,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(logs.length).toBe(1); expect(logs[0]).toContain( - '[Ionic Warning]: Form with selector: "#missingForm" could not be found. Verify that the id is correct and the form is rendered in the DOM.' + '[Ionic Warning]: [ion-button] - Form with selector: "#missingForm" could not be found. Verify that the id is correct and the form is rendered in the DOM.' ); }); @@ -197,7 +197,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(logs.length).toBe(1); expect(logs[0]).toContain( - '[Ionic Warning]: The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.' + '[Ionic Warning]: [ion-button] - The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.' ); }); }); diff --git a/core/src/components/datetime-button/datetime-button.tsx b/core/src/components/datetime-button/datetime-button.tsx index 957a2267f09..af0a77f88e0 100644 --- a/core/src/components/datetime-button/datetime-button.tsx +++ b/core/src/components/datetime-button/datetime-button.tsx @@ -63,7 +63,7 @@ export class DatetimeButton implements ComponentInterface { const { datetime } = this; if (!datetime) { printIonError( - 'An ID associated with an ion-datetime instance is required for ion-datetime-button to function properly.', + '[ion-datetime-button] - An ID associated with an ion-datetime instance is required to function properly.', this.el ); return; @@ -71,7 +71,7 @@ export class DatetimeButton implements ComponentInterface { const datetimeEl = (this.datetimeEl = document.getElementById(datetime) as HTMLIonDatetimeElement | null); if (!datetimeEl) { - printIonError(`No ion-datetime instance found for ID '${datetime}'.`, this.el); + printIonError(`[ion-datetime-button] - No ion-datetime instance found for ID '${datetime}'.`, this.el); return; } @@ -81,7 +81,7 @@ export class DatetimeButton implements ComponentInterface { */ if (datetimeEl.tagName !== 'ION-DATETIME') { printIonError( - `Expected an ion-datetime instance for ID '${datetime}' but received '${datetimeEl.tagName.toLowerCase()}' instead.`, + `[ion-datetime-button] - Expected an ion-datetime instance for ID '${datetime}' but received '${datetimeEl.tagName.toLowerCase()}' instead.`, datetimeEl ); return; @@ -245,7 +245,7 @@ export class DatetimeButton implements ComponentInterface { try { headerText = titleSelectedDatesFormatter(parsedValues); } catch (e) { - printIonError('Exception in provided `titleSelectedDatesFormatter`: ', e); + printIonError('[ion-datetime-button] - Exception in provided `titleSelectedDatesFormatter`:', e); } } this.dateText = headerText; diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index fcdb8bcabe0..b63590c66bb 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -584,7 +584,7 @@ export class Datetime implements ComponentInterface { * Custom behavior: ['a', 'b'] */ printIonWarning( - `ion-datetime was passed an array of values, but multiple="false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". + `[ion-datetime] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". Value Passed: [${value.map((v) => `'${v}'`).join(', ')}] `, @@ -1389,24 +1389,24 @@ export class Datetime implements ComponentInterface { if (multiple) { if (presentation !== 'date') { - printIonWarning('Multiple date selection is only supported for presentation="date".', el); + printIonWarning('[ion-datetime] - Multiple date selection is only supported for presentation="date".', el); } if (preferWheel) { - printIonWarning('Multiple date selection is not supported with preferWheel="true".', el); + printIonWarning('[ion-datetime] - Multiple date selection is not supported with preferWheel="true".', el); } } if (highlightedDates !== undefined) { if (presentation !== 'date' && presentation !== 'date-time' && presentation !== 'time-date') { printIonWarning( - 'The highlightedDates property is only supported with the date, date-time, and time-date presentations.', + '[ion-datetime] - The highlightedDates property is only supported with the date, date-time, and time-date presentations.', el ); } if (preferWheel) { - printIonWarning('The highlightedDates property is not supported with preferWheel="true".', el); + printIonWarning('[ion-datetime] - The highlightedDates property is not supported with preferWheel="true".', el); } } @@ -1668,7 +1668,7 @@ export class Datetime implements ComponentInterface { disabled = !isDateEnabled(convertDataToISO(referenceParts)); } catch (e) { printIonError( - 'Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e ); } @@ -1759,7 +1759,7 @@ export class Datetime implements ComponentInterface { disabled = !isDateEnabled(convertDataToISO(referenceParts)); } catch (e) { printIonError( - 'Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e ); } @@ -2262,7 +2262,7 @@ export class Datetime implements ComponentInterface { isCalDayDisabled = !isDateEnabled(dateIsoString); } catch (e) { printIonError( - 'Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', el, e ); @@ -2483,7 +2483,7 @@ export class Datetime implements ComponentInterface { try { headerText = titleSelectedDatesFormatter(convertDataToISO(activeParts)); } catch (e) { - printIonError('Exception in provided `titleSelectedDatesFormatter`: ', e); + printIonError('[ion-datetime] - Exception in provided `titleSelectedDatesFormatter`:', e); } } } else { diff --git a/core/src/components/datetime/test/basic/datetime.e2e.ts b/core/src/components/datetime/test/basic/datetime.e2e.ts index f96cc64bea9..67812b6d020 100644 --- a/core/src/components/datetime/test/basic/datetime.e2e.ts +++ b/core/src/components/datetime/test/basic/datetime.e2e.ts @@ -687,7 +687,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { expect(logs.length).toBe(1); expect(logs[0]).toContain( - '[Ionic Warning]: Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".' + '[Ionic Warning]: [ion-datetime] - "timeZone" and "timeZoneName" are not supported in "formatOptions".' ); }); @@ -717,7 +717,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { expect(logs.length).toBe(1); expect(logs[0]).toContain( - "[Ionic Warning]: Datetime: The 'date-time' presentation requires either a date or time object (or both) in formatOptions." + "[Ionic Warning]: [ion-datetime] - The 'date-time' presentation requires either a date or time object (or both) in formatOptions." ); }); }); diff --git a/core/src/components/datetime/utils/comparison.ts b/core/src/components/datetime/utils/comparison.ts index 0507b16908a..986d5839222 100644 --- a/core/src/components/datetime/utils/comparison.ts +++ b/core/src/components/datetime/utils/comparison.ts @@ -48,7 +48,7 @@ export const warnIfValueOutOfBounds = ( for (const val of valueArray) { if ((min !== undefined && isBefore(val, min)) || (max !== undefined && isAfter(val, max))) { printIonWarning( - 'The value provided to ion-datetime is out of bounds.\n\n' + + '[ion-datetime] - The value provided to ion-datetime is out of bounds.\n\n' + `Min: ${JSON.stringify(min)}\n` + `Max: ${JSON.stringify(max)}\n` + `Value: ${JSON.stringify(value)}` diff --git a/core/src/components/datetime/utils/parse.ts b/core/src/components/datetime/utils/parse.ts index efda69f655b..ef18892b233 100644 --- a/core/src/components/datetime/utils/parse.ts +++ b/core/src/components/datetime/utils/parse.ts @@ -105,7 +105,9 @@ export function parseDate(val: string | string[] | undefined | null): DatetimePa if (parse === null) { // wasn't able to parse the ISO datetime - printIonWarning(`Unable to parse date string: ${val}. Please provide a valid ISO 8601 datetime string.`); + printIonWarning( + `[ion-datetime] - Unable to parse date string: ${val}. Please provide a valid ISO 8601 datetime string.` + ); return undefined; } diff --git a/core/src/components/datetime/utils/state.ts b/core/src/components/datetime/utils/state.ts index f709d7289a1..80abeb25f53 100644 --- a/core/src/components/datetime/utils/state.ts +++ b/core/src/components/datetime/utils/state.ts @@ -218,7 +218,7 @@ export const getHighlightStyles = ( return highlightedDates(dateIsoString); } catch (e) { printIonError( - 'Exception thrown from provided `highlightedDates` callback. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `highlightedDates` callback. Please check your function and try again.', el, e ); diff --git a/core/src/components/datetime/utils/validate.ts b/core/src/components/datetime/utils/validate.ts index 46c3fe633b8..d11ceeec722 100644 --- a/core/src/components/datetime/utils/validate.ts +++ b/core/src/components/datetime/utils/validate.ts @@ -14,7 +14,7 @@ export const warnIfTimeZoneProvided = (el: HTMLElement, formatOptions?: FormatOp formatOptions?.time?.timeZone || formatOptions?.time?.timeZoneName ) { - printIonWarning('Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".', el); + printIonWarning('[ion-datetime] - "timeZone" and "timeZoneName" are not supported in "formatOptions".', el); } }; @@ -33,19 +33,22 @@ export const checkForPresentationFormatMismatch = ( case 'month': case 'year': if (formatOptions.date === undefined) { - printIonWarning(`Datetime: The '${presentation}' presentation requires a date object in formatOptions.`, el); + printIonWarning( + `[ion-datetime] - The '${presentation}' presentation requires a date object in formatOptions.`, + el + ); } break; case 'time': if (formatOptions.time === undefined) { - printIonWarning(`Datetime: The 'time' presentation requires a time object in formatOptions.`, el); + printIonWarning(`[ion-datetime] - The 'time' presentation requires a time object in formatOptions.`, el); } break; case 'date-time': case 'time-date': if (formatOptions.date === undefined && formatOptions.time === undefined) { printIonWarning( - `Datetime: The '${presentation}' presentation requires either a date or time object (or both) in formatOptions.`, + `[ion-datetime] - The '${presentation}' presentation requires either a date or time object (or both) in formatOptions.`, el ); } diff --git a/core/src/components/input-password-toggle/input-password-toggle.tsx b/core/src/components/input-password-toggle/input-password-toggle.tsx index a783cfc9af3..44b8e0828a1 100644 --- a/core/src/components/input-password-toggle/input-password-toggle.tsx +++ b/core/src/components/input-password-toggle/input-password-toggle.tsx @@ -59,7 +59,7 @@ export class InputPasswordToggle implements ComponentInterface { onTypeChange(newValue: TextFieldTypes) { if (newValue !== 'text' && newValue !== 'password') { printIonWarning( - `ion-input-password-toggle only supports inputs of type "text" or "password". Input of type "${newValue}" is not compatible.`, + `[ion-input-password-toggle] - Only inputs of type "text" or "password" are supported. Input of type "${newValue}" is not compatible.`, this.el ); @@ -74,7 +74,7 @@ export class InputPasswordToggle implements ComponentInterface { if (!inputElRef) { printIonWarning( - 'No ancestor ion-input found for ion-input-password-toggle. This component must be slotted inside of an ion-input.', + '[ion-input-password-toggle] - No ancestor ion-input found. This component must be slotted inside of an ion-input.', el ); diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index c70b2bb4009..42bee8e005e 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -50,11 +50,20 @@ export class Input implements ComponentInterface { * Resets when the input loses focus. */ private didInputClearOnEdit = false; + /** * The value of the input when the input is focused. */ private focusedValue?: string | number | null; + /** + * The `hasFocus` state ensures the focus class is + * added regardless of how the element is focused. + * The `ion-focused` class only applies when focused + * via tabbing, not by clicking. + * The `has-focus` logic was added to ensure the class + * is applied in both cases. + */ @State() hasFocus = false; @Element() el!: HTMLIonInputElement; diff --git a/core/src/components/input/input.utils.ts b/core/src/components/input/input.utils.ts index d6812be12d8..1567a3b52cd 100644 --- a/core/src/components/input/input.utils.ts +++ b/core/src/components/input/input.utils.ts @@ -24,7 +24,7 @@ export const getCounterText = ( try { return counterFormatter(valueLength, maxLength); } catch (e) { - printIonError('Exception in provided `counterFormatter`.', e); + printIonError('[ion-input] - Exception in provided `counterFormatter`:', e); return defaultCounterText; } }; diff --git a/core/src/components/item-sliding/item-sliding.tsx b/core/src/components/item-sliding/item-sliding.tsx index a250b5e9bfe..ef9eaae327c 100644 --- a/core/src/components/item-sliding/item-sliding.tsx +++ b/core/src/components/item-sliding/item-sliding.tsx @@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; import { findClosestIonContent, disableContentScrollY, resetContentScrollY } from '@utils/content'; import { isEndSide } from '@utils/helpers'; +import { printIonWarning } from '@utils/logging'; import { watchForOptions } from '@utils/watch-options'; import { getIonMode } from '../../global/ionic-global'; @@ -343,7 +344,7 @@ export class ItemSliding implements ComponentInterface { case ItemSide.None: return; default: - console.warn('invalid ItemSideFlags value', this.sides); + printIonWarning('[ion-item-sliding] - invalid ItemSideFlags value', this.sides); break; } diff --git a/core/src/components/menu/menu.tsx b/core/src/components/menu/menu.tsx index 8a70dd8fb1d..0fee7bda169 100644 --- a/core/src/components/menu/menu.tsx +++ b/core/src/components/menu/menu.tsx @@ -6,6 +6,7 @@ import { GESTURE_CONTROLLER } from '@utils/gesture'; import { shouldUseCloseWatcher } from '@utils/hardware-back-button'; import type { Attributes } from '@utils/helpers'; import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers'; +import { printIonError } from '@utils/logging'; import { menuController } from '@utils/menu-controller'; import { BACKDROP, GESTURE, getPresentedOverlay } from '@utils/overlays'; import { isPlatform } from '@utils/platform'; @@ -215,13 +216,13 @@ export class Menu implements ComponentInterface, MenuI { const content = this.contentId !== undefined ? document.getElementById(this.contentId) : null; if (content === null) { - console.error('Menu: must have a "content" element to listen for drag events on.'); + printIonError('[ion-menu] - Must have a "content" element to listen for drag events on.'); return; } if (this.el.contains(content)) { - console.error( - `Menu: "contentId" should refer to the main view's ion-content, not the ion-content inside of the ion-menu.` + printIonError( + `[ion-menu] - The "contentId" should refer to the main view's ion-content, not the ion-content inside of the ion-menu.` ); } diff --git a/core/src/components/modal/modal-interface.ts b/core/src/components/modal/modal-interface.ts index 0b3ce7f901d..7cc22f842bb 100644 --- a/core/src/components/modal/modal-interface.ts +++ b/core/src/components/modal/modal-interface.ts @@ -25,6 +25,7 @@ export interface ModalOptions { backdropBreakpoint?: number; handle?: boolean; handleBehavior?: ModalHandleBehavior; + expandToScroll?: boolean; } export interface ModalAnimationOptions { diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index c6f15bdce83..2ec55fca020 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -427,7 +427,7 @@ export class Modal implements ComponentInterface, OverlayInterface { } if (breakpoints !== undefined && initialBreakpoint !== undefined && !breakpoints.includes(initialBreakpoint)) { - printIonWarning('Your breakpoints array must include the initialBreakpoint value.'); + printIonWarning('[ion-modal] - Your breakpoints array must include the initialBreakpoint value.'); } if (!this.htmlAttributes?.id) { @@ -847,12 +847,12 @@ export class Modal implements ComponentInterface, OverlayInterface { @Method() async setCurrentBreakpoint(breakpoint: number): Promise { if (!this.isSheetModal) { - printIonWarning('setCurrentBreakpoint is only supported on sheet modals.'); + printIonWarning('[ion-modal] - setCurrentBreakpoint is only supported on sheet modals.'); return; } if (!this.breakpoints!.includes(breakpoint)) { printIonWarning( - `Attempted to set invalid breakpoint value ${breakpoint}. Please double check that the breakpoint value is part of your defined breakpoints.` + `[ion-modal] - Attempted to set invalid breakpoint value ${breakpoint}. Please double check that the breakpoint value is part of your defined breakpoints.` ); return; } diff --git a/core/src/components/modal/test/basic/modal.e2e.ts b/core/src/components/modal/test/basic/modal.e2e.ts index f431b6fe46f..325c4b3fbb5 100644 --- a/core/src/components/modal/test/basic/modal.e2e.ts +++ b/core/src/components/modal/test/basic/modal.e2e.ts @@ -145,7 +145,9 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await modal.evaluate((el: HTMLIonModalElement) => el.setCurrentBreakpoint(0.5)); expect(warnings.length).toBe(1); - expect(warnings[0]).toBe('[Ionic Warning]: setCurrentBreakpoint is only supported on sheet modals.'); + expect(warnings[0]).toBe( + '[Ionic Warning]: [ion-modal] - setCurrentBreakpoint is only supported on sheet modals.' + ); }); test('it should return undefined when getting the breakpoint on a non-sheet modal', async ({ page }) => { diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts b/core/src/components/modal/test/sheet/modal.e2e.ts index 6afb4cd67e1..469040602fb 100644 --- a/core/src/components/modal/test/sheet/modal.e2e.ts +++ b/core/src/components/modal/test/sheet/modal.e2e.ts @@ -96,7 +96,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => test('it should warn when setting an invalid breakpoint', async () => { expect(warnings.length).toBe(1); expect(warnings[0]).toBe( - '[Ionic Warning]: Attempted to set invalid breakpoint value 0.01. Please double check that the breakpoint value is part of your defined breakpoints.' + '[Ionic Warning]: [ion-modal] - Attempted to set invalid breakpoint value 0.01. Please double check that the breakpoint value is part of your defined breakpoints.' ); }); }); diff --git a/core/src/components/nav/nav.tsx b/core/src/components/nav/nav.tsx index cc71af4f6c5..3ecd39c2aa6 100644 --- a/core/src/components/nav/nav.tsx +++ b/core/src/components/nav/nav.tsx @@ -97,7 +97,7 @@ export class Nav implements NavOutlet { this.setRoot(this.root, this.rootParams); } } else if (isDev) { - printIonWarning(' does not support a root attribute when using ion-router.', this.el); + printIonWarning('[ion-nav] - A root attribute is not supported when using ion-router.', this.el); } } @@ -820,8 +820,8 @@ export class Nav implements NavOutlet { const finalNumViews = this.views.length + (insertViews?.length ?? 0) - (removeCount ?? 0); assert(finalNumViews >= 0, 'final balance can not be negative'); if (finalNumViews === 0) { - console.warn( - `You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`, + printIonWarning( + `[ion-nav] - You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`, this, this.el ); diff --git a/core/src/components/picker-legacy/picker.tsx b/core/src/components/picker-legacy/picker.tsx index 6b267823928..fe67cf83fec 100644 --- a/core/src/components/picker-legacy/picker.tsx +++ b/core/src/components/picker-legacy/picker.tsx @@ -206,7 +206,7 @@ export class Picker implements ComponentInterface, OverlayInterface { componentDidLoad() { printIonWarning( - 'ion-picker-legacy and ion-picker-legacy-column have been deprecated in favor of new versions of the ion-picker and ion-picker-column components. These new components display inline with your page content allowing for more presentation flexibility than before.', + '[ion-picker-legacy] - ion-picker-legacy and ion-picker-legacy-column have been deprecated in favor of new versions of the ion-picker and ion-picker-column components. These new components display inline with your page content allowing for more presentation flexibility than before.', this.el ); diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index d1205ab2f6a..61d525dff9a 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -648,7 +648,7 @@ export class Popover implements ComponentInterface, PopoverInterface { const triggerEl = (this.triggerEl = trigger !== undefined ? document.getElementById(trigger) : null); if (!triggerEl) { printIonWarning( - `A trigger element with the ID "${trigger}" was not found in the DOM. The trigger element must be in the DOM when the "trigger" property is set on ion-popover.`, + `[ion-popover] - A trigger element with the ID "${trigger}" was not found in the DOM. The trigger element must be in the DOM when the "trigger" property is set on ion-popover.`, this.el ); return; diff --git a/core/src/components/range/range.tsx b/core/src/components/range/range.tsx index 478b2a57e2e..eac3dd3ebcd 100644 --- a/core/src/components/range/range.tsx +++ b/core/src/components/range/range.tsx @@ -183,13 +183,13 @@ export class Range implements ComponentInterface { if (activeBarStart !== undefined) { if (activeBarStart > this.max) { printIonWarning( - `Range: The value of activeBarStart (${activeBarStart}) is greater than the max (${this.max}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, + `[ion-range] - The value of activeBarStart (${activeBarStart}) is greater than the max (${this.max}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, this.el ); this.activeBarStart = this.max; } else if (activeBarStart < this.min) { printIonWarning( - `Range: The value of activeBarStart (${activeBarStart}) is less than the min (${this.min}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, + `[ion-range] - The value of activeBarStart (${activeBarStart}) is less than the min (${this.min}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, this.el ); this.activeBarStart = this.min; diff --git a/core/src/components/refresher/refresher.tsx b/core/src/components/refresher/refresher.tsx index 25a121a3d0c..8d016873086 100644 --- a/core/src/components/refresher/refresher.tsx +++ b/core/src/components/refresher/refresher.tsx @@ -8,6 +8,7 @@ import { printIonContentErrorMsg, } from '@utils/content'; import { clamp, componentOnReady, getElementRoot, raf, transitionEndAsync } from '@utils/helpers'; +import { printIonError } from '@utils/logging'; import { ImpactStyle, hapticImpact } from '@utils/native/haptic'; import { getIonMode } from '../../global/ionic-global'; @@ -452,7 +453,7 @@ export class Refresher implements ComponentInterface { async connectedCallback() { if (this.el.getAttribute('slot') !== 'fixed') { - console.error('Make sure you use: '); + printIonError('[ion-refresher] - Make sure you use: '); return; } const contentEl = this.el.closest(ION_CONTENT_ELEMENT_SELECTOR); diff --git a/core/src/components/router-outlet/router-outlet.tsx b/core/src/components/router-outlet/router-outlet.tsx index d25234f814e..5187bddb6ed 100644 --- a/core/src/components/router-outlet/router-outlet.tsx +++ b/core/src/components/router-outlet/router-outlet.tsx @@ -4,6 +4,7 @@ import { getTimeGivenProgression } from '@utils/animation/cubic-bezier'; import { attachComponent, detachComponent } from '@utils/framework-delegate'; import { shallowEqualStringMap, hasLazyBuild } from '@utils/helpers'; import { createLockController } from '@utils/lock-controller'; +import { printIonError } from '@utils/logging'; import { transition } from '@utils/transition'; import { config } from '../../global/config'; @@ -146,7 +147,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet { try { changed = await this.transition(enteringEl, leavingEl, opts); } catch (e) { - console.error(e); + printIonError('[ion-router-outlet] - Exception in commit:', e); } unlock(); return changed; diff --git a/core/src/components/router/router.tsx b/core/src/components/router/router.tsx index 3f30e6f9750..c68c9c4701c 100644 --- a/core/src/components/router/router.tsx +++ b/core/src/components/router/router.tsx @@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Listen, Method, Prop } from '@stencil/core'; import type { BackButtonEvent } from '@utils/hardware-back-button'; import { debounce } from '@utils/helpers'; +import { printIonError, printIonWarning } from '@utils/logging'; import type { AnimationBuilder } from '../../interface'; import type { NavigationHookResult } from '../route/route-interface'; @@ -166,15 +167,15 @@ export class Router implements ComponentInterface { @Method() async navChanged(direction: RouterDirection): Promise { if (this.busy) { - console.warn('[ion-router] router is busy, navChanged was cancelled'); + printIonWarning('[ion-router] - Router is busy, navChanged was cancelled.'); return false; } const { ids, outlet } = await readNavState(window.document.body); const routes = readRoutes(this.el); const chain = findChainForIDs(ids, routes); if (!chain) { - console.warn( - '[ion-router] no matching URL for ', + printIonWarning( + '[ion-router] - No matching URL for', ids.map((i) => i.id) ); return false; @@ -182,7 +183,7 @@ export class Router implements ComponentInterface { const segments = chainToSegments(chain); if (!segments) { - console.warn('[ion-router] router could not match path because some required param is missing'); + printIonWarning('[ion-router] - Router could not match path because some required param is missing.'); return false; } @@ -232,7 +233,7 @@ export class Router implements ComponentInterface { animation?: AnimationBuilder ): Promise { if (!segments) { - console.error('[ion-router] URL is not part of the routing set'); + printIonError('[ion-router] - URL is not part of the routing set.'); return false; } @@ -253,7 +254,7 @@ export class Router implements ComponentInterface { const routes = readRoutes(this.el); const chain = findChainForSegments(segments, routes); if (!chain) { - console.error('[ion-router] the path does not match any route'); + printIonError('[ion-router] - The path does not match any route.'); return false; } @@ -275,7 +276,7 @@ export class Router implements ComponentInterface { try { changed = await this.writeNavState(node, chain, direction, segments, redirectFrom, index, animation); } catch (e) { - console.error(e); + printIonError('[ion-router] - Exception in safeWriteNavState:', e); } unlock(); return changed; @@ -338,7 +339,7 @@ export class Router implements ComponentInterface { animation?: AnimationBuilder ): Promise { if (this.busy) { - console.warn('[ion-router] router is busy, transition was cancelled'); + printIonWarning('[ion-router] - Router is busy, transition was cancelled.'); return false; } this.busy = true; diff --git a/core/src/components/router/utils/dom.ts b/core/src/components/router/utils/dom.ts index 95bc6fec53f..c10b34d0840 100644 --- a/core/src/components/router/utils/dom.ts +++ b/core/src/components/router/utils/dom.ts @@ -1,4 +1,5 @@ import { componentOnReady } from '@utils/helpers'; +import { printIonError } from '@utils/logging'; import type { AnimationBuilder } from '../../../interface'; @@ -51,7 +52,7 @@ export const writeNavState = async ( } return changed; } catch (e) { - console.error(e); + printIonError('[ion-router] - Exception in writeNavState:', e); return false; } }; diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index fb940c9d99b..115040298d3 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -3,6 +3,7 @@ import { Component, Element, Host, Prop, Method, State, Watch, forceUpdate, h } import type { ButtonInterface } from '@utils/element-interface'; import type { Attributes } from '@utils/helpers'; import { addEventListener, removeEventListener, inheritAttributes } from '@utils/helpers'; +import { printIonError, printIonWarning } from '@utils/logging'; import { hostContext } from '@utils/theme'; import { getIonMode } from '../../global/ionic-global'; @@ -75,7 +76,9 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { // Prevent buttons from being disabled when associated with segment content if (this.contentId && this.disabled) { - console.warn(`Segment Button: Segment buttons cannot be disabled when associated with an .`); + printIonWarning( + `[ion-segment-button] - Segment buttons cannot be disabled when associated with an .` + ); this.disabled = false; } } @@ -102,13 +105,15 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { // If no associated Segment Content exists, log an error and return if (!segmentContent) { - console.error(`Segment Button: Unable to find Segment Content with id="${this.contentId}".`); + printIonError(`[ion-segment-button] - Unable to find Segment Content with id="${this.contentId}".`); return; } // Ensure the found element is a valid ION-SEGMENT-CONTENT if (segmentContent.tagName !== 'ION-SEGMENT-CONTENT') { - console.error(`Segment Button: Element with id="${this.contentId}" is not an element.`); + printIonError( + `[ion-segment-button] - Element with id="${this.contentId}" is not an element.` + ); return; } } diff --git a/core/src/components/select/select.md.outline.scss b/core/src/components/select/select.md.outline.scss index e1f4d54395e..ca801e265bb 100644 --- a/core/src/components/select/select.md.outline.scss +++ b/core/src/components/select/select.md.outline.scss @@ -43,7 +43,7 @@ * the select is focused. */ :host(.select-fill-outline.select-expanded), -:host(.select-fill-outline.ion-focused) { +:host(.select-fill-outline.has-focus) { --border-width: var(--highlight-height); --border-color: var(--highlight-color); } @@ -240,3 +240,18 @@ :host(.label-floating.select-fill-outline) .select-outline-notch { border-top: none; } + +// Select Icon +// ---------------------------------------------------------------- + +/** + * When the select has an outline fill and + * in an item, then the icon should + * take on the highlight color. + */ +:host(.in-item.select-expanded.select-fill-outline) .select-wrapper .select-icon, +:host(.in-item.has-focus.select-fill-outline) .select-wrapper .select-icon, +:host(.in-item.has-focus.ion-valid.select-fill-outline) .select-wrapper .select-icon, +:host(.in-item.ion-touched.ion-invalid.select-fill-outline) .select-wrapper .select-icon { + color: var(--highlight-color); +} diff --git a/core/src/components/select/select.md.scss b/core/src/components/select/select.md.scss index 5d556e11f95..220c263c7f5 100644 --- a/core/src/components/select/select.md.scss +++ b/core/src/components/select/select.md.scss @@ -12,14 +12,6 @@ --highlight-height: 2px; } -.select-icon { - width: $select-md-icon-size; - - transition: transform .15s cubic-bezier(.4, 0, .2, 1); - - color: #{$text-color-step-500}; -} - // Select Label // ---------------------------------------------------------------- @@ -29,9 +21,9 @@ * only apply to floating or stacked labels. */ :host(.select-label-placement-floating.select-expanded) .label-text-wrapper, -:host(.select-label-placement-floating.ion-focused) .label-text-wrapper, +:host(.select-label-placement-floating.has-focus) .label-text-wrapper, :host(.select-label-placement-stacked.select-expanded) .label-text-wrapper, -:host(.select-label-placement-stacked.ion-focused) .label-text-wrapper { +:host(.select-label-placement-stacked.has-focus) .label-text-wrapper { color: var(--highlight-color); } @@ -61,7 +53,7 @@ } :host(.select-expanded) .select-highlight, -:host(.ion-focused) .select-highlight { +:host(.has-focus) .select-highlight { transform: scale(1); } @@ -77,6 +69,14 @@ // Select Icon // ---------------------------------------------------------------- +.select-icon { + width: $select-md-icon-size; + + transition: transform .15s cubic-bezier(.4, 0, .2, 1); + + color: #{$select-md-icon-color}; +} + /** * This rotates the chevron icon * when the select is activated. @@ -86,6 +86,18 @@ @include transform(rotate(180deg)); } +/** + * When the select has no fill and + * in an item, then the icon should + * be the same color as the text color. + */ +:host(.in-item.select-expanded) .select-wrapper .select-icon, +:host(.in-item.has-focus) .select-wrapper .select-icon, +:host(.in-item.has-focus.ion-valid) .select-wrapper .select-icon, +:host(.in-item.ion-touched.ion-invalid) .select-wrapper .select-icon { + color: #{$select-md-icon-color}; +} + /** * When the select is focused the icon should * take on the highlight color. @@ -95,7 +107,7 @@ :host(.select-expanded) .select-wrapper .select-icon, :host(.has-focus.ion-valid) .select-wrapper .select-icon, :host(.ion-touched.ion-invalid) .select-wrapper .select-icon, -:host(.ion-focused) .select-wrapper .select-icon { +:host(.has-focus) .select-wrapper .select-icon { color: var(--highlight-color); } diff --git a/core/src/components/select/select.md.solid.scss b/core/src/components/select/select.md.solid.scss index 321065ea803..12a42d2222f 100644 --- a/core/src/components/select/select.md.solid.scss +++ b/core/src/components/select/select.md.solid.scss @@ -27,6 +27,7 @@ * If the select has a validity state, the * border should reflect that as a color. */ +:host(.select-expanded.select-fill-solid.ion-valid), :host(.has-focus.select-fill-solid.ion-valid), :host(.select-fill-solid.ion-touched.ion-invalid) { --border-color: var(--highlight-color); @@ -56,9 +57,9 @@ * much darker on focus. */ :host(.select-fill-solid.select-expanded), -:host(.select-fill-solid.ion-focused) { +:host(.select-fill-solid.has-focus) { --background: #{$background-color-step-150}; - --border-color: #{$background-color-step-750}; + --border-color: var(--highlight-color); } :host(.select-fill-solid) .select-wrapper { @@ -79,3 +80,18 @@ */ max-width: calc(100% / #{$form-control-label-stacked-scale}); } + +// Select Icon +// ---------------------------------------------------------------- + +/** + * When the select has a solid fill and + * in an item, then the icon should + * take on the highlight color. + */ +:host(.in-item.select-expanded.select-fill-solid) .select-wrapper .select-icon, +:host(.in-item.has-focus.select-fill-solid) .select-wrapper .select-icon, +:host(.in-item.has-focus.ion-valid.select-fill-solid) .select-wrapper .select-icon, +:host(.in-item.ion-touched.ion-invalid.select-fill-solid) .select-wrapper .select-icon { + color: var(--highlight-color); +} diff --git a/core/src/components/select/select.md.vars.scss b/core/src/components/select/select.md.vars.scss index a8540da58a7..d3d1965b693 100644 --- a/core/src/components/select/select.md.vars.scss +++ b/core/src/components/select/select.md.vars.scss @@ -6,6 +6,10 @@ /// @prop - Size of the select icon $select-md-icon-size: dynamic-font(13px); + +/// @prop - Color of the select icon +$select-md-icon-color: $text-color-step-500; + /// @prop - The amount of whitespace to display on either side of the floating label $select-md-floating-label-padding: 4px; diff --git a/core/src/components/select/select.scss b/core/src/components/select/select.scss index cd0a7707aa8..157b2f5e358 100644 --- a/core/src/components/select/select.scss +++ b/core/src/components/select/select.scss @@ -88,7 +88,7 @@ pointer-events: none; } -:host(.ion-focused) button { +:host(.has-focus) button { border: 2px solid #5e9ed6; } @@ -308,7 +308,9 @@ button { * highlight when the select is blurred. */ :host(.has-focus.ion-valid), -:host(.ion-touched.ion-invalid) { +:host(.select-expanded.ion-valid), +:host(.ion-touched.ion-invalid), +:host(.select-expanded.ion-touched.ion-invalid) { --border-color: var(--highlight-color); } @@ -320,7 +322,7 @@ button { * present on the select. Otherwise the helper text should * be shown. */ - .select-bottom .error-text { +.select-bottom .error-text { display: none; color: var(--highlight-color-invalid); @@ -597,7 +599,7 @@ button { * :host(.label-floating.select-label-placement-floating) .native-wrapper .select-placeholder */ :host(.select-expanded.select-label-placement-floating) .native-wrapper .select-placeholder, -:host(.ion-focused.select-label-placement-floating) .native-wrapper .select-placeholder, +:host(.has-focus.select-label-placement-floating) .native-wrapper .select-placeholder, :host(.has-value.select-label-placement-floating) .native-wrapper .select-placeholder { opacity: 1; } diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx index 0645cd5e56b..7fc456092db 100644 --- a/core/src/components/select/select.tsx +++ b/core/src/components/select/select.tsx @@ -4,6 +4,7 @@ import type { NotchController } from '@utils/forms'; import { compareOptions, createNotchController, isOptionSelected } from '@utils/forms'; import { focusVisibleElement, renderHiddenInput, inheritAttributes } from '@utils/helpers'; import type { Attributes } from '@utils/helpers'; +import { printIonWarning } from '@utils/logging'; import { actionSheetController, alertController, popoverController, modalController } from '@utils/overlays'; import type { OverlaySelect } from '@utils/overlays-interface'; import { isRTL } from '@utils/rtl'; @@ -70,6 +71,16 @@ export class Select implements ComponentInterface { @State() isExpanded = false; + /** + * The `hasFocus` state ensures the focus class is + * added regardless of how the element is focused. + * The `ion-focused` class only applies when focused + * via tabbing, not by clicking. + * The `has-focus` logic was added to ensure the class + * is applied in both cases. + */ + @State() hasFocus = false; + /** * The text to display on the cancel button. */ @@ -426,15 +437,15 @@ export class Select implements ComponentInterface { private createOverlay(ev?: UIEvent): Promise { let selectInterface = this.interface; if (selectInterface === 'action-sheet' && this.multiple) { - console.warn( - `Select interface cannot be "${selectInterface}" with a multi-value select. Using the "alert" interface instead.` + printIonWarning( + `[ion-select] - Interface cannot be "${selectInterface}" with a multi-value select. Using the "alert" interface instead.` ); selectInterface = 'alert'; } if (selectInterface === 'popover' && !ev) { - console.warn( - `Select interface cannot be a "${selectInterface}" without passing an event. Using the "alert" interface instead.` + printIonWarning( + `[ion-select] - Interface cannot be a "${selectInterface}" without passing an event. Using the "alert" interface instead.` ); selectInterface = 'alert'; } @@ -851,10 +862,14 @@ export class Select implements ComponentInterface { }; private onFocus = () => { + this.hasFocus = true; + this.ionFocus.emit(); }; private onBlur = () => { + this.hasFocus = false; + this.ionBlur.emit(); }; @@ -1089,8 +1104,20 @@ export class Select implements ComponentInterface { } render() { - const { disabled, el, isExpanded, expandedIcon, labelPlacement, justify, placeholder, fill, shape, name, value } = - this; + const { + disabled, + el, + isExpanded, + expandedIcon, + labelPlacement, + justify, + placeholder, + fill, + shape, + name, + value, + hasFocus, + } = this; const mode = getIonMode(this); const hasFloatingOrStackedLabel = labelPlacement === 'floating' || labelPlacement === 'stacked'; const justifyEnabled = !hasFloatingOrStackedLabel && justify !== undefined; @@ -1136,6 +1163,8 @@ export class Select implements ComponentInterface { 'has-value': hasValue, 'label-floating': labelShouldFloat, 'has-placeholder': placeholder !== undefined, + 'has-focus': hasFocus, + // TODO(FW-6451): Remove `ion-focusable` class in favor of `has-focus`. 'ion-focusable': true, [`select-${rtl}`]: true, [`select-fill-${fill}`]: fill !== undefined, diff --git a/core/src/components/select/test/basic/select.e2e.ts b/core/src/components/select/test/basic/select.e2e.ts index c04899fe92c..d5a9c3d220f 100644 --- a/core/src/components/select/test/basic/select.e2e.ts +++ b/core/src/components/select/test/basic/select.e2e.ts @@ -324,3 +324,49 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { }); }); }); + +/** + * focus has a consistent behavior across modes + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('select: focus'), () => { + test('should have the focus class when tabbing', async ({ page, pageUtils }) => { + await page.setContent( + ` + + Apple + + `, + config + ); + + const select = page.locator('ion-select'); + + await pageUtils.pressKeys('Tab'); + await expect(select).toHaveClass(/has-focus/); + }); + + test('should have the focus class after clicking to close', async ({ page }) => { + await page.setContent( + ` + + Apple + + `, + config + ); + + const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent'); + const select = page.locator('ion-select'); + const alert = page.locator('ion-alert'); + const confirmButton = alert.locator('.alert-button:not(.alert-button-role-cancel)'); + + await select.click(); + await ionAlertDidPresent.next(); + + await confirmButton.click(); + + await expect(select).toHaveClass(/has-focus/); + }); + }); +}); diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png index d4cc64abbc7..1fc69f2a2c7 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png index a85cc6a5d4a..09dbd660ee0 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Safari-linux.png index a053819fd52..61ff0d1fc65 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png index f681df3c16b..b8f081c867d 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png index 9a6b6a34ed4..e13afdfc587 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png index de421cc6938..e6e9c4fdb8c 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/color/select.e2e.ts b/core/src/components/select/test/color/select.e2e.ts index 9ba5e0e2ed0..d28a3f08e88 100644 --- a/core/src/components/select/test/color/select.e2e.ts +++ b/core/src/components/select/test/color/select.e2e.ts @@ -7,7 +7,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should set label and highlight color on expand', async ({ page }) => { await page.setContent( ` - + Apple `, @@ -22,7 +22,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should set label and highlight color on expand', async ({ page }) => { await page.setContent( ` - + Apple `, @@ -37,7 +37,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should set label and highlight color on expand', async ({ page }) => { await page.setContent( ` - + Apple `, diff --git a/core/src/components/select/test/highlight/index.html b/core/src/components/select/test/highlight/index.html index 9d206b804f6..fc623e6394b 100644 --- a/core/src/components/select/test/highlight/index.html +++ b/core/src/components/select/test/highlight/index.html @@ -56,14 +56,14 @@

No Fill, Default

Focus

- + Apple

Valid, Focus

- + Apple
@@ -80,14 +80,14 @@

No Fill, Floating

Focus

- + Apple

Valid, Focus

- + Apple
@@ -104,14 +104,14 @@

No Fill, Stacked

Focus

- + Apple

Valid, Focus

- + Apple
@@ -128,14 +128,14 @@

No Fill, Custom

Focus

- + Apple

Valid, Focus

- + Apple
@@ -152,14 +152,14 @@

Solid, Default

Focus

- + Apple

Valid, Focus

- + Apple
@@ -176,14 +176,14 @@

Solid, Floating

Focus

- + Apple

Valid, Focus

- + Apple
@@ -200,14 +200,14 @@

Solid, Stacked

Focus

- + Apple

Valid, Focus

- + Apple
@@ -224,20 +224,14 @@

Solid, Custom

Focus

- + Apple

Valid, Focus

- + Apple
@@ -254,14 +248,14 @@

Outline, Default

Focus

- + Apple

Valid, Focus

- + Apple
@@ -278,14 +272,14 @@

Outline, Floating

Focus

- + Apple

Valid, Focus

- + Apple
@@ -302,14 +296,14 @@

Outline, Stacked

Focus

- + Apple

Valid, Focus

- + Apple
@@ -330,7 +324,7 @@

Focus

fill="outline" label-placement="start" value="hi@ionic.io" - class="custom ion-focused" + class="custom has-focus" label="Fruit" > Apple @@ -339,7 +333,7 @@

Focus

Valid, Focus

- + Apple
diff --git a/core/src/components/select/test/highlight/select.e2e.ts b/core/src/components/select/test/highlight/select.e2e.ts index 23fd444c56d..257d4332bd0 100644 --- a/core/src/components/select/test/highlight/select.e2e.ts +++ b/core/src/components/select/test/highlight/select.e2e.ts @@ -7,41 +7,114 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should render valid state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-no-fill-valid`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-no-fill-valid`)); }); test('should render invalid state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
+ `, + config + ); + + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-no-fill-invalid`)); + }); + test('should render invalid with focus state correctly', async ({ page }) => { + await page.setContent( + ` + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-no-fill-invalid`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-no-fill-invalid-focus`)); }); test('should render focused state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-no-fill-focus`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-no-fill-focus`)); }); test('should render custom highlight correctly', async ({ page }) => { await page.setContent( @@ -56,15 +129,15 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
- + Apple - + Apple - + Apple
@@ -80,41 +153,86 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should render valid state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-solid-valid`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-solid-valid`)); }); test('should render invalid state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-solid-invalid`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-solid-invalid`)); }); test('should render focused state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-solid-focus`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-solid-focus`)); }); test('should render custom highlight correctly', async ({ page }) => { await page.setContent( @@ -129,15 +247,15 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
- + Apple - + Apple - + Apple
@@ -153,41 +271,86 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should render valid state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-outline-valid`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-outline-valid`)); }); test('should render invalid state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-outline-invalid`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-outline-invalid`)); }); test('should render focused state correctly', async ({ page }) => { await page.setContent( ` - - Apple - + +
+ + Apple + + + + + Apple + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-outline-focus`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-outline-focus`)); }); test('should render custom highlight correctly', async ({ page }) => { await page.setContent( @@ -202,15 +365,15 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
- + Apple - + Apple - + Apple
@@ -229,39 +392,78 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co test('should render bottom highlight', async ({ page }) => { await page.setContent( ` - + +
+ + + + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-no-fill-highlight`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-no-fill-highlight`)); }); }); test.describe('select: solid', () => { test('should render bottom highlight', async ({ page }) => { await page.setContent( ` - + +
+ + + + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-solid-highlight`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-solid-highlight`)); }); }); test.describe('select: outline', () => { test('should render bottom highlight', async ({ page }) => { await page.setContent( ` - + +
+ + + + + + +
`, config ); - const select = page.locator('ion-select'); - await expect(select).toHaveScreenshot(screenshot(`select-outline-highlight`)); + const container = page.locator('#container'); + await expect(container).toHaveScreenshot(screenshot(`select-outline-highlight`)); }); }); }); diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Chrome-linux.png index c51b4aaf3a2..0d66b484995 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Firefox-linux.png index 2b893c508ce..fe35e86ace5 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Safari-linux.png index 0b21560a844..2fda5ff067a 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-focus-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Chrome-linux.png index a1943b028d7..01f2607f107 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Firefox-linux.png index d4e7f7c98b4..7efaf029f11 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Safari-linux.png index 0d4c6c5157d..21569fcb9f8 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-highlight-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4f64caaa2d9 Binary files /dev/null and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..17374b1cdec Binary files /dev/null and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..6a66fadd792 Binary files /dev/null and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-focus-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Chrome-linux.png index a252b187777..5e2173d3507 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Firefox-linux.png index 108e8a37df9..d5e00ca8d3a 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Safari-linux.png index 0fedf66b3a8..39f2939c4ea 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-invalid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Chrome-linux.png index 2c15c40fb48..a563e022187 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Firefox-linux.png index 5b6d1f853d1..0daf67b642f 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Safari-linux.png index 90ce749e758..054c0601624 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-no-fill-valid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Chrome-linux.png index c93c593b790..4ddc2e8060a 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Firefox-linux.png index b64a1f8e9c4..2ecf480b77c 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Safari-linux.png index dd466202f3a..ff862429c78 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-focus-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Chrome-linux.png index 1fc1d7cfab8..8a6937307f2 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Firefox-linux.png index ec0dd16c8cb..6c0cd64de3a 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Safari-linux.png index d55c4863315..2c987d69ca8 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-highlight-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Chrome-linux.png index fae85803bf3..9af9159021b 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Firefox-linux.png index e7ecdda8fd0..a78ab821854 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Safari-linux.png index 726ae826509..429f23da3bb 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-invalid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Chrome-linux.png index 8d8a3997375..a50344ed3b6 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Firefox-linux.png index ae183193c93..65ff8259d98 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Safari-linux.png index db0ab31a25d..21c81b27e86 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-outline-valid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Chrome-linux.png index a4ce2866094..1f4dab6c1fa 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Firefox-linux.png index 4108e541e2c..ecf4734a1f2 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Safari-linux.png index f4cdcc0aebd..cb41984f265 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-focus-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Chrome-linux.png index 800907d3020..737236497c9 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Firefox-linux.png index 5d0ea24f6c3..1a7559701de 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Safari-linux.png index bfb8867a3d8..6eec7736287 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-highlight-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Chrome-linux.png index a3c7e094204..a340d869936 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Firefox-linux.png index b9061f96f87..004b13c3422 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Safari-linux.png index 2387a9243b5..e2446623ce8 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-invalid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Chrome-linux.png index b1c7f080a75..61021ba07db 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Firefox-linux.png index 334f48308e9..c33caea5d84 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Safari-linux.png index 2b56a8d6091..0597a1ef085 100644 Binary files a/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/highlight/select.e2e.ts-snapshots/select-solid-valid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/split-pane/split-pane.tsx b/core/src/components/split-pane/split-pane.tsx index 7cd33ba580b..e2a02dab7a9 100644 --- a/core/src/components/split-pane/split-pane.tsx +++ b/core/src/components/split-pane/split-pane.tsx @@ -1,5 +1,6 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; +import { printIonWarning } from '@utils/logging'; import { getIonMode } from '../../global/ionic-global'; @@ -154,7 +155,7 @@ export class SplitPane implements ComponentInterface { const isMain = contentId !== undefined && child.id === contentId; if (isMain) { if (foundMain) { - console.warn('split pane cannot have more than one main node'); + printIonWarning('[ion-split-pane] - Cannot have more than one main node.'); return; } else { setPaneClass(child, isMain); @@ -163,7 +164,7 @@ export class SplitPane implements ComponentInterface { } } if (!foundMain) { - console.warn('split pane does not have a specified main node'); + printIonWarning('[ion-split-pane] - Does not have a specified main node.'); } } diff --git a/core/src/components/tab/tab.tsx b/core/src/components/tab/tab.tsx index ce507867fdd..a1f6be57a06 100644 --- a/core/src/components/tab/tab.tsx +++ b/core/src/components/tab/tab.tsx @@ -1,6 +1,7 @@ import type { ComponentInterface } from '@stencil/core'; import { Build, Component, Element, Host, Method, Prop, Watch, h } from '@stencil/core'; import { attachComponent } from '@utils/framework-delegate'; +import { printIonError } from '@utils/logging'; import type { ComponentRef, FrameworkDelegate } from '../../interface'; @@ -33,8 +34,8 @@ export class Tab implements ComponentInterface { async componentWillLoad() { if (Build.isDev) { if (this.component !== undefined && this.el.childElementCount > 0) { - console.error( - 'You can not use a lazy-loaded component in a tab and inlined content at the same time.' + + printIonError( + '[ion-tab] - You can not use a lazy-loaded component in a tab and inlined content at the same time.' + `- Remove the component attribute in: ` + ` or` + `- Remove the embedded content inside the ion-tab: ` @@ -66,7 +67,7 @@ export class Tab implements ComponentInterface { try { return attachComponent(this.delegate, this.el, this.component, ['ion-page']); } catch (e) { - console.error(e); + printIonError('[ion-tab] - Exception in prepareLazyLoaded:', e); } } return Promise.resolve(undefined); diff --git a/core/src/components/tabs/tabs.tsx b/core/src/components/tabs/tabs.tsx index 3eebb46d15b..e02e1aa91bd 100644 --- a/core/src/components/tabs/tabs.tsx +++ b/core/src/components/tabs/tabs.tsx @@ -1,5 +1,6 @@ import type { EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, h } from '@stencil/core'; +import { printIonError } from '@utils/logging'; import type { NavOutlet, RouteID, RouteWrite } from '../router/utils/interface'; import type { TabButtonClickEventDetail } from '../tab-bar/tab-bar-interface'; @@ -210,7 +211,7 @@ const getTab = (tabs: HTMLIonTabElement[], tab: string | HTMLIonTabElement): HTM const tabEl = typeof tab === 'string' ? tabs.find((t) => t.tab === tab) : tab; if (!tabEl) { - console.error(`tab with id: "${tabEl}" does not exist`); + printIonError(`[ion-tabs] - Tab with id: "${tabEl}" does not exist`); } return tabEl; }; diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 7764dfba8b5..afb9e3bf6b6 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -70,6 +70,14 @@ export class Textarea implements ComponentInterface { @Element() el!: HTMLIonTextareaElement; + /** + * The `hasFocus` state ensures the focus class is + * added regardless of how the element is focused. + * The `ion-focused` class only applies when focused + * via tabbing, not by clicking. + * The `has-focus` logic was added to ensure the class + * is applied in both cases. + */ @State() hasFocus = false; /** diff --git a/core/src/components/toast/animations/utils.ts b/core/src/components/toast/animations/utils.ts index d9057dda0d2..278ef2313fd 100644 --- a/core/src/components/toast/animations/utils.ts +++ b/core/src/components/toast/animations/utils.ts @@ -83,7 +83,7 @@ export function getAnimationPosition( function warnIfAnchorIsHidden(positionAnchor: HTMLElement, toast: HTMLElement) { if (positionAnchor.offsetParent === null) { printIonWarning( - 'The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.', + '[ion-toast] - The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.', toast ); } diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index d0064716181..45ec9fc0643 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -4,7 +4,7 @@ import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import type { Gesture } from '@utils/gesture'; import { raf } from '@utils/helpers'; import { createLockController } from '@utils/lock-controller'; -import { printIonWarning } from '@utils/logging'; +import { printIonError, printIonWarning } from '@utils/logging'; import { GESTURE, createDelegateController, @@ -498,7 +498,7 @@ export class Toast implements ComponentInterface, OverlayInterface { } if (position === 'middle' && positionAnchor !== undefined) { - printIonWarning('The positionAnchor property is ignored when using position="middle".', this.el); + printIonWarning('[ion-toast] - The positionAnchor property is ignored when using position="middle".', this.el); return undefined; } @@ -511,7 +511,10 @@ export class Toast implements ComponentInterface, OverlayInterface { */ const foundEl = document.getElementById(positionAnchor); if (foundEl === null) { - printIonWarning(`An anchor element with an ID of "${positionAnchor}" was not found in the DOM.`, el); + printIonWarning( + `[ion-toast] - An anchor element with an ID of "${positionAnchor}" was not found in the DOM.`, + el + ); return undefined; } @@ -522,7 +525,7 @@ export class Toast implements ComponentInterface, OverlayInterface { return positionAnchor; } - printIonWarning('Invalid positionAnchor value:', positionAnchor, el); + printIonWarning('[ion-toast] - Invalid positionAnchor value:', positionAnchor, el); return undefined; } @@ -549,7 +552,7 @@ export class Toast implements ComponentInterface, OverlayInterface { return false; } } catch (e) { - console.error(e); + printIonError('[ion-toast] - Exception in callButtonHandler:', e); } } return true; @@ -705,7 +708,7 @@ export class Toast implements ComponentInterface, OverlayInterface { */ if (layout === 'stacked' && startButtons.length > 0 && endButtons.length > 0) { printIonWarning( - 'This toast is using start and end buttons with the stacked toast layout. We recommend following the best practice of using either start or end buttons with the stacked toast layout.', + '[ion-toast] - This toast is using start and end buttons with the stacked toast layout. We recommend following the best practice of using either start or end buttons with the stacked toast layout.', el ); } diff --git a/core/src/components/toggle/toggle.scss b/core/src/components/toggle/toggle.scss index ad78e471c0b..b64107e66b0 100644 --- a/core/src/components/toggle/toggle.scss +++ b/core/src/components/toggle/toggle.scss @@ -31,8 +31,6 @@ max-width: 100%; - outline: none; - cursor: pointer; user-select: none; z-index: $z-index-item-input; @@ -69,8 +67,12 @@ pointer-events: none; } +/** + * The native input must be hidden with display instead of visibility or + * aria-hidden to avoid accessibility issues with nested interactive elements. + */ input { - @include visually-hidden(); + display: none; } // Toggle Wrapper diff --git a/core/src/components/toggle/toggle.tsx b/core/src/components/toggle/toggle.tsx index 8ccdb60d3dd..57a902c9411 100644 --- a/core/src/components/toggle/toggle.tsx +++ b/core/src/components/toggle/toggle.tsx @@ -35,6 +35,7 @@ import type { ToggleChangeEventDetail } from './toggle-interface'; }) export class Toggle implements ComponentInterface { private inputId = `ion-tg-${toggleIds++}`; + private inputLabelId = `${this.inputId}-lbl`; private helperTextId = `${this.inputId}-helper-text`; private errorTextId = `${this.inputId}-error-text`; private gesture?: Gesture; @@ -246,6 +247,15 @@ export class Toggle implements ComponentInterface { } } + private onKeyDown = (ev: KeyboardEvent) => { + if (ev.key === ' ') { + ev.preventDefault(); + if (!this.disabled) { + this.toggleChecked(); + } + } + }; + private onClick = (ev: MouseEvent) => { if (this.disabled) { return; @@ -355,8 +365,23 @@ export class Toggle implements ComponentInterface { } render() { - const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name, alignment, required } = - this; + const { + activated, + alignment, + checked, + color, + disabled, + el, + errorTextId, + hasLabel, + inheritedAttributes, + inputId, + inputLabelId, + justify, + labelPlacement, + name, + required, + } = this; const mode = getIonMode(this); const value = this.getValue(); @@ -365,9 +390,16 @@ export class Toggle implements ComponentInterface { return ( -