diff --git a/CHANGELOG.md b/CHANGELOG.md index 8874ca0c0df..3428f4c4d9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.6.0](https://github.com/ionic-team/ionic-framework/compare/v8.5.9...v8.6.0) (2025-06-04) + + +### Bug Fixes + +* **input-otp:** correctly handle autofill by splitting the values into all inputs ([#30444](https://github.com/ionic-team/ionic-framework/issues/30444)) ([b77447b](https://github.com/ionic-team/ionic-framework/commit/b77447bea050821da1e5f618ec7b7b530e7f7f5d)) +* **scroll-assist:** allow focus on input's siblings ([#30409](https://github.com/ionic-team/ionic-framework/issues/30409)) ([2dea607](https://github.com/ionic-team/ionic-framework/commit/2dea6071db12903f2ce815328db19b95366aa9a5)) + + +### Features + +* **datetime:** add animation to adjacent days selection ([#30298](https://github.com/ionic-team/ionic-framework/issues/30298)) ([e140b90](https://github.com/ionic-team/ionic-framework/commit/e140b9010fd63490a8d340b3d705869fb04a4319)) +* **datetime:** add showAdjacentDays to display days from the previous and next months ([#30262](https://github.com/ionic-team/ionic-framework/issues/30262)) ([b67259e](https://github.com/ionic-team/ionic-framework/commit/b67259edae267c2dbece360da532ca9017c8febd)) +* **input-otp:** add new input-otp component ([#30386](https://github.com/ionic-team/ionic-framework/issues/30386)) ([4d6a067](https://github.com/ionic-team/ionic-framework/commit/4d6a067677a7b828263c2ed71a409a3a4c392c85)) +* **toggle:** add iOS 18 haptic feedback ([#29945](https://github.com/ionic-team/ionic-framework/issues/29945)) ([796e007](https://github.com/ionic-team/ionic-framework/commit/796e00720e0da90eda1d246a6d5b11f954e5993c)) + + + + + ## [8.5.9](https://github.com/ionic-team/ionic-framework/compare/v8.5.8...v8.5.9) (2025-06-04) diff --git a/core/.eslintrc.js b/core/.eslintrc.js index 7b2a4188ede..e3acd261965 100644 --- a/core/.eslintrc.js +++ b/core/.eslintrc.js @@ -26,7 +26,7 @@ module.exports = { "@typescript-eslint/no-unused-vars": [ "warn", { - "varsIgnorePattern": "^h$" + "varsIgnorePattern": "^(h|Fragment)$" } ], "no-useless-catch": "off", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 0ae3e61282f..f86129eee83 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.6.0](https://github.com/ionic-team/ionic-framework/compare/v8.5.9...v8.6.0) (2025-06-04) + + +### Bug Fixes + +* **input-otp:** correctly handle autofill by splitting the values into all inputs ([#30444](https://github.com/ionic-team/ionic-framework/issues/30444)) ([b77447b](https://github.com/ionic-team/ionic-framework/commit/b77447bea050821da1e5f618ec7b7b530e7f7f5d)) +* **scroll-assist:** allow focus on input's siblings ([#30409](https://github.com/ionic-team/ionic-framework/issues/30409)) ([2dea607](https://github.com/ionic-team/ionic-framework/commit/2dea6071db12903f2ce815328db19b95366aa9a5)) + + +### Features + +* **datetime:** add animation to adjacent days selection ([#30298](https://github.com/ionic-team/ionic-framework/issues/30298)) ([e140b90](https://github.com/ionic-team/ionic-framework/commit/e140b9010fd63490a8d340b3d705869fb04a4319)) +* **datetime:** add showAdjacentDays to display days from the previous and next months ([#30262](https://github.com/ionic-team/ionic-framework/issues/30262)) ([b67259e](https://github.com/ionic-team/ionic-framework/commit/b67259edae267c2dbece360da532ca9017c8febd)) +* **input-otp:** add new input-otp component ([#30386](https://github.com/ionic-team/ionic-framework/issues/30386)) ([4d6a067](https://github.com/ionic-team/ionic-framework/commit/4d6a067677a7b828263c2ed71a409a3a4c392c85)) +* **toggle:** add iOS 18 haptic feedback ([#29945](https://github.com/ionic-team/ionic-framework/issues/29945)) ([796e007](https://github.com/ionic-team/ionic-framework/commit/796e00720e0da90eda1d246a6d5b11f954e5993c)) + + + + + ## [8.5.9](https://github.com/ionic-team/ionic-framework/compare/v8.5.8...v8.5.9) (2025-06-04) diff --git a/core/api.txt b/core/api.txt index 201c86c28d0..c792eb050a1 100644 --- a/core/api.txt +++ b/core/api.txt @@ -534,6 +534,7 @@ ion-datetime,prop,name,string,this.inputId,false,false ion-datetime,prop,preferWheel,boolean,false,false,false ion-datetime,prop,presentation,"date" | "date-time" | "month" | "month-year" | "time" | "time-date" | "year",'date-time',false,false ion-datetime,prop,readonly,boolean,false,false,false +ion-datetime,prop,showAdjacentDays,boolean,false,false,false ion-datetime,prop,showClearButton,boolean,false,false,false ion-datetime,prop,showDefaultButtons,boolean,false,false,false ion-datetime,prop,showDefaultTimeLabel,boolean,true,false,false @@ -779,6 +780,73 @@ ion-input,css-prop,--placeholder-font-weight,md ion-input,css-prop,--placeholder-opacity,ios ion-input,css-prop,--placeholder-opacity,md +ion-input-otp,scoped +ion-input-otp,prop,autocapitalize,string,'off',false,false +ion-input-otp,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record | undefined,undefined,false,true +ion-input-otp,prop,disabled,boolean,false,false,true +ion-input-otp,prop,fill,"outline" | "solid" | undefined,'outline',false,false +ion-input-otp,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined,undefined,false,false +ion-input-otp,prop,length,number,4,false,false +ion-input-otp,prop,pattern,string | undefined,undefined,false,false +ion-input-otp,prop,readonly,boolean,false,false,true +ion-input-otp,prop,separators,number[] | string | undefined,undefined,false,false +ion-input-otp,prop,shape,"rectangular" | "round" | "soft",'round',false,false +ion-input-otp,prop,size,"large" | "medium" | "small",'medium',false,false +ion-input-otp,prop,type,"number" | "text",'number',false,false +ion-input-otp,prop,value,null | number | string | undefined,'',false,false +ion-input-otp,method,setFocus,setFocus(index?: number) => Promise +ion-input-otp,event,ionBlur,FocusEvent,true +ion-input-otp,event,ionChange,InputOtpChangeEventDetail,true +ion-input-otp,event,ionComplete,InputOtpCompleteEventDetail,true +ion-input-otp,event,ionFocus,FocusEvent,true +ion-input-otp,event,ionInput,InputOtpInputEventDetail,true +ion-input-otp,css-prop,--background,ios +ion-input-otp,css-prop,--background,md +ion-input-otp,css-prop,--border-color,ios +ion-input-otp,css-prop,--border-color,md +ion-input-otp,css-prop,--border-radius,ios +ion-input-otp,css-prop,--border-radius,md +ion-input-otp,css-prop,--border-width,ios +ion-input-otp,css-prop,--border-width,md +ion-input-otp,css-prop,--color,ios +ion-input-otp,css-prop,--color,md +ion-input-otp,css-prop,--height,ios +ion-input-otp,css-prop,--height,md +ion-input-otp,css-prop,--highlight-color-focused,ios +ion-input-otp,css-prop,--highlight-color-focused,md +ion-input-otp,css-prop,--highlight-color-invalid,ios +ion-input-otp,css-prop,--highlight-color-invalid,md +ion-input-otp,css-prop,--highlight-color-valid,ios +ion-input-otp,css-prop,--highlight-color-valid,md +ion-input-otp,css-prop,--margin-bottom,ios +ion-input-otp,css-prop,--margin-bottom,md +ion-input-otp,css-prop,--margin-end,ios +ion-input-otp,css-prop,--margin-end,md +ion-input-otp,css-prop,--margin-start,ios +ion-input-otp,css-prop,--margin-start,md +ion-input-otp,css-prop,--margin-top,ios +ion-input-otp,css-prop,--margin-top,md +ion-input-otp,css-prop,--min-width,ios +ion-input-otp,css-prop,--min-width,md +ion-input-otp,css-prop,--padding-bottom,ios +ion-input-otp,css-prop,--padding-bottom,md +ion-input-otp,css-prop,--padding-end,ios +ion-input-otp,css-prop,--padding-end,md +ion-input-otp,css-prop,--padding-start,ios +ion-input-otp,css-prop,--padding-start,md +ion-input-otp,css-prop,--padding-top,ios +ion-input-otp,css-prop,--padding-top,md +ion-input-otp,css-prop,--separator-border-radius,ios +ion-input-otp,css-prop,--separator-border-radius,md +ion-input-otp,css-prop,--separator-color,ios +ion-input-otp,css-prop,--separator-color,md +ion-input-otp,css-prop,--separator-height,ios +ion-input-otp,css-prop,--separator-height,md +ion-input-otp,css-prop,--separator-width,ios +ion-input-otp,css-prop,--separator-width,md +ion-input-otp,css-prop,--width,ios +ion-input-otp,css-prop,--width,md + ion-input-password-toggle,shadow ion-input-password-toggle,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record | undefined,undefined,false,true ion-input-password-toggle,prop,hideIcon,string | undefined,undefined,false,false diff --git a/core/package-lock.json b/core/package-lock.json index d31de4310eb..c423936dd77 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,15 +1,15 @@ { "name": "@ionic/core", - "version": "8.5.9", + "version": "8.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@ionic/core", - "version": "8.5.9", + "version": "8.6.0", "license": "MIT", "dependencies": { - "@stencil/core": "4.20.0", + "@stencil/core": "4.33.1", "ionicons": "^7.2.2", "tslib": "^2.1.0" }, @@ -1776,6 +1776,110 @@ "rollup": "^1.20.0||^2.0.0" } }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1810,9 +1914,9 @@ } }, "node_modules/@stencil/core": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.20.0.tgz", - "integrity": "sha512-WPrTHFngvN081RY+dJPneKQLwnOFD60OMCOQGmmSHfCW0f4ujPMzzhwWU1gcSwXPWXz5O+8cBiiCaxAbJU7kAg==", + "version": "4.33.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.33.1.tgz", + "integrity": "sha512-12k9xhAJBkpg598it+NRmaYIdEe6TSnsL/v6/KRXDcUyTK11VYwZQej2eHnMWtqot+znJ+GNTqb5YbiXi+5Low==", "license": "MIT", "bin": { "stencil": "bin/stencil" @@ -1820,6 +1924,16 @@ "engines": { "node": ">=16.0.0", "npm": ">=7.10.0" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9" } }, "node_modules/@stencil/react-output-target": { @@ -5605,19 +5719,6 @@ "@stencil/core": "^4.0.3" } }, - "node_modules/ionicons/node_modules/@stencil/core": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.21.0.tgz", - "integrity": "sha512-v50lnVbzS8mpMSnEVxR+G75XpvxHKtkJaQrNPE8+/fF6Ppr5z4bcdcBhcP8LPfEW+4BZcic6VifMXRwTopc+kw==", - "license": "MIT", - "bin": { - "stencil": "bin/stencil" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.10.0" - } - }, "node_modules/is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -11803,6 +11904,54 @@ "picomatch": "^2.2.2" } }, + "@rollup/rollup-darwin-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "optional": true + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -11835,9 +11984,19 @@ "requires": {} }, "@stencil/core": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.20.0.tgz", - "integrity": "sha512-WPrTHFngvN081RY+dJPneKQLwnOFD60OMCOQGmmSHfCW0f4ujPMzzhwWU1gcSwXPWXz5O+8cBiiCaxAbJU7kAg==" + "version": "4.33.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.33.1.tgz", + "integrity": "sha512-12k9xhAJBkpg598it+NRmaYIdEe6TSnsL/v6/KRXDcUyTK11VYwZQej2eHnMWtqot+znJ+GNTqb5YbiXi+5Low==", + "requires": { + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9" + } }, "@stencil/react-output-target": { "version": "0.5.3", @@ -14591,13 +14750,6 @@ "integrity": "sha512-I3iYIfc9Q9FRifWyFSwTAvbEABWlWY32i0sAVDDPGYnaIZVugkLCZFbEcrphW6ixVPg8tt1oLwalo/JJwbEqnA==", "requires": { "@stencil/core": "^4.0.3" - }, - "dependencies": { - "@stencil/core": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.21.0.tgz", - "integrity": "sha512-v50lnVbzS8mpMSnEVxR+G75XpvxHKtkJaQrNPE8+/fF6Ppr5z4bcdcBhcP8LPfEW+4BZcic6VifMXRwTopc+kw==" - } } }, "is-alphabetical": { diff --git a/core/package.json b/core/package.json index c208f540f80..895054ef2de 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/core", - "version": "8.5.9", + "version": "8.6.0", "description": "Base components for Ionic", "keywords": [ "ionic", @@ -31,7 +31,7 @@ "loader/" ], "dependencies": { - "@stencil/core": "4.20.0", + "@stencil/core": "4.33.1", "ionicons": "^7.2.2", "tslib": "^2.1.0" }, diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 513a53d50fe..3fc70d62b02 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -18,6 +18,7 @@ import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-int import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface"; import { SpinnerTypes } from "./components/spinner/spinner-configs"; import { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface"; +import { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail } from "./components/input-otp/input-otp-interface"; import { MenuChangeEventDetail, MenuCloseEventDetail, MenuType, Side } from "./components/menu/menu-interface"; import { ModalBreakpointChangeEventDetail, ModalHandleBehavior } from "./components/modal/modal-interface"; import { NavComponent, NavComponentWithProps, NavOptions, RouterOutletOptions, SwipeGestureHandler, TransitionDoneFn, TransitionInstruction } from "./components/nav/nav-interface"; @@ -55,6 +56,7 @@ export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-int export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface"; export { SpinnerTypes } from "./components/spinner/spinner-configs"; export { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface"; +export { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail } from "./components/input-otp/input-otp-interface"; export { MenuChangeEventDetail, MenuCloseEventDetail, MenuType, Side } from "./components/menu/menu-interface"; export { ModalBreakpointChangeEventDetail, ModalHandleBehavior } from "./components/modal/modal-interface"; export { NavComponent, NavComponentWithProps, NavOptions, RouterOutletOptions, SwipeGestureHandler, TransitionDoneFn, TransitionInstruction } from "./components/nav/nav-interface"; @@ -83,6 +85,7 @@ export namespace Components { interface IonAccordion { /** * If `true`, the accordion cannot be interacted with. + * @default false */ "disabled": boolean; /** @@ -91,32 +94,39 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, the accordion cannot be interacted with, but does not alter the opacity. + * @default false */ "readonly": boolean; /** * The toggle icon to use. This icon will be rotated when the accordion is expanded or collapsed. + * @default chevronDown */ "toggleIcon": string; /** * The slot inside of `ion-item` to place the toggle icon. Defaults to `"end"`. + * @default 'end' */ "toggleIconSlot": 'start' | 'end'; /** * The value of the accordion. Defaults to an autogenerated value. + * @default `ion-accordion-${accordionIds++}` */ "value": string; } interface IonAccordionGroup { /** * If `true`, all accordions inside of the accordion group will animate when expanding or collapsing. + * @default true */ "animated": boolean; /** * If `true`, the accordion group cannot be interacted with. + * @default false */ "disabled": boolean; /** * Describes the expansion behavior for each accordion. Possible values are `"compact"` and `"inset"`. Defaults to `"compact"`. + * @default 'compact' */ "expand": 'compact' | 'inset'; "getAccordions": () => Promise; @@ -130,6 +140,7 @@ export namespace Components { "multiple"?: boolean; /** * If `true`, the accordion group cannot be interacted with, but does not alter the opacity. + * @default false */ "readonly": boolean; /** @@ -144,14 +155,17 @@ export namespace Components { interface IonActionSheet { /** * If `true`, the action sheet will animate. + * @default true */ "animated": boolean; /** * If `true`, the action sheet will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss": boolean; /** * An array of buttons for the action sheet. + * @default [] */ "buttons": (ActionSheetButton | string)[]; /** @@ -169,6 +183,9 @@ export namespace Components { * Animation to use when the action sheet is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController": boolean; /** * Title for the action sheet. @@ -180,10 +197,12 @@ export namespace Components { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the action sheet will open. If `false`, the action sheet will close. Use this if you need finer grained control over presentation, otherwise just use the actionSheetController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the action sheet dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose": boolean; /** @@ -213,6 +232,7 @@ export namespace Components { "subHeader"?: string; /** * If `true`, the action sheet will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; /** @@ -223,14 +243,17 @@ export namespace Components { interface IonAlert { /** * If `true`, the alert will animate. + * @default true */ "animated": boolean; /** * If `true`, the alert will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss": boolean; /** * Array of buttons to be added to the alert. + * @default [] */ "buttons": (AlertButton | string)[]; /** @@ -248,6 +271,9 @@ export namespace Components { * Animation to use when the alert is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController": boolean; /** * The main title in the heading of the alert. @@ -259,14 +285,17 @@ export namespace Components { "htmlAttributes"?: { [key: string]: any }; /** * Array of input to show in the alert. + * @default [] */ "inputs": AlertInput[]; /** * If `true`, the alert will open. If `false`, the alert will close. Use this if you need finer grained control over presentation, otherwise just use the alertController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the alert dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose": boolean; /** @@ -300,6 +329,7 @@ export namespace Components { "subHeader"?: string; /** * If `true`, the alert will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; /** @@ -326,6 +356,7 @@ export namespace Components { "defaultHref"?: string; /** * If `true`, the user cannot interact with the button. + * @default false */ "disabled": boolean; /** @@ -346,20 +377,24 @@ export namespace Components { "text"?: string | null; /** * The type of the button. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } interface IonBackdrop { /** * If `true`, the backdrop will stop propagation on tap. + * @default true */ "stopPropagation": boolean; /** * If `true`, the backdrop will can be clicked and will emit the `ionBackdropTap` event. + * @default true */ "tappable": boolean; /** * If `true`, the backdrop will be visible. + * @default true */ "visible": boolean; } @@ -376,8 +411,12 @@ export namespace Components { interface IonBreadcrumb { /** * If `true`, the breadcrumb will take on a different look to show that it is the currently active breadcrumb. Defaults to `true` for the last breadcrumb if it is not set on any. + * @default false */ "active": boolean; + /** + * @default false + */ "collapsed": boolean; /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). @@ -385,6 +424,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the breadcrumb. + * @default false */ "disabled": boolean; /** @@ -410,6 +450,7 @@ export namespace Components { "routerAnimation": AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection": RouterDirection; /** @@ -429,10 +470,12 @@ export namespace Components { "color"?: Color; /** * The number of breadcrumbs to show after the collapsed indicator. If `itemsBeforeCollapse` + `itemsAfterCollapse` is greater than `maxItems`, the breadcrumbs will not be collapsed. + * @default 1 */ "itemsAfterCollapse": number; /** * The number of breadcrumbs to show before the collapsed indicator. If `itemsBeforeCollapse` + `itemsAfterCollapse` is greater than `maxItems`, the breadcrumbs will not be collapsed. + * @default 1 */ "itemsBeforeCollapse": number; /** @@ -447,6 +490,7 @@ export namespace Components { interface IonButton { /** * The type of button. + * @default 'button' */ "buttonType": string; /** @@ -455,6 +499,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the button. + * @default false */ "disabled": boolean; /** @@ -491,6 +536,7 @@ export namespace Components { "routerAnimation": AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection": RouterDirection; /** @@ -503,6 +549,7 @@ export namespace Components { "size"?: 'small' | 'default' | 'large'; /** * If `true`, activates a button with a heavier font weight. + * @default false */ "strong": boolean; /** @@ -511,18 +558,21 @@ export namespace Components { "target": string | undefined; /** * The type of the button. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } interface IonButtons { /** * If true, buttons will disappear when its parent toolbar has fully collapsed if the toolbar is not the first toolbar. If the toolbar is the first toolbar, the buttons will be hidden and will only be shown once all toolbars have fully collapsed. Only applies in `ios` mode with `collapse` set to `true` on `ion-header`. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles) + * @default false */ "collapse": boolean; } interface IonCard { /** * If `true`, a button tag will be rendered and the card will be tappable. + * @default false */ "button": boolean; /** @@ -531,6 +581,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the card. + * @default false */ "disabled": boolean; /** @@ -555,6 +606,7 @@ export namespace Components { "routerAnimation": AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection": RouterDirection; /** @@ -563,6 +615,7 @@ export namespace Components { "target": string | undefined; /** * The type of the button. Only used when an `onclick` or `button` property is present. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } @@ -583,6 +636,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, the card header will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; } @@ -613,6 +667,7 @@ export namespace Components { "alignment"?: 'start' | 'center'; /** * If `true`, the checkbox is selected. + * @default false */ "checked": boolean; /** @@ -621,6 +676,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the checkbox. + * @default false */ "disabled": boolean; /** @@ -633,6 +689,7 @@ export namespace Components { "helperText"?: string; /** * If `true`, the checkbox will visually appear as indeterminate. + * @default false */ "indeterminate": boolean; /** @@ -641,6 +698,7 @@ export namespace Components { "justify"?: 'start' | 'end' | 'space-between'; /** * Where to place the label relative to the checkbox. `"start"`: The label will appear to the left of the checkbox in LTR and to the right in RTL. `"end"`: The label will appear to the right of the checkbox in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the checkbox regardless of the direction. The alignment of the label can be controlled with the `alignment` property. + * @default 'start' */ "labelPlacement": 'start' | 'end' | 'fixed' | 'stacked'; /** @@ -649,15 +707,18 @@ export namespace Components { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** * If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid. + * @default false */ "required": boolean; "setFocus": () => Promise; /** * The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an ``, it's only used when the checkbox participates in a native `
`. + * @default 'on' */ "value": any | null; } @@ -668,6 +729,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the chip. + * @default false */ "disabled": boolean; /** @@ -676,6 +738,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * Display an outline style button. + * @default false */ "outline": boolean; } @@ -784,6 +847,7 @@ export namespace Components { "color"?: Color; /** * Controls where the fixed content is placed relative to the main content in the DOM. This can be used to control the order in which fixed elements receive keyboard focus. For example, if a FAB in the fixed slot should receive keyboard focus before the main page content, set this property to `'before'`. + * @default 'after' */ "fixedSlotPlacement": 'after' | 'before'; /** @@ -792,6 +856,7 @@ export namespace Components { "forceOverscroll"?: boolean; /** * If `true`, the content will scroll behind the headers and footers. This effect can easily be seen by setting the toolbar to transparent. + * @default false */ "fullscreen": boolean; /** @@ -811,6 +876,7 @@ export namespace Components { "scrollByPoint": (x: number, y: number, duration: number) => Promise; /** * Because of performance reasons, ionScroll events are disabled by default, in order to enable them and start listening from (ionScroll), set this property to `true`. + * @default false */ "scrollEvents": boolean; /** @@ -832,10 +898,12 @@ export namespace Components { "scrollToTop": (duration?: number) => Promise; /** * If you want to enable the content scrolling in the X axis, set this property to `true`. + * @default false */ "scrollX": boolean; /** * If you want to disable the content scrolling in the Y axis, set this property to `false`. + * @default true */ "scrollY": boolean; } @@ -846,14 +914,17 @@ export namespace Components { "cancel": (closeOverlay?: boolean) => Promise; /** * The text to display on the picker's cancel button. + * @default 'Cancel' */ "cancelText": string; /** * The text to display on the picker's "Clear" button. + * @default 'Clear' */ "clearText": string; /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** @@ -866,14 +937,17 @@ export namespace Components { "dayValues"?: number[] | number | string; /** * If `true`, the user cannot interact with the datetime. + * @default false */ "disabled": boolean; /** * The text to display on the picker's "Done" button. + * @default 'Done' */ "doneText": string; /** * The first day of the week to use for `ion-datetime`. The default value is `0` and represents Sunday. + * @default 0 */ "firstDayOfWeek": number; /** @@ -898,6 +972,7 @@ export namespace Components { "isDateEnabled"?: (dateIsoString: string) => boolean; /** * The locale to use for `ion-datetime`. This impacts month and day name formatting. The `"default"` value refers to the default locale set by your device. + * @default 'default' */ "locale": string; /** @@ -922,46 +997,61 @@ export namespace Components { "monthValues"?: number[] | number | string; /** * If `true`, multiple dates can be selected at once. Only applies to `presentation="date"` and `preferWheel="false"`. + * @default false */ "multiple": boolean; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** * If `true`, a wheel picker will be rendered instead of a calendar grid where possible. If `false`, a calendar grid will be rendered instead of a wheel picker where possible. A wheel picker can be rendered instead of a grid when `presentation` is one of the following values: `"date"`, `"date-time"`, or `"time-date"`. A wheel picker will always be rendered regardless of the `preferWheel` value when `presentation` is one of the following values: `"time"`, `"month"`, `"month-year"`, or `"year"`. + * @default false */ "preferWheel": boolean; /** * Which values you want to select. `"date"` will show a calendar picker to select the month, day, and year. `"time"` will show a time picker to select the hour, minute, and (optionally) AM/PM. `"date-time"` will show the date picker first and time picker second. `"time-date"` will show the time picker first and date picker second. + * @default 'date-time' */ "presentation": DatetimePresentation; /** * If `true`, the datetime appears normal but the selected date cannot be changed. + * @default false */ "readonly": boolean; /** * Resets the internal state of the datetime but does not update the value. Passing a valid ISO-8601 string will reset the state of the component to the provided date. If no value is provided, the internal state will be reset to the clamped value of the min, max and today. */ "reset": (startDate?: string) => Promise; + /** + * If `true`, the datetime calendar displays a six-week (42-day) layout, including days from the previous and next months to fill the grid. These adjacent days are selectable unless disabled. + * @default false + */ + "showAdjacentDays": boolean; /** * If `true`, a "Clear" button will be rendered alongside the default "Cancel" and "OK" buttons at the bottom of the `ion-datetime` component. Developers can also use the `button` slot if they want to customize these buttons. If custom buttons are set in the `button` slot then the default buttons will not be rendered. + * @default false */ "showClearButton": boolean; /** * If `true`, the default "Cancel" and "OK" buttons will be rendered at the bottom of the `ion-datetime` component. Developers can also use the `button` slot if they want to customize these buttons. If custom buttons are set in the `button` slot then the default buttons will not be rendered. + * @default false */ "showDefaultButtons": boolean; /** * If `true`, the default "Time" label will be rendered for the time selector of the `ion-datetime` component. Developers can also use the `time-label` slot if they want to customize this label. If a custom label is set in the `time-label` slot then the default label will not be rendered. + * @default true */ "showDefaultTimeLabel": boolean; /** * If `true`, a header will be shown above the calendar picker. This will include both the slotted title, and the selected date. + * @default false */ "showDefaultTitle": boolean; /** * If `cover`, the `ion-datetime` will expand to cover the full width of its container. If `fixed`, the `ion-datetime` will have a fixed width. + * @default 'fixed' */ "size": 'cover' | 'fixed'; /** @@ -980,6 +1070,7 @@ export namespace Components { interface IonDatetimeButton { /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** @@ -988,6 +1079,7 @@ export namespace Components { "datetime"?: string; /** * If `true`, the user cannot interact with the button. + * @default false */ "disabled": boolean; /** @@ -998,6 +1090,7 @@ export namespace Components { interface IonFab { /** * If `true`, both the `ion-fab-button` and all `ion-fab-list` inside `ion-fab` will become active. That means `ion-fab-button` will become a `close` icon and `ion-fab-list` will become visible. + * @default false */ "activated": boolean; /** @@ -1006,6 +1099,7 @@ export namespace Components { "close": () => Promise; /** * If `true`, the fab will display on the edge of the header if `vertical` is `"top"`, and on the edge of the footer if it is `"bottom"`. Should be used with a `fixed` slot. + * @default false */ "edge": boolean; /** @@ -1024,10 +1118,12 @@ export namespace Components { interface IonFabButton { /** * If `true`, the fab button will be show a close icon. + * @default false */ "activated": boolean; /** * The icon name to use for the close icon. This will appear when the fab button is pressed. Only applies if it is the main button inside of a fab containing a fab list. + * @default close */ "closeIcon": string; /** @@ -1036,6 +1132,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the fab button. + * @default false */ "disabled": boolean; /** @@ -1060,10 +1157,12 @@ export namespace Components { "routerAnimation": AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection": RouterDirection; /** * If `true`, the fab button will show when in a fab-list. + * @default false */ "show": boolean; /** @@ -1076,20 +1175,24 @@ export namespace Components { "target": string | undefined; /** * If `true`, the fab button will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; /** * The type of the button. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } interface IonFabList { /** * If `true`, the fab list will show all fab buttons in the list. + * @default false */ "activated": boolean; /** * The side the fab list will show on relative to the main fab button. + * @default 'bottom' */ "side": 'start' | 'end' | 'top' | 'bottom'; } @@ -1104,12 +1207,14 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, the footer will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). Note: In order to scroll content behind the footer, the `fullscreen` attribute needs to be set on the content. + * @default false */ "translucent": boolean; } interface IonGrid { /** * If `true`, the grid will have a fixed width based on the screen size. + * @default false */ "fixed": boolean; } @@ -1124,6 +1229,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, the header will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). Note: In order to scroll content behind the header, the `fullscreen` attribute needs to be set on the content. + * @default false */ "translucent": boolean; } @@ -1144,14 +1250,17 @@ export namespace Components { "complete": () => Promise; /** * If `true`, the infinite scroll will be hidden and scroll event listeners will be removed. Set this to true to disable the infinite scroll from actively trying to receive new data while scrolling. This is useful when it is known that there is no more data that can be added, and the infinite scroll is no longer needed. + * @default false */ "disabled": boolean; /** * The position of the infinite scroll element. The value can be either `top` or `bottom`. + * @default 'bottom' */ "position": 'top' | 'bottom'; /** * The threshold distance from the bottom of the content to call the `infinite` output event when scrolled. The threshold value can be either a percent, or in pixels. For example, use the value of `10%` for the `infinite` output event to get called when the user has scrolled 10% from the bottom of the page. Use the value `100px` when the scroll is within 100 pixels from the bottom of the page. + * @default '15%' */ "threshold": string; } @@ -1168,22 +1277,27 @@ export namespace Components { interface IonInput { /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'off' */ "autocapitalize": string; /** * Indicates whether the value of the control can be automatically completed by the browser. + * @default 'off' */ "autocomplete": AutocompleteTypes; /** * Whether auto correction should be enabled when the user is entering/editing the text value. + * @default 'off' */ "autocorrect": 'on' | 'off'; /** * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. + * @default false */ "autofocus": boolean; /** * If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input. + * @default false */ "clearInput": boolean; /** @@ -1200,6 +1314,7 @@ export namespace Components { "color"?: Color; /** * If `true`, a character counter will display the ratio of characters used and the total character limit. Developers must also set the `maxlength` property for the counter to be calculated correctly. + * @default false */ "counter": boolean; /** @@ -1212,6 +1327,7 @@ export namespace Components { "debounce"?: number; /** * If `true`, the user cannot interact with the input. + * @default false */ "disabled": boolean; /** @@ -1244,6 +1360,7 @@ export namespace Components { "label"?: string; /** * Where to place the label relative to the input. `"start"`: The label will appear to the left of the input in LTR and to the right in RTL. `"end"`: The label will appear to the right of the input in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the input when the input is focused or it has a value. Otherwise it will appear on top of the input. `"stacked"`: The label will appear smaller and above the input regardless even when the input is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). + * @default 'start' */ "labelPlacement": 'start' | 'end' | 'floating' | 'stacked' | 'fixed'; /** @@ -1272,6 +1389,7 @@ export namespace Components { "multiple"?: boolean; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** @@ -1284,10 +1402,12 @@ export namespace Components { "placeholder"?: string; /** * If `true`, the user cannot modify the value. + * @default false */ "readonly": boolean; /** * If `true`, the user must fill in a value before submitting a form. + * @default false */ "required": boolean; /** @@ -1300,6 +1420,7 @@ export namespace Components { "shape"?: 'round'; /** * If `true`, the element will have its spelling and grammar checked. + * @default false */ "spellcheck": boolean; /** @@ -1308,10 +1429,80 @@ export namespace Components { "step"?: string; /** * The type of control to display. The default type is text. + * @default 'text' */ "type": TextFieldTypes; /** * The value of the input. + * @default '' + */ + "value"?: string | number | null; + } + interface IonInputOtp { + /** + * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'off' + */ + "autocapitalize": string; + /** + * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + */ + "color"?: Color; + /** + * If `true`, the user cannot interact with the input. + * @default false + */ + "disabled": boolean; + /** + * The fill for the input boxes. If `"solid"` the input boxes will have a background. If `"outline"` the input boxes will be transparent with a border. + * @default 'outline' + */ + "fill"?: 'outline' | 'solid'; + /** + * A hint to the browser for which keyboard to display. Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, `"email"`, `"numeric"`, `"decimal"`, and `"search"`. For numbers (type="number"): "numeric" For text (type="text"): "text" + */ + "inputmode"?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'; + /** + * The number of input boxes to display. + * @default 4 + */ + "length": number; + /** + * A regex pattern string for allowed characters. Defaults based on type. For numbers (`type="number"`): `"[\p{N}]"` For text (`type="text"`): `"[\p{L}\p{N}]"` + */ + "pattern"?: string; + /** + * If `true`, the user cannot modify the value. + * @default false + */ + "readonly": boolean; + /** + * Where separators should be shown between input boxes. Can be a comma-separated string or an array of numbers. For example: `"3"` will show a separator after the 3rd input box. `[1,4]` will show a separator after the 1st and 4th input boxes. `"all"` will show a separator between every input box. + */ + "separators"?: 'all' | string | number[]; + /** + * Sets focus to an input box. + * @param index - The index of the input box to focus (0-based). If provided and the input box has a value, the input box at that index will be focused. Otherwise, the first empty input box or the last input if all are filled will be focused. + */ + "setFocus": (index?: number) => Promise; + /** + * The shape of the input boxes. If "round" they will have an increased border radius. If "rectangular" they will have no border radius. If "soft" they will have a soft border radius. + * @default 'round' + */ + "shape": 'round' | 'rectangular' | 'soft'; + /** + * The size of the input boxes. + * @default 'medium' + */ + "size": 'small' | 'medium' | 'large'; + /** + * The type of input allowed in the input boxes. + * @default 'number' + */ + "type": 'text' | 'number'; + /** + * The value of the input group. + * @default '' */ "value"?: string | number | null; } @@ -1332,11 +1523,15 @@ export namespace Components { * The icon that can be used to represent showing a password. If not set, the "eye" Ionicon will be used. */ "showIcon"?: string; + /** + * @default 'password' + */ "type": TextFieldTypes; } interface IonItem { /** * If `true`, a button tag will be rendered and the item will be tappable. + * @default false */ "button": boolean; /** @@ -1349,10 +1544,12 @@ export namespace Components { "detail"?: boolean; /** * The icon to use when `detail` is set to `true`. + * @default chevronForward */ "detailIcon": string; /** * If `true`, the user cannot interact with the item. + * @default false */ "disabled": boolean; /** @@ -1381,6 +1578,7 @@ export namespace Components { "routerAnimation": AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection": RouterDirection; /** @@ -1389,6 +1587,7 @@ export namespace Components { "target": string | undefined; /** * The type of the button. Only used when an `onclick` or `button` property is present. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } @@ -1403,6 +1602,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * When it's set to `true`, the item-divider will stay visible when it reaches the top of the viewport until the next `ion-item-divider` replaces it. This feature relies in `position:sticky`: https://caniuse.com/#feat=css-sticky + * @default false */ "sticky": boolean; } @@ -1415,6 +1615,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the item option. + * @default false */ "disabled": boolean; /** @@ -1423,6 +1624,7 @@ export namespace Components { "download": string | undefined; /** * If `true`, the option will expand to take up the available width and cover any other options. + * @default false */ "expandable": boolean; /** @@ -1443,6 +1645,7 @@ export namespace Components { "target": string | undefined; /** * The type of the button. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } @@ -1450,6 +1653,7 @@ export namespace Components { "fireSwipeEvent": () => Promise; /** * The side the option button should be on. Possible values: `"start"` and `"end"`. If you have multiple `ion-item-options`, a side must be provided for each. + * @default 'end' */ "side": Side; } @@ -1464,6 +1668,7 @@ export namespace Components { "closeOpened": () => Promise; /** * If `true`, the user cannot interact with the sliding item. + * @default false */ "disabled": boolean; /** @@ -1501,6 +1706,7 @@ export namespace Components { "closeSlidingItems": () => Promise; /** * If `true`, the list will have margin around it and rounded corners. + * @default false */ "inset": boolean; /** @@ -1529,10 +1735,12 @@ export namespace Components { interface IonLoading { /** * If `true`, the loading indicator will animate. + * @default true */ "animated": boolean; /** * If `true`, the loading indicator will be dismissed when the backdrop is clicked. + * @default false */ "backdropDismiss": boolean; /** @@ -1548,12 +1756,16 @@ export namespace Components { "dismiss": (data?: any, role?: string) => Promise; /** * Number of milliseconds to wait before dismissing the loading indicator. + * @default 0 */ "duration": number; /** * Animation to use when the loading indicator is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController": boolean; /** * Additional attributes to pass to the loader. @@ -1561,10 +1773,12 @@ export namespace Components { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the loading indicator will open. If `false`, the loading indicator will close. Use this if you need finer grained control over presentation, otherwise just use the loadingController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the loading indicator dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose": boolean; /** @@ -1594,6 +1808,7 @@ export namespace Components { "present": () => Promise; /** * If `true`, a backdrop will be displayed behind the loading indicator. + * @default true */ "showBackdrop": boolean; /** @@ -1602,6 +1817,7 @@ export namespace Components { "spinner"?: SpinnerTypes | null; /** * If `true`, the loading indicator will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; /** @@ -1620,6 +1836,7 @@ export namespace Components { "contentId"?: string; /** * If `true`, the menu is disabled. + * @default false */ "disabled": boolean; /** @@ -1632,6 +1849,7 @@ export namespace Components { "isOpen": () => Promise; /** * The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. + * @default 50 */ "maxEdgeStart": number; /** @@ -1648,10 +1866,12 @@ export namespace Components { "setOpen": (shouldOpen: boolean, animated?: boolean, role?: string) => Promise; /** * Which side of the view the menu should be placed. + * @default 'start' */ "side": Side; /** * If `true`, swiping the menu is enabled. + * @default true */ "swipeGesture": boolean; /** @@ -1666,6 +1886,7 @@ export namespace Components { interface IonMenuButton { /** * Automatically hides the menu button when the corresponding menu is not active + * @default true */ "autoHide": boolean; /** @@ -1674,6 +1895,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the menu button. + * @default false */ "disabled": boolean; /** @@ -1686,12 +1908,14 @@ export namespace Components { "mode"?: "ios" | "md"; /** * The type of the button. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; } interface IonMenuToggle { /** * Automatically hides the content when the corresponding menu is not active. By default, it's `true`. Change it to `false` in order to keep `ion-menu-toggle` always visible regardless the state of the menu. + * @default true */ "autoHide": boolean; /** @@ -1702,14 +1926,17 @@ export namespace Components { interface IonModal { /** * If `true`, the modal will animate. + * @default true */ "animated": boolean; /** * A decimal value between 0 and 1 that indicates the point after which the backdrop will begin to fade in when using a sheet modal. Prior to this point, the backdrop will be hidden and the content underneath the sheet can be interacted with. This value is exclusive meaning the backdrop will become active after the value specified. + * @default 0 */ "backdropBreakpoint": number; /** * If `true`, the modal will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss": boolean; /** @@ -1718,6 +1945,7 @@ export namespace Components { "breakpoints"?: number[]; /** * Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + * @default true */ "canDismiss": boolean | ((data?: any, role?: string) => Promise); /** @@ -1745,10 +1973,12 @@ export namespace Components { "enterAnimation"?: AnimationBuilder; /** * Controls whether scrolling or dragging within the sheet modal expands it to a larger breakpoint. This only takes effect when `breakpoints` and `initialBreakpoint` are set. If `true`, scrolling or dragging anywhere in the modal will first expand it to the next breakpoint. Once fully expanded, scrolling will affect the content. If `false`, scrolling will always affect the content. The modal will only expand when dragging the header or handle. The modal will close when dragging the header or handle. It can also be closed when dragging the content, but only if the content is scrolled to the top. + * @default true */ "expandToScroll": boolean; /** * If `true`, focus will not be allowed to move outside of this overlay. If `false`, focus will be allowed to move outside of the overlay. In most scenarios this property should remain set to `true`. Setting this property to `false` can cause severe accessibility issues as users relying on assistive technologies may be able to move focus into a confusing state. We recommend only setting this to `false` when absolutely necessary. Developers may want to consider disabling focus trapping if this overlay presents a non-Ionic overlay from a 3rd party library. Developers would disable focus trapping on the Ionic overlay when presenting the 3rd party overlay and then re-enable focus trapping when dismissing the 3rd party overlay and moving focus back to the Ionic overlay. + * @default true */ "focusTrap": boolean; /** @@ -1761,8 +1991,12 @@ export namespace Components { "handle"?: boolean; /** * The interaction behavior for the sheet modal when the handle is pressed. Defaults to `"none"`, which means the modal will not change size or position when the handle is pressed. Set to `"cycle"` to let the modal cycle between available breakpoints when pressed. Handle behavior is unavailable when the `handle` property is set to `false` or when the `breakpoints` property is not set (using a fullscreen or card modal). + * @default 'none' */ "handleBehavior"?: ModalHandleBehavior; + /** + * @default false + */ "hasController": boolean; /** * Additional attributes to pass to the modal. @@ -1774,14 +2008,17 @@ export namespace Components { "initialBreakpoint"?: number; /** * If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the component passed into `ion-modal` will automatically be mounted when the modal is created. The component will remain mounted even when the modal is dismissed. However, the component will be destroyed when the modal is destroyed. This property is not reactive and should only be used when initially creating a modal. Note: This feature only applies to inline modals in JavaScript frameworks such as Angular, React, and Vue. + * @default false */ "keepContentsMounted": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose": boolean; /** @@ -1815,6 +2052,7 @@ export namespace Components { "setCurrentBreakpoint": (breakpoint: number) => Promise; /** * If `true`, a backdrop will be displayed behind the modal. This property controls whether or not the backdrop darkens the screen when the modal is presented. It does not control whether or not the backdrop is active or present in the DOM. + * @default true */ "showBackdrop": boolean; /** @@ -1825,6 +2063,7 @@ export namespace Components { interface IonNav { /** * If `true`, the nav should animate the transition of components. + * @default true */ "animated": boolean; /** @@ -1963,6 +2202,7 @@ export namespace Components { "routerAnimation"?: AnimationBuilder; /** * The transition direction when navigating to another page. + * @default 'forward' */ "routerDirection": RouterDirection; } @@ -1986,10 +2226,12 @@ export namespace Components { interface IonPickerColumn { /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** * If `true`, the user cannot interact with the picker. + * @default false */ "disabled": boolean; /** @@ -1998,6 +2240,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, tapping the picker will reveal a number input keyboard that lets the user type in values for each picker column. This is useful when working with time pickers. + * @default false */ "numericInput": boolean; "scrollActiveItemIntoView": (smooth?: boolean) => Promise; @@ -2017,10 +2260,12 @@ export namespace Components { interface IonPickerColumnOption { /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** * If `true`, the user cannot interact with the picker column option. + * @default false */ "disabled": boolean; /** @@ -2031,18 +2276,22 @@ export namespace Components { interface IonPickerLegacy { /** * If `true`, the picker will animate. + * @default true */ "animated": boolean; /** * If `true`, the picker will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss": boolean; /** * Array of buttons to be displayed at the top of the picker. + * @default [] */ "buttons": PickerButton[]; /** * Array of columns to be displayed in the picker. + * @default [] */ "columns": PickerColumn[]; /** @@ -2058,6 +2307,7 @@ export namespace Components { "dismiss": (data?: any, role?: string) => Promise; /** * Number of milliseconds to wait before dismissing the picker. + * @default 0 */ "duration": number; /** @@ -2069,6 +2319,9 @@ export namespace Components { * @param name The name of the column. */ "getColumn": (name: string) => Promise; + /** + * @default false + */ "hasController": boolean; /** * Additional attributes to pass to the picker. @@ -2076,10 +2329,12 @@ export namespace Components { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the picker will open. If `false`, the picker will close. Use this if you need finer grained control over presentation, otherwise just use the pickerController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the picker dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose": boolean; /** @@ -2105,6 +2360,7 @@ export namespace Components { "present": () => Promise; /** * If `true`, a backdrop will be displayed behind the picker. + * @default true */ "showBackdrop": boolean; /** @@ -2125,14 +2381,17 @@ export namespace Components { "alignment"?: PositionAlign; /** * If `true`, the popover will animate. + * @default true */ "animated": boolean; /** * If `true`, the popover will display an arrow that points at the `reference` when running in `ios` mode. Does not apply in `md` mode. + * @default true */ "arrow": boolean; /** * If `true`, the popover will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss": boolean; /** @@ -2157,6 +2416,7 @@ export namespace Components { "dismiss": (data?: any, role?: string, dismissParentPopover?: boolean) => Promise; /** * If `true`, the popover will be automatically dismissed when the content has been clicked. + * @default false */ "dismissOnSelect": boolean; /** @@ -2169,9 +2429,13 @@ export namespace Components { "event": any; /** * If `true`, focus will not be allowed to move outside of this overlay. If `false`, focus will be allowed to move outside of the overlay. In most scenarios this property should remain set to `true`. Setting this property to `false` can cause severe accessibility issues as users relying on assistive technologies may be able to move focus into a confusing state. We recommend only setting this to `false` when absolutely necessary. Developers may want to consider disabling focus trapping if this overlay presents a non-Ionic overlay from a 3rd party library. Developers would disable focus trapping on the Ionic overlay when presenting the 3rd party overlay and then re-enable focus trapping when dismissing the 3rd party overlay and moving focus back to the Ionic overlay. + * @default true */ "focusTrap": boolean; "getParentPopover": () => Promise; + /** + * @default false + */ "hasController": boolean; /** * Additional attributes to pass to the popover. @@ -2179,16 +2443,22 @@ export namespace Components { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the component passed into `ion-popover` will automatically be mounted when the popover is created. The component will remain mounted even when the popover is dismissed. However, the component will be destroyed when the popover is destroyed. This property is not reactive and should only be used when initially creating a popover. Note: This feature only applies to inline popovers in JavaScript frameworks such as Angular, React, and Vue. + * @default false */ "keepContentsMounted": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose": boolean; + /** + * @default false + */ "keyboardEvents": boolean; /** * Animation to use when the popover is dismissed. @@ -2217,22 +2487,27 @@ export namespace Components { "presentFromTrigger": (event?: any, focusDescendant?: boolean) => Promise; /** * Describes what to position the popover relative to. If `"trigger"`, the popover will be positioned relative to the trigger button. If passing in an event, this is determined via event.target. If `"event"`, the popover will be positioned relative to the x/y coordinates of the trigger action. If passing in an event, this is determined via event.clientX and event.clientY. + * @default 'trigger' */ "reference": PositionReference; /** * If `true`, a backdrop will be displayed behind the popover. This property controls whether or not the backdrop darkens the screen when the popover is presented. It does not control whether or not the backdrop is active or present in the DOM. + * @default true */ "showBackdrop": boolean; /** * Describes which side of the `reference` point to position the popover on. The `"start"` and `"end"` values are RTL-aware, and the `"left"` and `"right"` values are not. + * @default 'bottom' */ "side": PositionSide; /** * Describes how to calculate the popover width. If `"cover"`, the popover width will match the width of the trigger. If `"auto"`, the popover width will be set to a static default value. + * @default 'auto' */ "size": PopoverSize; /** * If `true`, the popover will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; /** @@ -2241,12 +2516,14 @@ export namespace Components { "trigger": string | undefined; /** * Describes what kind of interaction with the trigger that should cause the popover to open. Does not apply when the `trigger` property is `undefined`. If `"click"`, the popover will be presented when the trigger is left clicked. If `"hover"`, the popover will be presented when a pointer hovers over the trigger. If `"context-menu"`, the popover will be presented when the trigger is right clicked on desktop and long pressed on mobile. This will also prevent your device's normal context menu from appearing. + * @default 'click' */ "triggerAction": TriggerAction; } interface IonProgressBar { /** * If the buffer and value are smaller than 1, the buffer circles will show. The buffer should be between [0, 1]. + * @default 1 */ "buffer": number; /** @@ -2259,14 +2536,17 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If true, reverse the progress bar direction. + * @default false */ "reversed": boolean; /** * The state of the progress bar, based on if the time the process takes is known or not. Default options are: `"determinate"` (no animation), `"indeterminate"` (animate from left to right). + * @default 'determinate' */ "type": 'determinate' | 'indeterminate'; /** * The value determines how much of the active bar should display when the `type` is `"determinate"`. The value should be between [0, 1]. + * @default 0 */ "value": number; } @@ -2281,6 +2561,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the radio. + * @default false */ "disabled": boolean; /** @@ -2289,6 +2570,7 @@ export namespace Components { "justify"?: 'start' | 'end' | 'space-between'; /** * Where to place the label relative to the radio. `"start"`: The label will appear to the left of the radio in LTR and to the right in RTL. `"end"`: The label will appear to the right of the radio in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the radio regardless of the direction. The alignment of the label can be controlled with the `alignment` property. + * @default 'start' */ "labelPlacement": 'start' | 'end' | 'fixed' | 'stacked'; /** @@ -2297,6 +2579,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; "setButtonTabindex": (value: number) => Promise; @@ -2309,6 +2592,7 @@ export namespace Components { interface IonRadioGroup { /** * If `true`, the radios can be deselected. + * @default false */ "allowEmptySelection": boolean; /** @@ -2325,6 +2609,7 @@ export namespace Components { "helperText"?: string; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; "setFocus": () => Promise; @@ -2348,10 +2633,12 @@ export namespace Components { "debounce"?: number; /** * If `true`, the user cannot interact with the range. + * @default false */ "disabled": boolean; /** * Show two knobs. + * @default false */ "dualKnobs": boolean; /** @@ -2360,14 +2647,17 @@ export namespace Components { "label"?: string; /** * Where to place the label relative to the range. `"start"`: The label will appear to the left of the range in LTR and to the right in RTL. `"end"`: The label will appear to the right of the range in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the range regardless of the direction. + * @default 'start' */ "labelPlacement": 'start' | 'end' | 'fixed' | 'stacked'; /** * Maximum integer value of the range. + * @default 100 */ "max": number; /** * Minimum integer value of the range. + * @default 0 */ "min": number; /** @@ -2376,30 +2666,37 @@ export namespace Components { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.rangeId */ "name": string; /** * If `true`, a pin with integer value is shown when the knob is pressed. + * @default false */ "pin": boolean; /** * A callback used to format the pin text. By default the pin text is set to `Math.round(value)`. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + * @default (value: number): number => Math.round(value) */ "pinFormatter": PinFormatter; /** * If `true`, the knob snaps to tick marks evenly spaced based on the step property value. + * @default false */ "snaps": boolean; /** * Specifies the value granularity. + * @default 1 */ "step": number; /** * If `true`, tick marks are displayed based on the step value. Only applies when `snaps` is `true`. + * @default true */ "ticks": boolean; /** * the value of the range. + * @default 0 */ "value": RangeValue; } @@ -2410,6 +2707,7 @@ export namespace Components { "cancel": () => Promise; /** * Time it takes to close the refresher. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default '280ms' */ "closeDuration": string; /** @@ -2418,6 +2716,7 @@ export namespace Components { "complete": () => Promise; /** * If `true`, the refresher will be hidden. + * @default false */ "disabled": boolean; /** @@ -2430,18 +2729,22 @@ export namespace Components { "mode"?: "ios" | "md"; /** * How much to multiply the pull speed by. To slow the pull animation down, pass a number less than `1`. To speed up the pull, pass a number greater than `1`. The default value is `1` which is equal to the speed of the cursor. If a negative value is passed in, the factor will be `1` instead. For example: If the value passed is `1.2` and the content is dragged by `10` pixels, instead of `10` pixels the content will be pulled by `12` pixels (an increase of 20 percent). If the value passed is `0.8`, the dragged amount will be `8` pixels, less than the amount the cursor has moved. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default 1 */ "pullFactor": number; /** * The maximum distance of the pull until the refresher will automatically go into the `refreshing` state. Defaults to the result of `pullMin + 60`. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default this.pullMin + 60 */ "pullMax": number; /** * The minimum distance the user must pull down until the refresher will go into the `refreshing` state. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default 60 */ "pullMin": number; /** * Time it takes the refresher to snap back to the `refreshing` state. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default '280ms' */ "snapbackDuration": string; } @@ -2473,6 +2776,7 @@ export namespace Components { "complete": (listOrReorder?: boolean | any[]) => Promise; /** * If `true`, the reorder will be hidden. + * @default true */ "disabled": boolean; } @@ -2485,6 +2789,7 @@ export namespace Components { "addRipple": (x: number, y: number) => Promise<() => void>; /** * Sets the type of ripple-effect: - `bounded`: the ripple effect expands from the user's click position - `unbounded`: the ripple effect expands from the center of the button and overflows the container. NOTE: Surfaces for bounded ripples should have the overflow property set to hidden, while surfaces for unbounded ripples should have it set to visible. + * @default 'bounded' */ "type": 'bounded' | 'unbounded'; } @@ -2507,6 +2812,7 @@ export namespace Components { "componentProps"?: { [key: string]: any }; /** * Relative path that needs to match in order for this route to apply. Accepts paths similar to expressjs so that you can define parameters in the url /foo/:bar where bar would be available in incoming props. + * @default '' */ "url": string; } @@ -2536,10 +2842,12 @@ export namespace Components { "push": (path: string, direction?: RouterDirection, animation?: AnimationBuilder) => Promise; /** * The root path to use when matching URLs. By default, this is set to "/", but you can specify an alternate prefix for all URL paths. + * @default '/' */ "root": string; /** * The router can work in two "modes": - With hash: `/index.html#/path/to/page` - Without hash: `/path/to/page` Using one or another might depend in the requirements of your app and/or where it's deployed. Usually "hash-less" navigation works better for SEO and it's more user friendly too, but it might requires additional server-side configuration in order to properly work. On the other side hash-navigation is much easier to deploy, it even works over the file protocol. By default, this property is `true`, change to `false` to allow hash-less URLs. + * @default true */ "useHash": boolean; } @@ -2562,6 +2870,7 @@ export namespace Components { "routerAnimation": AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection": RouterDirection; /** @@ -2572,6 +2881,7 @@ export namespace Components { interface IonRouterOutlet { /** * If `true`, the router-outlet should animate the transition of components. + * @default true */ "animated": boolean; /** @@ -2583,6 +2893,7 @@ export namespace Components { "getRouteId": () => Promise; /** * The mode determines which platform styles to use. + * @default getIonMode(this) */ "mode": "ios" | "md"; "setRouteId": (id: string, params: ComponentProps | undefined, direction: RouterDirection, animation?: AnimationBuilder) => Promise; @@ -2593,26 +2904,32 @@ export namespace Components { interface IonSearchbar { /** * If `true`, enable searchbar animation. + * @default false */ "animated": boolean; /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'off' */ "autocapitalize": string; /** * Set the input's autocomplete property. + * @default 'off' */ "autocomplete": AutocompleteTypes; /** * Set the input's autocorrect property. + * @default 'off' */ "autocorrect": 'on' | 'off'; /** * Set the cancel button icon. Only applies to `md` mode. Defaults to `arrow-back-sharp`. + * @default config.get('backButtonIcon', arrowBackSharp) as string */ "cancelButtonIcon": string; /** * Set the cancel button text. Only applies to `ios` mode. + * @default 'Cancel' */ "cancelButtonText": string; /** @@ -2629,6 +2946,7 @@ export namespace Components { "debounce"?: number; /** * If `true`, the user cannot interact with the input. + * @default false */ "disabled": boolean; /** @@ -2657,10 +2975,12 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If used in a form, set the name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) + * @default 'Search' */ "placeholder": string; /** @@ -2673,22 +2993,27 @@ export namespace Components { "setFocus": () => Promise; /** * Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state. + * @default 'never' */ "showCancelButton": 'never' | 'focus' | 'always'; /** * Sets the behavior for the clear button. Defaults to `"focus"`. Setting to `"focus"` shows the clear button on focus if the input is not empty. Setting to `"never"` hides the clear button. Setting to `"always"` shows the clear button regardless of focus state, but only if the input is not empty. + * @default 'always' */ "showClearButton": 'never' | 'focus' | 'always'; /** * If `true`, enable spellcheck on the input. + * @default false */ "spellcheck": boolean; /** * Set the type of the input. + * @default 'search' */ "type": 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url'; /** * the value of the searchbar. + * @default '' */ "value"?: string | null; } @@ -2699,6 +3024,7 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the segment. + * @default false */ "disabled": boolean; /** @@ -2707,14 +3033,17 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons. + * @default false */ "scrollable": boolean; /** * If `true`, navigating to an `ion-segment-button` with the keyboard will focus and select the element. If `false`, keyboard navigation will only focus the `ion-segment-button` element. + * @default false */ "selectOnFocus": boolean; /** * If `true`, users will be able to swipe between segment buttons to activate them. + * @default true */ "swipeGesture": boolean; /** @@ -2729,10 +3058,12 @@ export namespace Components { "contentId"?: string; /** * If `true`, the user cannot interact with the segment button. + * @default false */ "disabled": boolean; /** * Set the layout of the text and icon in the segment. + * @default 'icon-top' */ "layout"?: SegmentButtonLayout; /** @@ -2742,10 +3073,12 @@ export namespace Components { "setFocus": () => Promise; /** * The type of the button. + * @default 'button' */ "type": 'submit' | 'reset' | 'button'; /** * The value of the segment button. + * @default 'ion-sb-' + ids++ */ "value": SegmentValue; } @@ -2754,6 +3087,7 @@ export namespace Components { interface IonSegmentView { /** * If `true`, the segment view cannot be interacted with. + * @default false */ "disabled": boolean; /** @@ -2765,6 +3099,7 @@ export namespace Components { interface IonSelect { /** * The text to display on the cancel button. + * @default 'Cancel' */ "cancelText": string; /** @@ -2777,6 +3112,7 @@ export namespace Components { "compareWith"?: string | SelectCompareFn | null; /** * If `true`, the user cannot interact with the select. + * @default false */ "disabled": boolean; /** @@ -2797,10 +3133,12 @@ export namespace Components { "helperText"?: string; /** * The interface the select should use: `action-sheet`, `popover`, `alert`, or `modal`. + * @default 'alert' */ "interface": SelectInterface; /** * Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](./alert), the [ion-action-sheet docs](./action-sheet), the [ion-popover docs](./popover), and the [ion-modal docs](./modal) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface. + * @default {} */ "interfaceOptions": any; /** @@ -2813,6 +3151,7 @@ export namespace Components { "label"?: string; /** * Where to place the label relative to the select. `"start"`: The label will appear to the left of the select in LTR and to the right in RTL. `"end"`: The label will appear to the right of the select in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the select when the select is focused or it has a value. Otherwise it will appear on top of the select. `"stacked"`: The label will appear smaller and above the select regardless even when the select is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). When using `"floating"` or `"stacked"` we recommend initializing the select with either a `value` or a `placeholder`. + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'floating' | 'stacked' | 'fixed'; /** @@ -2821,14 +3160,17 @@ export namespace Components { "mode"?: "ios" | "md"; /** * If `true`, the select can accept multiple values. + * @default false */ "multiple": boolean; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** * The text to display on the ok button. + * @default 'OK' */ "okText": string; /** @@ -2842,6 +3184,7 @@ export namespace Components { "placeholder"?: string; /** * If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid. + * @default false */ "required": boolean; /** @@ -2864,11 +3207,15 @@ export namespace Components { interface IonSelectModal { "header"?: string; "multiple"?: boolean; + /** + * @default [] + */ "options": SelectModalOption[]; } interface IonSelectOption { /** * If `true`, the user cannot interact with the select option. This property does not apply when `interface="action-sheet"` as `ion-action-sheet` does not allow for disabled buttons. + * @default false */ "disabled": boolean; /** @@ -2891,6 +3238,7 @@ export namespace Components { "multiple"?: boolean; /** * An array of options for the popover + * @default [] */ "options": SelectPopoverOption[]; /** @@ -2901,6 +3249,7 @@ export namespace Components { interface IonSkeletonText { /** * If `true`, the skeleton text will animate. + * @default false */ "animated": boolean; } @@ -2919,6 +3268,7 @@ export namespace Components { "name"?: SpinnerTypes; /** * If `true`, the spinner's animation will be paused. + * @default false */ "paused": boolean; } @@ -2929,15 +3279,20 @@ export namespace Components { "contentId"?: string; /** * If `true`, the split pane will be hidden. + * @default false */ "disabled": boolean; "isVisible": () => Promise; /** * When the split-pane should be shown. Can be a CSS media query expression, or a shortcut expression. Can also be a boolean expression. + * @default QUERY['lg'] */ "when": string | boolean; } interface IonTab { + /** + * @default false + */ "active": boolean; /** * The component to display inside of the tab. @@ -2968,12 +3323,14 @@ export namespace Components { "selectedTab"?: string; /** * If `true`, the tab bar will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; } interface IonTabButton { /** * If `true`, the user cannot interact with the tab button. + * @default false */ "disabled": boolean; /** @@ -2998,6 +3355,7 @@ export namespace Components { "rel": string | undefined; /** * The selected tab component + * @default false */ "selected": boolean; /** @@ -3026,6 +3384,9 @@ export namespace Components { */ "select": (tab: string | HTMLIonTabElement) => Promise; "setRouteId": (id: string) => Promise; + /** + * @default false + */ "useRouter": boolean; } interface IonText { @@ -3041,18 +3402,22 @@ export namespace Components { interface IonTextarea { /** * If `true`, the textarea container will grow and shrink based on the contents of the textarea. + * @default false */ "autoGrow": boolean; /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'none' */ "autocapitalize": string; /** * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. + * @default false */ "autofocus": boolean; /** * If `true`, the value will be cleared after focus upon edit. + * @default false */ "clearOnEdit": boolean; /** @@ -3065,6 +3430,7 @@ export namespace Components { "cols"?: number; /** * If `true`, a character counter will display the ratio of characters used and the total character limit. Developers must also set the `maxlength` property for the counter to be calculated correctly. + * @default false */ "counter": boolean; /** @@ -3077,6 +3443,7 @@ export namespace Components { "debounce"?: number; /** * If `true`, the user cannot interact with the textarea. + * @default false */ "disabled": boolean; /** @@ -3109,6 +3476,7 @@ export namespace Components { "label"?: string; /** * Where to place the label relative to the textarea. `"start"`: The label will appear to the left of the textarea in LTR and to the right in RTL. `"end"`: The label will appear to the right of the textarea in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the textarea when the textarea is focused or it has a value. Otherwise it will appear on top of the textarea. `"stacked"`: The label will appear smaller and above the textarea regardless even when the textarea is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). + * @default 'start' */ "labelPlacement": 'start' | 'end' | 'floating' | 'stacked' | 'fixed'; /** @@ -3125,6 +3493,7 @@ export namespace Components { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** @@ -3133,10 +3502,12 @@ export namespace Components { "placeholder"?: string; /** * If `true`, the user cannot modify the value. + * @default false */ "readonly": boolean; /** * If `true`, the user must fill in a value before submitting a form. + * @default false */ "required": boolean; /** @@ -3153,10 +3524,12 @@ export namespace Components { "shape"?: 'round'; /** * If `true`, the element will have its spelling and grammar checked. + * @default false */ "spellcheck": boolean; /** * The value of the textarea. + * @default '' */ "value"?: string | null; /** @@ -3179,6 +3552,7 @@ export namespace Components { interface IonToast { /** * If `true`, the toast will animate. + * @default true */ "animated": boolean; /** @@ -3202,12 +3576,16 @@ export namespace Components { "dismiss": (data?: any, role?: string) => Promise; /** * How many milliseconds to wait before hiding the toast. By default, it will show until `dismiss()` is called. + * @default config.getNumber('toastDuration', 0) */ "duration": number; /** * Animation to use when the toast is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController": boolean; /** * Header to be shown in the toast. @@ -3223,14 +3601,17 @@ export namespace Components { "icon"?: string; /** * If `true`, the toast will open. If `false`, the toast will close. Use this if you need finer grained control over presentation, otherwise just use the toastController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the toast dismisses. You will need to do that in your code. + * @default false */ "isOpen": boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default false */ "keyboardClose": boolean; /** * Defines how the message and buttons are laid out in the toast. 'baseline': The message and the buttons will appear on the same line. Message text may wrap within the message container. 'stacked': The buttons containers and message will stack on top of each other. Use this if you have long text in your buttons. + * @default 'baseline' */ "layout": ToastLayout; /** @@ -3256,6 +3637,7 @@ export namespace Components { "overlayIndex": number; /** * The starting position of the toast on the screen. Can be tweaked further using the `positionAnchor` property. + * @default 'bottom' */ "position": ToastPosition; /** @@ -3272,6 +3654,7 @@ export namespace Components { "swipeGesture"?: ToastSwipeGestureDirection; /** * If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent": boolean; /** @@ -3286,6 +3669,7 @@ export namespace Components { "alignment"?: 'start' | 'center'; /** * If `true`, the toggle is selected. + * @default false */ "checked": boolean; /** @@ -3294,10 +3678,12 @@ export namespace Components { "color"?: Color; /** * If `true`, the user cannot interact with the toggle. + * @default false */ "disabled": boolean; /** * Enables the on/off accessibility switch labels within the toggle. + * @default config.get('toggleOnOffLabels') */ "enableOnOffLabels": boolean | undefined; /** @@ -3314,6 +3700,7 @@ export namespace Components { "justify"?: 'start' | 'end' | 'space-between'; /** * Where to place the label relative to the input. `"start"`: The label will appear to the left of the toggle in LTR and to the right in RTL. `"end"`: The label will appear to the right of the toggle in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the toggle regardless of the direction. The alignment of the label can be controlled with the `alignment` property. + * @default 'start' */ "labelPlacement": 'start' | 'end' | 'fixed' | 'stacked'; /** @@ -3322,14 +3709,17 @@ export namespace Components { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name": string; /** * If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid. + * @default false */ "required": boolean; /** * The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a ``, it's only used when the toggle participates in a native ``. + * @default 'on' */ "value"?: string | null; } @@ -3400,6 +3790,10 @@ export interface IonInputCustomEvent extends CustomEvent { detail: T; target: HTMLIonInputElement; } +export interface IonInputOtpCustomEvent extends CustomEvent { + detail: T; + target: HTMLIonInputOtpElement; +} export interface IonItemOptionsCustomEvent extends CustomEvent { detail: T; target: HTMLIonItemOptionsElement; @@ -3929,6 +4323,27 @@ declare global { prototype: HTMLIonInputElement; new (): HTMLIonInputElement; }; + interface HTMLIonInputOtpElementEventMap { + "ionInput": InputOtpInputEventDetail; + "ionChange": InputOtpChangeEventDetail; + "ionComplete": InputOtpCompleteEventDetail; + "ionBlur": FocusEvent; + "ionFocus": FocusEvent; + } + interface HTMLIonInputOtpElement extends Components.IonInputOtp, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLIonInputOtpElement, ev: IonInputOtpCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLIonInputOtpElement, ev: IonInputOtpCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLIonInputOtpElement: { + prototype: HTMLIonInputOtpElement; + new (): HTMLIonInputOtpElement; + }; interface HTMLIonInputPasswordToggleElement extends Components.IonInputPasswordToggle, HTMLStencilElement { } var HTMLIonInputPasswordToggleElement: { @@ -4788,6 +5203,7 @@ declare global { "ion-infinite-scroll": HTMLIonInfiniteScrollElement; "ion-infinite-scroll-content": HTMLIonInfiniteScrollContentElement; "ion-input": HTMLIonInputElement; + "ion-input-otp": HTMLIonInputOtpElement; "ion-input-password-toggle": HTMLIonInputPasswordToggleElement; "ion-item": HTMLIonItemElement; "ion-item-divider": HTMLIonItemDividerElement; @@ -4856,6 +5272,7 @@ declare namespace LocalJSX { interface IonAccordion { /** * If `true`, the accordion cannot be interacted with. + * @default false */ "disabled"?: boolean; /** @@ -4864,32 +5281,39 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If `true`, the accordion cannot be interacted with, but does not alter the opacity. + * @default false */ "readonly"?: boolean; /** * The toggle icon to use. This icon will be rotated when the accordion is expanded or collapsed. + * @default chevronDown */ "toggleIcon"?: string; /** * The slot inside of `ion-item` to place the toggle icon. Defaults to `"end"`. + * @default 'end' */ "toggleIconSlot"?: 'start' | 'end'; /** * The value of the accordion. Defaults to an autogenerated value. + * @default `ion-accordion-${accordionIds++}` */ "value"?: string; } interface IonAccordionGroup { /** * If `true`, all accordions inside of the accordion group will animate when expanding or collapsing. + * @default true */ "animated"?: boolean; /** * If `true`, the accordion group cannot be interacted with. + * @default false */ "disabled"?: boolean; /** * Describes the expansion behavior for each accordion. Possible values are `"compact"` and `"inset"`. Defaults to `"compact"`. + * @default 'compact' */ "expand"?: 'compact' | 'inset'; /** @@ -4910,6 +5334,7 @@ declare namespace LocalJSX { "onIonValueChange"?: (event: IonAccordionGroupCustomEvent) => void; /** * If `true`, the accordion group cannot be interacted with, but does not alter the opacity. + * @default false */ "readonly"?: boolean; /** @@ -4920,14 +5345,17 @@ declare namespace LocalJSX { interface IonActionSheet { /** * If `true`, the action sheet will animate. + * @default true */ "animated"?: boolean; /** * If `true`, the action sheet will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss"?: boolean; /** * An array of buttons for the action sheet. + * @default [] */ "buttons"?: (ActionSheetButton | string)[]; /** @@ -4939,6 +5367,9 @@ declare namespace LocalJSX { * Animation to use when the action sheet is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController"?: boolean; /** * Title for the action sheet. @@ -4950,10 +5381,12 @@ declare namespace LocalJSX { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the action sheet will open. If `false`, the action sheet will close. Use this if you need finer grained control over presentation, otherwise just use the actionSheetController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the action sheet dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose"?: boolean; /** @@ -5003,6 +5436,7 @@ declare namespace LocalJSX { "subHeader"?: string; /** * If `true`, the action sheet will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; /** @@ -5013,14 +5447,17 @@ declare namespace LocalJSX { interface IonAlert { /** * If `true`, the alert will animate. + * @default true */ "animated"?: boolean; /** * If `true`, the alert will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss"?: boolean; /** * Array of buttons to be added to the alert. + * @default [] */ "buttons"?: (AlertButton | string)[]; /** @@ -5032,6 +5469,9 @@ declare namespace LocalJSX { * Animation to use when the alert is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController"?: boolean; /** * The main title in the heading of the alert. @@ -5043,14 +5483,17 @@ declare namespace LocalJSX { "htmlAttributes"?: { [key: string]: any }; /** * Array of input to show in the alert. + * @default [] */ "inputs"?: AlertInput[]; /** * If `true`, the alert will open. If `false`, the alert will close. Use this if you need finer grained control over presentation, otherwise just use the alertController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the alert dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose"?: boolean; /** @@ -5104,6 +5547,7 @@ declare namespace LocalJSX { "subHeader"?: string; /** * If `true`, the alert will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; /** @@ -5126,6 +5570,7 @@ declare namespace LocalJSX { "defaultHref"?: string; /** * If `true`, the user cannot interact with the button. + * @default false */ "disabled"?: boolean; /** @@ -5146,6 +5591,7 @@ declare namespace LocalJSX { "text"?: string | null; /** * The type of the button. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } @@ -5156,14 +5602,17 @@ declare namespace LocalJSX { "onIonBackdropTap"?: (event: IonBackdropCustomEvent) => void; /** * If `true`, the backdrop will stop propagation on tap. + * @default true */ "stopPropagation"?: boolean; /** * If `true`, the backdrop will can be clicked and will emit the `ionBackdropTap` event. + * @default true */ "tappable"?: boolean; /** * If `true`, the backdrop will be visible. + * @default true */ "visible"?: boolean; } @@ -5180,8 +5629,12 @@ declare namespace LocalJSX { interface IonBreadcrumb { /** * If `true`, the breadcrumb will take on a different look to show that it is the currently active breadcrumb. Defaults to `true` for the last breadcrumb if it is not set on any. + * @default false */ "active"?: boolean; + /** + * @default false + */ "collapsed"?: boolean; /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). @@ -5189,6 +5642,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the breadcrumb. + * @default false */ "disabled"?: boolean; /** @@ -5226,6 +5680,7 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection"?: RouterDirection; /** @@ -5245,10 +5700,12 @@ declare namespace LocalJSX { "color"?: Color; /** * The number of breadcrumbs to show after the collapsed indicator. If `itemsBeforeCollapse` + `itemsAfterCollapse` is greater than `maxItems`, the breadcrumbs will not be collapsed. + * @default 1 */ "itemsAfterCollapse"?: number; /** * The number of breadcrumbs to show before the collapsed indicator. If `itemsBeforeCollapse` + `itemsAfterCollapse` is greater than `maxItems`, the breadcrumbs will not be collapsed. + * @default 1 */ "itemsBeforeCollapse"?: number; /** @@ -5267,6 +5724,7 @@ declare namespace LocalJSX { interface IonButton { /** * The type of button. + * @default 'button' */ "buttonType"?: string; /** @@ -5275,6 +5733,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the button. + * @default false */ "disabled"?: boolean; /** @@ -5319,6 +5778,7 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection"?: RouterDirection; /** @@ -5331,6 +5791,7 @@ declare namespace LocalJSX { "size"?: 'small' | 'default' | 'large'; /** * If `true`, activates a button with a heavier font weight. + * @default false */ "strong"?: boolean; /** @@ -5339,18 +5800,21 @@ declare namespace LocalJSX { "target"?: string | undefined; /** * The type of the button. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } interface IonButtons { /** * If true, buttons will disappear when its parent toolbar has fully collapsed if the toolbar is not the first toolbar. If the toolbar is the first toolbar, the buttons will be hidden and will only be shown once all toolbars have fully collapsed. Only applies in `ios` mode with `collapse` set to `true` on `ion-header`. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles) + * @default false */ "collapse"?: boolean; } interface IonCard { /** * If `true`, a button tag will be rendered and the card will be tappable. + * @default false */ "button"?: boolean; /** @@ -5359,6 +5823,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the card. + * @default false */ "disabled"?: boolean; /** @@ -5383,6 +5848,7 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection"?: RouterDirection; /** @@ -5391,6 +5857,7 @@ declare namespace LocalJSX { "target"?: string | undefined; /** * The type of the button. Only used when an `onclick` or `button` property is present. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } @@ -5411,6 +5878,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If `true`, the card header will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; } @@ -5441,6 +5909,7 @@ declare namespace LocalJSX { "alignment"?: 'start' | 'center'; /** * If `true`, the checkbox is selected. + * @default false */ "checked"?: boolean; /** @@ -5449,6 +5918,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the checkbox. + * @default false */ "disabled"?: boolean; /** @@ -5461,6 +5931,7 @@ declare namespace LocalJSX { "helperText"?: string; /** * If `true`, the checkbox will visually appear as indeterminate. + * @default false */ "indeterminate"?: boolean; /** @@ -5469,6 +5940,7 @@ declare namespace LocalJSX { "justify"?: 'start' | 'end' | 'space-between'; /** * Where to place the label relative to the checkbox. `"start"`: The label will appear to the left of the checkbox in LTR and to the right in RTL. `"end"`: The label will appear to the right of the checkbox in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the checkbox regardless of the direction. The alignment of the label can be controlled with the `alignment` property. + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'fixed' | 'stacked'; /** @@ -5477,6 +5949,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -5493,10 +5966,12 @@ declare namespace LocalJSX { "onIonFocus"?: (event: IonCheckboxCustomEvent) => void; /** * If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid. + * @default false */ "required"?: boolean; /** * The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an ``, it's only used when the checkbox participates in a native ``. + * @default 'on' */ "value"?: any | null; } @@ -5507,6 +5982,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the chip. + * @default false */ "disabled"?: boolean; /** @@ -5515,6 +5991,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * Display an outline style button. + * @default false */ "outline"?: boolean; } @@ -5623,6 +6100,7 @@ declare namespace LocalJSX { "color"?: Color; /** * Controls where the fixed content is placed relative to the main content in the DOM. This can be used to control the order in which fixed elements receive keyboard focus. For example, if a FAB in the fixed slot should receive keyboard focus before the main page content, set this property to `'before'`. + * @default 'after' */ "fixedSlotPlacement"?: 'after' | 'before'; /** @@ -5631,6 +6109,7 @@ declare namespace LocalJSX { "forceOverscroll"?: boolean; /** * If `true`, the content will scroll behind the headers and footers. This effect can easily be seen by setting the toolbar to transparent. + * @default false */ "fullscreen"?: boolean; /** @@ -5647,28 +6126,34 @@ declare namespace LocalJSX { "onIonScrollStart"?: (event: IonContentCustomEvent) => void; /** * Because of performance reasons, ionScroll events are disabled by default, in order to enable them and start listening from (ionScroll), set this property to `true`. + * @default false */ "scrollEvents"?: boolean; /** * If you want to enable the content scrolling in the X axis, set this property to `true`. + * @default false */ "scrollX"?: boolean; /** * If you want to disable the content scrolling in the Y axis, set this property to `false`. + * @default true */ "scrollY"?: boolean; } interface IonDatetime { /** * The text to display on the picker's cancel button. + * @default 'Cancel' */ "cancelText"?: string; /** * The text to display on the picker's "Clear" button. + * @default 'Clear' */ "clearText"?: string; /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** @@ -5677,14 +6162,17 @@ declare namespace LocalJSX { "dayValues"?: number[] | number | string; /** * If `true`, the user cannot interact with the datetime. + * @default false */ "disabled"?: boolean; /** * The text to display on the picker's "Done" button. + * @default 'Done' */ "doneText"?: string; /** * The first day of the week to use for `ion-datetime`. The default value is `0` and represents Sunday. + * @default 0 */ "firstDayOfWeek"?: number; /** @@ -5709,6 +6197,7 @@ declare namespace LocalJSX { "isDateEnabled"?: (dateIsoString: string) => boolean; /** * The locale to use for `ion-datetime`. This impacts month and day name formatting. The `"default"` value refers to the default locale set by your device. + * @default 'default' */ "locale"?: string; /** @@ -5733,10 +6222,12 @@ declare namespace LocalJSX { "monthValues"?: number[] | number | string; /** * If `true`, multiple dates can be selected at once. Only applies to `presentation="date"` and `preferWheel="false"`. + * @default false */ "multiple"?: boolean; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -5769,34 +6260,47 @@ declare namespace LocalJSX { "onIonValueChange"?: (event: IonDatetimeCustomEvent) => void; /** * If `true`, a wheel picker will be rendered instead of a calendar grid where possible. If `false`, a calendar grid will be rendered instead of a wheel picker where possible. A wheel picker can be rendered instead of a grid when `presentation` is one of the following values: `"date"`, `"date-time"`, or `"time-date"`. A wheel picker will always be rendered regardless of the `preferWheel` value when `presentation` is one of the following values: `"time"`, `"month"`, `"month-year"`, or `"year"`. + * @default false */ "preferWheel"?: boolean; /** * Which values you want to select. `"date"` will show a calendar picker to select the month, day, and year. `"time"` will show a time picker to select the hour, minute, and (optionally) AM/PM. `"date-time"` will show the date picker first and time picker second. `"time-date"` will show the time picker first and date picker second. + * @default 'date-time' */ "presentation"?: DatetimePresentation; /** * If `true`, the datetime appears normal but the selected date cannot be changed. + * @default false */ "readonly"?: boolean; + /** + * If `true`, the datetime calendar displays a six-week (42-day) layout, including days from the previous and next months to fill the grid. These adjacent days are selectable unless disabled. + * @default false + */ + "showAdjacentDays"?: boolean; /** * If `true`, a "Clear" button will be rendered alongside the default "Cancel" and "OK" buttons at the bottom of the `ion-datetime` component. Developers can also use the `button` slot if they want to customize these buttons. If custom buttons are set in the `button` slot then the default buttons will not be rendered. + * @default false */ "showClearButton"?: boolean; /** * If `true`, the default "Cancel" and "OK" buttons will be rendered at the bottom of the `ion-datetime` component. Developers can also use the `button` slot if they want to customize these buttons. If custom buttons are set in the `button` slot then the default buttons will not be rendered. + * @default false */ "showDefaultButtons"?: boolean; /** * If `true`, the default "Time" label will be rendered for the time selector of the `ion-datetime` component. Developers can also use the `time-label` slot if they want to customize this label. If a custom label is set in the `time-label` slot then the default label will not be rendered. + * @default true */ "showDefaultTimeLabel"?: boolean; /** * If `true`, a header will be shown above the calendar picker. This will include both the slotted title, and the selected date. + * @default false */ "showDefaultTitle"?: boolean; /** * If `cover`, the `ion-datetime` will expand to cover the full width of its container. If `fixed`, the `ion-datetime` will have a fixed width. + * @default 'fixed' */ "size"?: 'cover' | 'fixed'; /** @@ -5815,6 +6319,7 @@ declare namespace LocalJSX { interface IonDatetimeButton { /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** @@ -5823,6 +6328,7 @@ declare namespace LocalJSX { "datetime"?: string; /** * If `true`, the user cannot interact with the button. + * @default false */ "disabled"?: boolean; /** @@ -5833,10 +6339,12 @@ declare namespace LocalJSX { interface IonFab { /** * If `true`, both the `ion-fab-button` and all `ion-fab-list` inside `ion-fab` will become active. That means `ion-fab-button` will become a `close` icon and `ion-fab-list` will become visible. + * @default false */ "activated"?: boolean; /** * If `true`, the fab will display on the edge of the header if `vertical` is `"top"`, and on the edge of the footer if it is `"bottom"`. Should be used with a `fixed` slot. + * @default false */ "edge"?: boolean; /** @@ -5851,10 +6359,12 @@ declare namespace LocalJSX { interface IonFabButton { /** * If `true`, the fab button will be show a close icon. + * @default false */ "activated"?: boolean; /** * The icon name to use for the close icon. This will appear when the fab button is pressed. Only applies if it is the main button inside of a fab containing a fab list. + * @default close */ "closeIcon"?: string; /** @@ -5863,6 +6373,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the fab button. + * @default false */ "disabled"?: boolean; /** @@ -5895,10 +6406,12 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection"?: RouterDirection; /** * If `true`, the fab button will show when in a fab-list. + * @default false */ "show"?: boolean; /** @@ -5911,20 +6424,24 @@ declare namespace LocalJSX { "target"?: string | undefined; /** * If `true`, the fab button will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; /** * The type of the button. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } interface IonFabList { /** * If `true`, the fab list will show all fab buttons in the list. + * @default false */ "activated"?: boolean; /** * The side the fab list will show on relative to the main fab button. + * @default 'bottom' */ "side"?: 'start' | 'end' | 'top' | 'bottom'; } @@ -5939,12 +6456,14 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If `true`, the footer will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). Note: In order to scroll content behind the footer, the `fullscreen` attribute needs to be set on the content. + * @default false */ "translucent"?: boolean; } interface IonGrid { /** * If `true`, the grid will have a fixed width based on the screen size. + * @default false */ "fixed"?: boolean; } @@ -5959,6 +6478,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If `true`, the header will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). Note: In order to scroll content behind the header, the `fullscreen` attribute needs to be set on the content. + * @default false */ "translucent"?: boolean; } @@ -5987,6 +6507,7 @@ declare namespace LocalJSX { interface IonInfiniteScroll { /** * If `true`, the infinite scroll will be hidden and scroll event listeners will be removed. Set this to true to disable the infinite scroll from actively trying to receive new data while scrolling. This is useful when it is known that there is no more data that can be added, and the infinite scroll is no longer needed. + * @default false */ "disabled"?: boolean; /** @@ -5995,10 +6516,12 @@ declare namespace LocalJSX { "onIonInfinite"?: (event: IonInfiniteScrollCustomEvent) => void; /** * The position of the infinite scroll element. The value can be either `top` or `bottom`. + * @default 'bottom' */ "position"?: 'top' | 'bottom'; /** * The threshold distance from the bottom of the content to call the `infinite` output event when scrolled. The threshold value can be either a percent, or in pixels. For example, use the value of `10%` for the `infinite` output event to get called when the user has scrolled 10% from the bottom of the page. Use the value `100px` when the scroll is within 100 pixels from the bottom of the page. + * @default '15%' */ "threshold"?: string; } @@ -6015,22 +6538,27 @@ declare namespace LocalJSX { interface IonInput { /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'off' */ "autocapitalize"?: string; /** * Indicates whether the value of the control can be automatically completed by the browser. + * @default 'off' */ "autocomplete"?: AutocompleteTypes; /** * Whether auto correction should be enabled when the user is entering/editing the text value. + * @default 'off' */ "autocorrect"?: 'on' | 'off'; /** * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. + * @default false */ "autofocus"?: boolean; /** * If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input. + * @default false */ "clearInput"?: boolean; /** @@ -6047,6 +6575,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, a character counter will display the ratio of characters used and the total character limit. Developers must also set the `maxlength` property for the counter to be calculated correctly. + * @default false */ "counter"?: boolean; /** @@ -6059,6 +6588,7 @@ declare namespace LocalJSX { "debounce"?: number; /** * If `true`, the user cannot interact with the input. + * @default false */ "disabled"?: boolean; /** @@ -6087,6 +6617,7 @@ declare namespace LocalJSX { "label"?: string; /** * Where to place the label relative to the input. `"start"`: The label will appear to the left of the input in LTR and to the right in RTL. `"end"`: The label will appear to the right of the input in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the input when the input is focused or it has a value. Otherwise it will appear on top of the input. `"stacked"`: The label will appear smaller and above the input regardless even when the input is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'floating' | 'stacked' | 'fixed'; /** @@ -6115,6 +6646,7 @@ declare namespace LocalJSX { "multiple"?: boolean; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -6143,10 +6675,12 @@ declare namespace LocalJSX { "placeholder"?: string; /** * If `true`, the user cannot modify the value. + * @default false */ "readonly"?: boolean; /** * If `true`, the user must fill in a value before submitting a form. + * @default false */ "required"?: boolean; /** @@ -6155,6 +6689,7 @@ declare namespace LocalJSX { "shape"?: 'round'; /** * If `true`, the element will have its spelling and grammar checked. + * @default false */ "spellcheck"?: boolean; /** @@ -6163,10 +6698,95 @@ declare namespace LocalJSX { "step"?: string; /** * The type of control to display. The default type is text. + * @default 'text' */ "type"?: TextFieldTypes; /** * The value of the input. + * @default '' + */ + "value"?: string | number | null; + } + interface IonInputOtp { + /** + * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'off' + */ + "autocapitalize"?: string; + /** + * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + */ + "color"?: Color; + /** + * If `true`, the user cannot interact with the input. + * @default false + */ + "disabled"?: boolean; + /** + * The fill for the input boxes. If `"solid"` the input boxes will have a background. If `"outline"` the input boxes will be transparent with a border. + * @default 'outline' + */ + "fill"?: 'outline' | 'solid'; + /** + * A hint to the browser for which keyboard to display. Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, `"email"`, `"numeric"`, `"decimal"`, and `"search"`. For numbers (type="number"): "numeric" For text (type="text"): "text" + */ + "inputmode"?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'; + /** + * The number of input boxes to display. + * @default 4 + */ + "length"?: number; + /** + * Emitted when the input group loses focus. + */ + "onIonBlur"?: (event: IonInputOtpCustomEvent) => void; + /** + * The `ionChange` event is fired when the user modifies the input's value. Unlike the `ionInput` event, the `ionChange` event is only fired when changes are committed, not as the user types. The `ionChange` event fires when the `` component loses focus after its value has changed. This event will not emit when programmatically setting the `value` property. + */ + "onIonChange"?: (event: IonInputOtpCustomEvent) => void; + /** + * Emitted when all input boxes have been filled with valid values. + */ + "onIonComplete"?: (event: IonInputOtpCustomEvent) => void; + /** + * Emitted when the input group has focus. + */ + "onIonFocus"?: (event: IonInputOtpCustomEvent) => void; + /** + * The `ionInput` event is fired each time the user modifies the input's value. Unlike the `ionChange` event, the `ionInput` event is fired for each alteration to the input's value. This typically happens for each keystroke as the user types. For elements that accept text input (`type=text`, `type=tel`, etc.), the interface is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others, the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event). If the input is cleared on edit, the type is `null`. + */ + "onIonInput"?: (event: IonInputOtpCustomEvent) => void; + /** + * A regex pattern string for allowed characters. Defaults based on type. For numbers (`type="number"`): `"[\p{N}]"` For text (`type="text"`): `"[\p{L}\p{N}]"` + */ + "pattern"?: string; + /** + * If `true`, the user cannot modify the value. + * @default false + */ + "readonly"?: boolean; + /** + * Where separators should be shown between input boxes. Can be a comma-separated string or an array of numbers. For example: `"3"` will show a separator after the 3rd input box. `[1,4]` will show a separator after the 1st and 4th input boxes. `"all"` will show a separator between every input box. + */ + "separators"?: 'all' | string | number[]; + /** + * The shape of the input boxes. If "round" they will have an increased border radius. If "rectangular" they will have no border radius. If "soft" they will have a soft border radius. + * @default 'round' + */ + "shape"?: 'round' | 'rectangular' | 'soft'; + /** + * The size of the input boxes. + * @default 'medium' + */ + "size"?: 'small' | 'medium' | 'large'; + /** + * The type of input allowed in the input boxes. + * @default 'number' + */ + "type"?: 'text' | 'number'; + /** + * The value of the input group. + * @default '' */ "value"?: string | number | null; } @@ -6187,11 +6807,15 @@ declare namespace LocalJSX { * The icon that can be used to represent showing a password. If not set, the "eye" Ionicon will be used. */ "showIcon"?: string; + /** + * @default 'password' + */ "type"?: TextFieldTypes; } interface IonItem { /** * If `true`, a button tag will be rendered and the item will be tappable. + * @default false */ "button"?: boolean; /** @@ -6204,10 +6828,12 @@ declare namespace LocalJSX { "detail"?: boolean; /** * The icon to use when `detail` is set to `true`. + * @default chevronForward */ "detailIcon"?: string; /** * If `true`, the user cannot interact with the item. + * @default false */ "disabled"?: boolean; /** @@ -6236,6 +6862,7 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection"?: RouterDirection; /** @@ -6244,6 +6871,7 @@ declare namespace LocalJSX { "target"?: string | undefined; /** * The type of the button. Only used when an `onclick` or `button` property is present. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } @@ -6258,6 +6886,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * When it's set to `true`, the item-divider will stay visible when it reaches the top of the viewport until the next `ion-item-divider` replaces it. This feature relies in `position:sticky`: https://caniuse.com/#feat=css-sticky + * @default false */ "sticky"?: boolean; } @@ -6270,6 +6899,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the item option. + * @default false */ "disabled"?: boolean; /** @@ -6278,6 +6908,7 @@ declare namespace LocalJSX { "download"?: string | undefined; /** * If `true`, the option will expand to take up the available width and cover any other options. + * @default false */ "expandable"?: boolean; /** @@ -6298,6 +6929,7 @@ declare namespace LocalJSX { "target"?: string | undefined; /** * The type of the button. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } @@ -6308,12 +6940,14 @@ declare namespace LocalJSX { "onIonSwipe"?: (event: IonItemOptionsCustomEvent) => void; /** * The side the option button should be on. Possible values: `"start"` and `"end"`. If you have multiple `ion-item-options`, a side must be provided for each. + * @default 'end' */ "side"?: Side; } interface IonItemSliding { /** * If `true`, the user cannot interact with the sliding item. + * @default false */ "disabled"?: boolean; /** @@ -6346,6 +6980,7 @@ declare namespace LocalJSX { interface IonList { /** * If `true`, the list will have margin around it and rounded corners. + * @default false */ "inset"?: boolean; /** @@ -6374,10 +7009,12 @@ declare namespace LocalJSX { interface IonLoading { /** * If `true`, the loading indicator will animate. + * @default true */ "animated"?: boolean; /** * If `true`, the loading indicator will be dismissed when the backdrop is clicked. + * @default false */ "backdropDismiss"?: boolean; /** @@ -6387,12 +7024,16 @@ declare namespace LocalJSX { "delegate"?: FrameworkDelegate; /** * Number of milliseconds to wait before dismissing the loading indicator. + * @default 0 */ "duration"?: number; /** * Animation to use when the loading indicator is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController"?: boolean; /** * Additional attributes to pass to the loader. @@ -6400,10 +7041,12 @@ declare namespace LocalJSX { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the loading indicator will open. If `false`, the loading indicator will close. Use this if you need finer grained control over presentation, otherwise just use the loadingController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the loading indicator dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose"?: boolean; /** @@ -6453,6 +7096,7 @@ declare namespace LocalJSX { "overlayIndex": number; /** * If `true`, a backdrop will be displayed behind the loading indicator. + * @default true */ "showBackdrop"?: boolean; /** @@ -6461,6 +7105,7 @@ declare namespace LocalJSX { "spinner"?: SpinnerTypes | null; /** * If `true`, the loading indicator will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; /** @@ -6475,10 +7120,12 @@ declare namespace LocalJSX { "contentId"?: string; /** * If `true`, the menu is disabled. + * @default false */ "disabled"?: boolean; /** * The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. + * @default 50 */ "maxEdgeStart"?: number; /** @@ -6507,10 +7154,12 @@ declare namespace LocalJSX { "onIonWillOpen"?: (event: IonMenuCustomEvent) => void; /** * Which side of the view the menu should be placed. + * @default 'start' */ "side"?: Side; /** * If `true`, swiping the menu is enabled. + * @default true */ "swipeGesture"?: boolean; /** @@ -6521,6 +7170,7 @@ declare namespace LocalJSX { interface IonMenuButton { /** * Automatically hides the menu button when the corresponding menu is not active + * @default true */ "autoHide"?: boolean; /** @@ -6529,6 +7179,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the menu button. + * @default false */ "disabled"?: boolean; /** @@ -6541,12 +7192,14 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The type of the button. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; } interface IonMenuToggle { /** * Automatically hides the content when the corresponding menu is not active. By default, it's `true`. Change it to `false` in order to keep `ion-menu-toggle` always visible regardless the state of the menu. + * @default true */ "autoHide"?: boolean; /** @@ -6557,14 +7210,17 @@ declare namespace LocalJSX { interface IonModal { /** * If `true`, the modal will animate. + * @default true */ "animated"?: boolean; /** * A decimal value between 0 and 1 that indicates the point after which the backdrop will begin to fade in when using a sheet modal. Prior to this point, the backdrop will be hidden and the content underneath the sheet can be interacted with. This value is exclusive meaning the backdrop will become active after the value specified. + * @default 0 */ "backdropBreakpoint"?: number; /** * If `true`, the modal will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss"?: boolean; /** @@ -6573,6 +7229,7 @@ declare namespace LocalJSX { "breakpoints"?: number[]; /** * Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + * @default true */ "canDismiss"?: boolean | ((data?: any, role?: string) => Promise); /** @@ -6594,10 +7251,12 @@ declare namespace LocalJSX { "enterAnimation"?: AnimationBuilder; /** * Controls whether scrolling or dragging within the sheet modal expands it to a larger breakpoint. This only takes effect when `breakpoints` and `initialBreakpoint` are set. If `true`, scrolling or dragging anywhere in the modal will first expand it to the next breakpoint. Once fully expanded, scrolling will affect the content. If `false`, scrolling will always affect the content. The modal will only expand when dragging the header or handle. The modal will close when dragging the header or handle. It can also be closed when dragging the content, but only if the content is scrolled to the top. + * @default true */ "expandToScroll"?: boolean; /** * If `true`, focus will not be allowed to move outside of this overlay. If `false`, focus will be allowed to move outside of the overlay. In most scenarios this property should remain set to `true`. Setting this property to `false` can cause severe accessibility issues as users relying on assistive technologies may be able to move focus into a confusing state. We recommend only setting this to `false` when absolutely necessary. Developers may want to consider disabling focus trapping if this overlay presents a non-Ionic overlay from a 3rd party library. Developers would disable focus trapping on the Ionic overlay when presenting the 3rd party overlay and then re-enable focus trapping when dismissing the 3rd party overlay and moving focus back to the Ionic overlay. + * @default true */ "focusTrap"?: boolean; /** @@ -6606,8 +7265,12 @@ declare namespace LocalJSX { "handle"?: boolean; /** * The interaction behavior for the sheet modal when the handle is pressed. Defaults to `"none"`, which means the modal will not change size or position when the handle is pressed. Set to `"cycle"` to let the modal cycle between available breakpoints when pressed. Handle behavior is unavailable when the `handle` property is set to `false` or when the `breakpoints` property is not set (using a fullscreen or card modal). + * @default 'none' */ "handleBehavior"?: ModalHandleBehavior; + /** + * @default false + */ "hasController"?: boolean; /** * Additional attributes to pass to the modal. @@ -6619,14 +7282,17 @@ declare namespace LocalJSX { "initialBreakpoint"?: number; /** * If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the component passed into `ion-modal` will automatically be mounted when the modal is created. The component will remain mounted even when the modal is dismissed. However, the component will be destroyed when the modal is destroyed. This property is not reactive and should only be used when initially creating a modal. Note: This feature only applies to inline modals in JavaScript frameworks such as Angular, React, and Vue. + * @default false */ "keepContentsMounted"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose"?: boolean; /** @@ -6684,6 +7350,7 @@ declare namespace LocalJSX { "presentingElement"?: HTMLElement; /** * If `true`, a backdrop will be displayed behind the modal. This property controls whether or not the backdrop darkens the screen when the modal is presented. It does not control whether or not the backdrop is active or present in the DOM. + * @default true */ "showBackdrop"?: boolean; /** @@ -6694,6 +7361,7 @@ declare namespace LocalJSX { interface IonNav { /** * If `true`, the nav should animate the transition of components. + * @default true */ "animated"?: boolean; /** @@ -6741,6 +7409,7 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder; /** * The transition direction when navigating to another page. + * @default 'forward' */ "routerDirection"?: RouterDirection; } @@ -6764,10 +7433,12 @@ declare namespace LocalJSX { interface IonPickerColumn { /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** * If `true`, the user cannot interact with the picker. + * @default false */ "disabled"?: boolean; /** @@ -6776,6 +7447,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If `true`, tapping the picker will reveal a number input keyboard that lets the user type in values for each picker column. This is useful when working with time pickers. + * @default false */ "numericInput"?: boolean; /** @@ -6790,10 +7462,12 @@ declare namespace LocalJSX { interface IonPickerColumnOption { /** * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). + * @default 'primary' */ "color"?: Color; /** * If `true`, the user cannot interact with the picker column option. + * @default false */ "disabled"?: boolean; /** @@ -6804,18 +7478,22 @@ declare namespace LocalJSX { interface IonPickerLegacy { /** * If `true`, the picker will animate. + * @default true */ "animated"?: boolean; /** * If `true`, the picker will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss"?: boolean; /** * Array of buttons to be displayed at the top of the picker. + * @default [] */ "buttons"?: PickerButton[]; /** * Array of columns to be displayed in the picker. + * @default [] */ "columns"?: PickerColumn[]; /** @@ -6825,12 +7503,16 @@ declare namespace LocalJSX { "delegate"?: FrameworkDelegate; /** * Number of milliseconds to wait before dismissing the picker. + * @default 0 */ "duration"?: number; /** * Animation to use when the picker is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController"?: boolean; /** * Additional attributes to pass to the picker. @@ -6838,10 +7520,12 @@ declare namespace LocalJSX { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the picker will open. If `false`, the picker will close. Use this if you need finer grained control over presentation, otherwise just use the pickerController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the picker dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose"?: boolean; /** @@ -6887,6 +7571,7 @@ declare namespace LocalJSX { "overlayIndex": number; /** * If `true`, a backdrop will be displayed behind the picker. + * @default true */ "showBackdrop"?: boolean; /** @@ -6911,14 +7596,17 @@ declare namespace LocalJSX { "alignment"?: PositionAlign; /** * If `true`, the popover will animate. + * @default true */ "animated"?: boolean; /** * If `true`, the popover will display an arrow that points at the `reference` when running in `ios` mode. Does not apply in `md` mode. + * @default true */ "arrow"?: boolean; /** * If `true`, the popover will be dismissed when the backdrop is clicked. + * @default true */ "backdropDismiss"?: boolean; /** @@ -6936,6 +7624,7 @@ declare namespace LocalJSX { "delegate"?: FrameworkDelegate; /** * If `true`, the popover will be automatically dismissed when the content has been clicked. + * @default false */ "dismissOnSelect"?: boolean; /** @@ -6948,8 +7637,12 @@ declare namespace LocalJSX { "event"?: any; /** * If `true`, focus will not be allowed to move outside of this overlay. If `false`, focus will be allowed to move outside of the overlay. In most scenarios this property should remain set to `true`. Setting this property to `false` can cause severe accessibility issues as users relying on assistive technologies may be able to move focus into a confusing state. We recommend only setting this to `false` when absolutely necessary. Developers may want to consider disabling focus trapping if this overlay presents a non-Ionic overlay from a 3rd party library. Developers would disable focus trapping on the Ionic overlay when presenting the 3rd party overlay and then re-enable focus trapping when dismissing the 3rd party overlay and moving focus back to the Ionic overlay. + * @default true */ "focusTrap"?: boolean; + /** + * @default false + */ "hasController"?: boolean; /** * Additional attributes to pass to the popover. @@ -6957,16 +7650,22 @@ declare namespace LocalJSX { "htmlAttributes"?: { [key: string]: any }; /** * If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the component passed into `ion-popover` will automatically be mounted when the popover is created. The component will remain mounted even when the popover is dismissed. However, the component will be destroyed when the popover is destroyed. This property is not reactive and should only be used when initially creating a popover. Note: This feature only applies to inline popovers in JavaScript frameworks such as Angular, React, and Vue. + * @default false */ "keepContentsMounted"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default true */ "keyboardClose"?: boolean; + /** + * @default false + */ "keyboardEvents"?: boolean; /** * Animation to use when the popover is dismissed. @@ -7015,22 +7714,27 @@ declare namespace LocalJSX { "overlayIndex": number; /** * Describes what to position the popover relative to. If `"trigger"`, the popover will be positioned relative to the trigger button. If passing in an event, this is determined via event.target. If `"event"`, the popover will be positioned relative to the x/y coordinates of the trigger action. If passing in an event, this is determined via event.clientX and event.clientY. + * @default 'trigger' */ "reference"?: PositionReference; /** * If `true`, a backdrop will be displayed behind the popover. This property controls whether or not the backdrop darkens the screen when the popover is presented. It does not control whether or not the backdrop is active or present in the DOM. + * @default true */ "showBackdrop"?: boolean; /** * Describes which side of the `reference` point to position the popover on. The `"start"` and `"end"` values are RTL-aware, and the `"left"` and `"right"` values are not. + * @default 'bottom' */ "side"?: PositionSide; /** * Describes how to calculate the popover width. If `"cover"`, the popover width will match the width of the trigger. If `"auto"`, the popover width will be set to a static default value. + * @default 'auto' */ "size"?: PopoverSize; /** * If `true`, the popover will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; /** @@ -7039,12 +7743,14 @@ declare namespace LocalJSX { "trigger"?: string | undefined; /** * Describes what kind of interaction with the trigger that should cause the popover to open. Does not apply when the `trigger` property is `undefined`. If `"click"`, the popover will be presented when the trigger is left clicked. If `"hover"`, the popover will be presented when a pointer hovers over the trigger. If `"context-menu"`, the popover will be presented when the trigger is right clicked on desktop and long pressed on mobile. This will also prevent your device's normal context menu from appearing. + * @default 'click' */ "triggerAction"?: TriggerAction; } interface IonProgressBar { /** * If the buffer and value are smaller than 1, the buffer circles will show. The buffer should be between [0, 1]. + * @default 1 */ "buffer"?: number; /** @@ -7057,14 +7763,17 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If true, reverse the progress bar direction. + * @default false */ "reversed"?: boolean; /** * The state of the progress bar, based on if the time the process takes is known or not. Default options are: `"determinate"` (no animation), `"indeterminate"` (animate from left to right). + * @default 'determinate' */ "type"?: 'determinate' | 'indeterminate'; /** * The value determines how much of the active bar should display when the `type` is `"determinate"`. The value should be between [0, 1]. + * @default 0 */ "value"?: number; } @@ -7079,6 +7788,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the radio. + * @default false */ "disabled"?: boolean; /** @@ -7087,6 +7797,7 @@ declare namespace LocalJSX { "justify"?: 'start' | 'end' | 'space-between'; /** * Where to place the label relative to the radio. `"start"`: The label will appear to the left of the radio in LTR and to the right in RTL. `"end"`: The label will appear to the right of the radio in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the radio regardless of the direction. The alignment of the label can be controlled with the `alignment` property. + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'fixed' | 'stacked'; /** @@ -7095,6 +7806,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -7113,6 +7825,7 @@ declare namespace LocalJSX { interface IonRadioGroup { /** * If `true`, the radios can be deselected. + * @default false */ "allowEmptySelection"?: boolean; /** @@ -7129,6 +7842,7 @@ declare namespace LocalJSX { "helperText"?: string; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -7159,10 +7873,12 @@ declare namespace LocalJSX { "debounce"?: number; /** * If `true`, the user cannot interact with the range. + * @default false */ "disabled"?: boolean; /** * Show two knobs. + * @default false */ "dualKnobs"?: boolean; /** @@ -7171,14 +7887,17 @@ declare namespace LocalJSX { "label"?: string; /** * Where to place the label relative to the range. `"start"`: The label will appear to the left of the range in LTR and to the right in RTL. `"end"`: The label will appear to the right of the range in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the range regardless of the direction. + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'fixed' | 'stacked'; /** * Maximum integer value of the range. + * @default 100 */ "max"?: number; /** * Minimum integer value of the range. + * @default 0 */ "min"?: number; /** @@ -7187,6 +7906,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.rangeId */ "name"?: string; /** @@ -7215,36 +7935,44 @@ declare namespace LocalJSX { "onIonKnobMoveStart"?: (event: IonRangeCustomEvent) => void; /** * If `true`, a pin with integer value is shown when the knob is pressed. + * @default false */ "pin"?: boolean; /** * A callback used to format the pin text. By default the pin text is set to `Math.round(value)`. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + * @default (value: number): number => Math.round(value) */ "pinFormatter"?: PinFormatter; /** * If `true`, the knob snaps to tick marks evenly spaced based on the step property value. + * @default false */ "snaps"?: boolean; /** * Specifies the value granularity. + * @default 1 */ "step"?: number; /** * If `true`, tick marks are displayed based on the step value. Only applies when `snaps` is `true`. + * @default true */ "ticks"?: boolean; /** * the value of the range. + * @default 0 */ "value"?: RangeValue; } interface IonRefresher { /** * Time it takes to close the refresher. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default '280ms' */ "closeDuration"?: string; /** * If `true`, the refresher will be hidden. + * @default false */ "disabled"?: boolean; /** @@ -7265,18 +7993,22 @@ declare namespace LocalJSX { "onIonStart"?: (event: IonRefresherCustomEvent) => void; /** * How much to multiply the pull speed by. To slow the pull animation down, pass a number less than `1`. To speed up the pull, pass a number greater than `1`. The default value is `1` which is equal to the speed of the cursor. If a negative value is passed in, the factor will be `1` instead. For example: If the value passed is `1.2` and the content is dragged by `10` pixels, instead of `10` pixels the content will be pulled by `12` pixels (an increase of 20 percent). If the value passed is `0.8`, the dragged amount will be `8` pixels, less than the amount the cursor has moved. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default 1 */ "pullFactor"?: number; /** * The maximum distance of the pull until the refresher will automatically go into the `refreshing` state. Defaults to the result of `pullMin + 60`. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default this.pullMin + 60 */ "pullMax"?: number; /** * The minimum distance the user must pull down until the refresher will go into the `refreshing` state. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default 60 */ "pullMin"?: number; /** * Time it takes the refresher to snap back to the `refreshing` state. Does not apply when the refresher content uses a spinner, enabling the native refresher. + * @default '280ms' */ "snapbackDuration"?: string; } @@ -7303,6 +8035,7 @@ declare namespace LocalJSX { interface IonReorderGroup { /** * If `true`, the reorder will be hidden. + * @default true */ "disabled"?: boolean; /** @@ -7313,6 +8046,7 @@ declare namespace LocalJSX { interface IonRippleEffect { /** * Sets the type of ripple-effect: - `bounded`: the ripple effect expands from the user's click position - `unbounded`: the ripple effect expands from the center of the button and overflows the container. NOTE: Surfaces for bounded ripples should have the overflow property set to hidden, while surfaces for unbounded ripples should have it set to visible. + * @default 'bounded' */ "type"?: 'bounded' | 'unbounded'; } @@ -7339,6 +8073,7 @@ declare namespace LocalJSX { "onIonRouteDataChanged"?: (event: IonRouteCustomEvent) => void; /** * Relative path that needs to match in order for this route to apply. Accepts paths similar to expressjs so that you can define parameters in the url /foo/:bar where bar would be available in incoming props. + * @default '' */ "url"?: string; } @@ -7367,10 +8102,12 @@ declare namespace LocalJSX { "onIonRouteWillChange"?: (event: IonRouterCustomEvent) => void; /** * The root path to use when matching URLs. By default, this is set to "/", but you can specify an alternate prefix for all URL paths. + * @default '/' */ "root"?: string; /** * The router can work in two "modes": - With hash: `/index.html#/path/to/page` - Without hash: `/path/to/page` Using one or another might depend in the requirements of your app and/or where it's deployed. Usually "hash-less" navigation works better for SEO and it's more user friendly too, but it might requires additional server-side configuration in order to properly work. On the other side hash-navigation is much easier to deploy, it even works over the file protocol. By default, this property is `true`, change to `false` to allow hash-less URLs. + * @default true */ "useHash"?: boolean; } @@ -7393,6 +8130,7 @@ declare namespace LocalJSX { "routerAnimation"?: AnimationBuilder | undefined; /** * When using a router, it specifies the transition direction when navigating to another page using `href`. + * @default 'forward' */ "routerDirection"?: RouterDirection; /** @@ -7403,6 +8141,7 @@ declare namespace LocalJSX { interface IonRouterOutlet { /** * If `true`, the router-outlet should animate the transition of components. + * @default true */ "animated"?: boolean; /** @@ -7412,6 +8151,7 @@ declare namespace LocalJSX { "delegate"?: FrameworkDelegate; /** * The mode determines which platform styles to use. + * @default getIonMode(this) */ "mode"?: "ios" | "md"; "onIonNavDidChange"?: (event: IonRouterOutletCustomEvent) => void; @@ -7424,26 +8164,32 @@ declare namespace LocalJSX { interface IonSearchbar { /** * If `true`, enable searchbar animation. + * @default false */ "animated"?: boolean; /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'off' */ "autocapitalize"?: string; /** * Set the input's autocomplete property. + * @default 'off' */ "autocomplete"?: AutocompleteTypes; /** * Set the input's autocorrect property. + * @default 'off' */ "autocorrect"?: 'on' | 'off'; /** * Set the cancel button icon. Only applies to `md` mode. Defaults to `arrow-back-sharp`. + * @default config.get('backButtonIcon', arrowBackSharp) as string */ "cancelButtonIcon"?: string; /** * Set the cancel button text. Only applies to `ios` mode. + * @default 'Cancel' */ "cancelButtonText"?: string; /** @@ -7460,6 +8206,7 @@ declare namespace LocalJSX { "debounce"?: number; /** * If `true`, the user cannot interact with the input. + * @default false */ "disabled"?: boolean; /** @@ -7484,6 +8231,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If used in a form, set the name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -7516,6 +8264,7 @@ declare namespace LocalJSX { "onIonStyle"?: (event: IonSearchbarCustomEvent) => void; /** * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) + * @default 'Search' */ "placeholder"?: string; /** @@ -7524,22 +8273,27 @@ declare namespace LocalJSX { "searchIcon"?: string; /** * Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state. + * @default 'never' */ "showCancelButton"?: 'never' | 'focus' | 'always'; /** * Sets the behavior for the clear button. Defaults to `"focus"`. Setting to `"focus"` shows the clear button on focus if the input is not empty. Setting to `"never"` hides the clear button. Setting to `"always"` shows the clear button regardless of focus state, but only if the input is not empty. + * @default 'always' */ "showClearButton"?: 'never' | 'focus' | 'always'; /** * If `true`, enable spellcheck on the input. + * @default false */ "spellcheck"?: boolean; /** * Set the type of the input. + * @default 'search' */ "type"?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url'; /** * the value of the searchbar. + * @default '' */ "value"?: string | null; } @@ -7550,6 +8304,7 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the segment. + * @default false */ "disabled"?: boolean; /** @@ -7570,14 +8325,17 @@ declare namespace LocalJSX { "onIonStyle"?: (event: IonSegmentCustomEvent) => void; /** * If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons. + * @default false */ "scrollable"?: boolean; /** * If `true`, navigating to an `ion-segment-button` with the keyboard will focus and select the element. If `false`, keyboard navigation will only focus the `ion-segment-button` element. + * @default false */ "selectOnFocus"?: boolean; /** * If `true`, users will be able to swipe between segment buttons to activate them. + * @default true */ "swipeGesture"?: boolean; /** @@ -7592,10 +8350,12 @@ declare namespace LocalJSX { "contentId"?: string; /** * If `true`, the user cannot interact with the segment button. + * @default false */ "disabled"?: boolean; /** * Set the layout of the text and icon in the segment. + * @default 'icon-top' */ "layout"?: SegmentButtonLayout; /** @@ -7604,10 +8364,12 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The type of the button. + * @default 'button' */ "type"?: 'submit' | 'reset' | 'button'; /** * The value of the segment button. + * @default 'ion-sb-' + ids++ */ "value"?: SegmentValue; } @@ -7616,6 +8378,7 @@ declare namespace LocalJSX { interface IonSegmentView { /** * If `true`, the segment view cannot be interacted with. + * @default false */ "disabled"?: boolean; /** @@ -7626,6 +8389,7 @@ declare namespace LocalJSX { interface IonSelect { /** * The text to display on the cancel button. + * @default 'Cancel' */ "cancelText"?: string; /** @@ -7638,6 +8402,7 @@ declare namespace LocalJSX { "compareWith"?: string | SelectCompareFn | null; /** * If `true`, the user cannot interact with the select. + * @default false */ "disabled"?: boolean; /** @@ -7658,10 +8423,12 @@ declare namespace LocalJSX { "helperText"?: string; /** * The interface the select should use: `action-sheet`, `popover`, `alert`, or `modal`. + * @default 'alert' */ "interface"?: SelectInterface; /** * Any additional options that the `alert`, `action-sheet` or `popover` interface can take. See the [ion-alert docs](./alert), the [ion-action-sheet docs](./action-sheet), the [ion-popover docs](./popover), and the [ion-modal docs](./modal) for the create options for each interface. Note: `interfaceOptions` will not override `inputs` or `buttons` with the `alert` interface. + * @default {} */ "interfaceOptions"?: any; /** @@ -7674,6 +8441,7 @@ declare namespace LocalJSX { "label"?: string; /** * Where to place the label relative to the select. `"start"`: The label will appear to the left of the select in LTR and to the right in RTL. `"end"`: The label will appear to the right of the select in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the select when the select is focused or it has a value. Otherwise it will appear on top of the select. `"stacked"`: The label will appear smaller and above the select regardless even when the select is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). When using `"floating"` or `"stacked"` we recommend initializing the select with either a `value` or a `placeholder`. + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'floating' | 'stacked' | 'fixed'; /** @@ -7682,14 +8450,17 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * If `true`, the select can accept multiple values. + * @default false */ "multiple"?: boolean; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** * The text to display on the ok button. + * @default 'OK' */ "okText"?: string; /** @@ -7722,6 +8493,7 @@ declare namespace LocalJSX { "placeholder"?: string; /** * If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid. + * @default false */ "required"?: boolean; /** @@ -7744,11 +8516,15 @@ declare namespace LocalJSX { interface IonSelectModal { "header"?: string; "multiple"?: boolean; + /** + * @default [] + */ "options"?: SelectModalOption[]; } interface IonSelectOption { /** * If `true`, the user cannot interact with the select option. This property does not apply when `interface="action-sheet"` as `ion-action-sheet` does not allow for disabled buttons. + * @default false */ "disabled"?: boolean; /** @@ -7771,6 +8547,7 @@ declare namespace LocalJSX { "multiple"?: boolean; /** * An array of options for the popover + * @default [] */ "options"?: SelectPopoverOption[]; /** @@ -7781,6 +8558,7 @@ declare namespace LocalJSX { interface IonSkeletonText { /** * If `true`, the skeleton text will animate. + * @default false */ "animated"?: boolean; /** @@ -7803,6 +8581,7 @@ declare namespace LocalJSX { "name"?: SpinnerTypes; /** * If `true`, the spinner's animation will be paused. + * @default false */ "paused"?: boolean; } @@ -7813,6 +8592,7 @@ declare namespace LocalJSX { "contentId"?: string; /** * If `true`, the split pane will be hidden. + * @default false */ "disabled"?: boolean; /** @@ -7821,10 +8601,14 @@ declare namespace LocalJSX { "onIonSplitPaneVisible"?: (event: IonSplitPaneCustomEvent<{ visible: boolean }>) => void; /** * When the split-pane should be shown. Can be a CSS media query expression, or a shortcut expression. Can also be a boolean expression. + * @default QUERY['lg'] */ "when"?: string | boolean; } interface IonTab { + /** + * @default false + */ "active"?: boolean; /** * The component to display inside of the tab. @@ -7853,12 +8637,14 @@ declare namespace LocalJSX { "selectedTab"?: string; /** * If `true`, the tab bar will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; } interface IonTabButton { /** * If `true`, the user cannot interact with the tab button. + * @default false */ "disabled"?: boolean; /** @@ -7887,6 +8673,7 @@ declare namespace LocalJSX { "rel"?: string | undefined; /** * The selected tab component + * @default false */ "selected"?: boolean; /** @@ -7911,6 +8698,9 @@ declare namespace LocalJSX { * Emitted when the navigation is about to transition to a new component. */ "onIonTabsWillChange"?: (event: IonTabsCustomEvent<{ tab: string }>) => void; + /** + * @default false + */ "useRouter"?: boolean; } interface IonText { @@ -7926,18 +8716,22 @@ declare namespace LocalJSX { interface IonTextarea { /** * If `true`, the textarea container will grow and shrink based on the contents of the textarea. + * @default false */ "autoGrow"?: boolean; /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + * @default 'none' */ "autocapitalize"?: string; /** * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information. + * @default false */ "autofocus"?: boolean; /** * If `true`, the value will be cleared after focus upon edit. + * @default false */ "clearOnEdit"?: boolean; /** @@ -7950,6 +8744,7 @@ declare namespace LocalJSX { "cols"?: number; /** * If `true`, a character counter will display the ratio of characters used and the total character limit. Developers must also set the `maxlength` property for the counter to be calculated correctly. + * @default false */ "counter"?: boolean; /** @@ -7962,6 +8757,7 @@ declare namespace LocalJSX { "debounce"?: number; /** * If `true`, the user cannot interact with the textarea. + * @default false */ "disabled"?: boolean; /** @@ -7990,6 +8786,7 @@ declare namespace LocalJSX { "label"?: string; /** * Where to place the label relative to the textarea. `"start"`: The label will appear to the left of the textarea in LTR and to the right in RTL. `"end"`: The label will appear to the right of the textarea in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the textarea when the textarea is focused or it has a value. Otherwise it will appear on top of the textarea. `"stacked"`: The label will appear smaller and above the textarea regardless even when the textarea is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'floating' | 'stacked' | 'fixed'; /** @@ -8006,6 +8803,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -8030,10 +8828,12 @@ declare namespace LocalJSX { "placeholder"?: string; /** * If `true`, the user cannot modify the value. + * @default false */ "readonly"?: boolean; /** * If `true`, the user must fill in a value before submitting a form. + * @default false */ "required"?: boolean; /** @@ -8046,10 +8846,12 @@ declare namespace LocalJSX { "shape"?: 'round'; /** * If `true`, the element will have its spelling and grammar checked. + * @default false */ "spellcheck"?: boolean; /** * The value of the textarea. + * @default '' */ "value"?: string | null; /** @@ -8076,6 +8878,7 @@ declare namespace LocalJSX { interface IonToast { /** * If `true`, the toast will animate. + * @default true */ "animated"?: boolean; /** @@ -8093,12 +8896,16 @@ declare namespace LocalJSX { "delegate"?: FrameworkDelegate; /** * How many milliseconds to wait before hiding the toast. By default, it will show until `dismiss()` is called. + * @default config.getNumber('toastDuration', 0) */ "duration"?: number; /** * Animation to use when the toast is presented. */ "enterAnimation"?: AnimationBuilder; + /** + * @default false + */ "hasController"?: boolean; /** * Header to be shown in the toast. @@ -8114,14 +8921,17 @@ declare namespace LocalJSX { "icon"?: string; /** * If `true`, the toast will open. If `false`, the toast will close. Use this if you need finer grained control over presentation, otherwise just use the toastController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the toast dismisses. You will need to do that in your code. + * @default false */ "isOpen"?: boolean; /** * If `true`, the keyboard will be automatically dismissed when the overlay is presented. + * @default false */ "keyboardClose"?: boolean; /** * Defines how the message and buttons are laid out in the toast. 'baseline': The message and the buttons will appear on the same line. Message text may wrap within the message container. 'stacked': The buttons containers and message will stack on top of each other. Use this if you have long text in your buttons. + * @default 'baseline' */ "layout"?: ToastLayout; /** @@ -8171,6 +8981,7 @@ declare namespace LocalJSX { "overlayIndex": number; /** * The starting position of the toast on the screen. Can be tweaked further using the `positionAnchor` property. + * @default 'bottom' */ "position"?: ToastPosition; /** @@ -8183,6 +8994,7 @@ declare namespace LocalJSX { "swipeGesture"?: ToastSwipeGestureDirection; /** * If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility). + * @default false */ "translucent"?: boolean; /** @@ -8197,6 +9009,7 @@ declare namespace LocalJSX { "alignment"?: 'start' | 'center'; /** * If `true`, the toggle is selected. + * @default false */ "checked"?: boolean; /** @@ -8205,10 +9018,12 @@ declare namespace LocalJSX { "color"?: Color; /** * If `true`, the user cannot interact with the toggle. + * @default false */ "disabled"?: boolean; /** * Enables the on/off accessibility switch labels within the toggle. + * @default config.get('toggleOnOffLabels') */ "enableOnOffLabels"?: boolean | undefined; /** @@ -8225,6 +9040,7 @@ declare namespace LocalJSX { "justify"?: 'start' | 'end' | 'space-between'; /** * Where to place the label relative to the input. `"start"`: The label will appear to the left of the toggle in LTR and to the right in RTL. `"end"`: The label will appear to the right of the toggle in LTR and to the left in RTL. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("..."). `"stacked"`: The label will appear above the toggle regardless of the direction. The alignment of the label can be controlled with the `alignment` property. + * @default 'start' */ "labelPlacement"?: 'start' | 'end' | 'fixed' | 'stacked'; /** @@ -8233,6 +9049,7 @@ declare namespace LocalJSX { "mode"?: "ios" | "md"; /** * The name of the control, which is submitted with the form data. + * @default this.inputId */ "name"?: string; /** @@ -8249,10 +9066,12 @@ declare namespace LocalJSX { "onIonFocus"?: (event: IonToggleCustomEvent) => void; /** * If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid. + * @default false */ "required"?: boolean; /** * The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a ``, it's only used when the toggle participates in a native ``. + * @default 'on' */ "value"?: string | null; } @@ -8301,6 +9120,7 @@ declare namespace LocalJSX { "ion-infinite-scroll": IonInfiniteScroll; "ion-infinite-scroll-content": IonInfiniteScrollContent; "ion-input": IonInput; + "ion-input-otp": IonInputOtp; "ion-input-password-toggle": IonInputPasswordToggle; "ion-item": IonItem; "ion-item-divider": IonItemDivider; @@ -8403,6 +9223,7 @@ declare module "@stencil/core" { "ion-infinite-scroll": LocalJSX.IonInfiniteScroll & JSXBase.HTMLAttributes; "ion-infinite-scroll-content": LocalJSX.IonInfiniteScrollContent & JSXBase.HTMLAttributes; "ion-input": LocalJSX.IonInput & JSXBase.HTMLAttributes; + "ion-input-otp": LocalJSX.IonInputOtp & JSXBase.HTMLAttributes; "ion-input-password-toggle": LocalJSX.IonInputPasswordToggle & JSXBase.HTMLAttributes; "ion-item": LocalJSX.IonItem & JSXBase.HTMLAttributes; "ion-item-divider": LocalJSX.IonItemDivider & JSXBase.HTMLAttributes; diff --git a/core/src/components/datetime/datetime-interface.ts b/core/src/components/datetime/datetime-interface.ts index 475a672d069..2919e0c69d0 100644 --- a/core/src/components/datetime/datetime-interface.ts +++ b/core/src/components/datetime/datetime-interface.ts @@ -15,6 +15,7 @@ export interface DatetimeParts { hour?: number; minute?: number; ampm?: 'am' | 'pm'; + isAdjacentDay?: boolean; } export type DatetimePresentation = 'date-time' | 'time-date' | 'date' | 'time' | 'month' | 'year' | 'month-year'; diff --git a/core/src/components/datetime/datetime.ios.scss b/core/src/components/datetime/datetime.ios.scss index 71bff22441a..5da21dd60ce 100644 --- a/core/src/components/datetime/datetime.ios.scss +++ b/core/src/components/datetime/datetime.ios.scss @@ -251,7 +251,8 @@ * is selected should have ion-color for * text color and be bolder. */ -:host .calendar-day.calendar-day-active { +:host .calendar-day.calendar-day-active, +:host .calendar-day.calendar-day-adjacent-day.calendar-day-active { color: current-color(base); font-weight: 600; @@ -267,6 +268,10 @@ color: current-color(contrast); } +:host .calendar-day.calendar-day-adjacent-day { + color: $text-color-step-700; +} + // Time / Header // ----------------------------------- :host .datetime-time { diff --git a/core/src/components/datetime/datetime.md.scss b/core/src/components/datetime/datetime.md.scss index 7b3d78f5e81..fe592b9e947 100644 --- a/core/src/components/datetime/datetime.md.scss +++ b/core/src/components/datetime/datetime.md.scss @@ -117,16 +117,22 @@ * is selected should have ion-color for * text color and be bolder. */ -:host .calendar-day.calendar-day-active { +:host .calendar-day.calendar-day-active, +:host .calendar-day.calendar-day-adjacent-day.calendar-day-active { color: current-color(contrast); } -.calendar-day.calendar-day-active { +.calendar-day.calendar-day-active, +.calendar-day.calendar-day-active:focus { border: 1px solid current-color(base); background: current-color(base); } +:host .calendar-day.calendar-day-adjacent-day { + color: $text-color-step-500; +} + // Time / Header // ----------------------------------- :host .datetime-time { diff --git a/core/src/components/datetime/datetime.scss b/core/src/components/datetime/datetime.scss index 9c7a4b0a2c8..f3053d0ed16 100644 --- a/core/src/components/datetime/datetime.scss +++ b/core/src/components/datetime/datetime.scss @@ -364,7 +364,8 @@ opacity: 0.4; } -.calendar-day:focus { + +.calendar-day:not(.calendar-day-adjacent-day):focus { background: current-color(base, 0.2); box-shadow: 0px 0px 0px 4px current-color(base, 0.2); diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index ad9e9bc22ec..71a9512e560 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -139,6 +139,7 @@ export class Datetime implements ComponentInterface { hour: 13, minute: 52, ampm: 'pm', + isAdjacentDay: false, }; @Element() el!: HTMLIonDatetimeElement; @@ -207,6 +208,13 @@ export class Datetime implements ComponentInterface { */ @Prop() isDateEnabled?: (dateIsoString: string) => boolean; + /** + * If `true`, the datetime calendar displays a six-week (42-day) layout, + * including days from the previous and next months to fill the grid. + * These adjacent days are selectable unless disabled. + */ + @Prop() showAdjacentDays = false; + @Watch('disabled') protected disabledChanged() { this.emitStyle(); @@ -805,12 +813,17 @@ export class Datetime implements ComponentInterface { private focusWorkingDay = (currentMonth: Element) => { /** - * Get the number of padding days so + * Get the number of offset days so * we know how much to offset our next selector by * to grab the correct calendar-day element. */ - const padding = currentMonth.querySelectorAll('.calendar-day-padding'); - const { day } = this.workingParts; + + const { day, month, year } = this.workingParts; + const firstOfMonth = new Date(`${month}/1/${year}`).getDay(); + const offset = + firstOfMonth >= this.firstDayOfWeek + ? firstOfMonth - this.firstDayOfWeek + : 7 - (this.firstDayOfWeek - firstOfMonth); if (day === null) { return; @@ -821,7 +834,7 @@ export class Datetime implements ComponentInterface { * and focus it. */ const dayEl = currentMonth.querySelector( - `.calendar-day-wrapper:nth-of-type(${padding.length + day}) .calendar-day` + `.calendar-day-wrapper:nth-of-type(${offset + day}) .calendar-day` ) as HTMLElement | null; if (dayEl) { dayEl.focus(); @@ -2201,10 +2214,34 @@ export class Datetime implements ComponentInterface { }} >
- {getDaysOfMonth(month, year, this.firstDayOfWeek % 7).map((dateObject, index) => { - const { day, dayOfWeek } = dateObject; - const { el, highlightedDates, isDateEnabled, multiple } = this; - const referenceParts = { month, day, year }; + {getDaysOfMonth(month, year, this.firstDayOfWeek % 7, this.showAdjacentDays).map((dateObject, index) => { + const { day, dayOfWeek, isAdjacentDay } = dateObject; + const { el, highlightedDates, isDateEnabled, multiple, showAdjacentDays } = this; + let _month = month; + let _year = year; + if (showAdjacentDays && isAdjacentDay && day !== null) { + if (day > 20) { + // Leading with the adjacent day from the previous month + // if its a adjacent day and is higher than '20' (last week even in feb) + if (month === 1) { + _year = year - 1; + _month = 12; + } else { + _month = month - 1; + } + } else if (day < 15) { + // Leading with the adjacent day from the next month + // if its a adjacent day and is lower than '15' (first two weeks) + if (month === 12) { + _year = year + 1; + _month = 1; + } else { + _month = month + 1; + } + } + } + + const referenceParts = { month: _month, day, year: _year, isAdjacentDay }; const isCalendarPadding = day === null; const { isActive, @@ -2259,7 +2296,7 @@ export class Datetime implements ComponentInterface { * Custom highlight styles should not override the style for selected dates, * nor apply to "filler days" at the start of the grid. */ - if (highlightedDates !== undefined && !isActive && day !== null) { + if (highlightedDates !== undefined && !isActive && day !== null && !isAdjacentDay) { dateStyle = getHighlightStyles(highlightedDates, dateIsoString, el); } @@ -2267,10 +2304,12 @@ export class Datetime implements ComponentInterface { // "Filler days" at the beginning of the grid should not get the calendar day // CSS parts added to them - if (!isCalendarPadding) { + if (!isCalendarPadding && !isAdjacentDay) { dateParts = `calendar-day${isActive ? ' active' : ''}${isToday ? ' today' : ''}${ isCalDayDisabled ? ' disabled' : '' }`; + } else if (isAdjacentDay) { + dateParts = `calendar-day${isCalDayDisabled ? ' disabled' : ''}`; } return ( @@ -2294,8 +2333,8 @@ export class Datetime implements ComponentInterface { }} tabindex="-1" data-day={day} - data-month={month} - data-year={year} + data-month={_month} + data-year={_year} data-index={index} data-day-of-week={dayOfWeek} disabled={isButtonDisabled} @@ -2305,6 +2344,7 @@ export class Datetime implements ComponentInterface { 'calendar-day-active': isActive, 'calendar-day-constrained': isCalDayConstrained, 'calendar-day-today': isToday, + 'calendar-day-adjacent-day': isAdjacentDay, }} part={dateParts} aria-hidden={isCalendarPadding ? 'true' : null} @@ -2315,30 +2355,27 @@ export class Datetime implements ComponentInterface { return; } - this.setWorkingParts({ - ...this.workingParts, - month, - day, - year, - }); - - // multiple only needs date info, so we can wipe out other fields like time - if (multiple) { - this.setActiveParts( - { - month, - day, - year, - }, - isActive - ); + if (isAdjacentDay) { + // The user selected a day outside the current month. Ignore this button, as the month will be re-rendered. + this.el.blur(); + this.activeParts = { ...activePart, ...referenceParts }; + this.animateToDate(referenceParts); + this.confirm(); } else { - this.setActiveParts({ - ...activePart, - month, - day, - year, + this.setWorkingParts({ + ...this.workingParts, + ...referenceParts, }); + + // Multiple only needs date info so we can wipe out other fields like time. + if (multiple) { + this.setActiveParts(referenceParts, isActive); + } else { + this.setActiveParts({ + ...activePart, + ...referenceParts, + }); + } } }} > diff --git a/core/src/components/datetime/test/basic/datetime.e2e.ts-snapshots/datetime-focus-selected-calendar-day-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/basic/datetime.e2e.ts-snapshots/datetime-focus-selected-calendar-day-md-ltr-Mobile-Firefox-linux.png index d444ea481e5..f85f714555e 100644 Binary files a/core/src/components/datetime/test/basic/datetime.e2e.ts-snapshots/datetime-focus-selected-calendar-day-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/basic/datetime.e2e.ts-snapshots/datetime-focus-selected-calendar-day-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-focus-selected-calendar-day-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-focus-selected-calendar-day-ios-ltr-Mobile-Firefox-linux.png index d8fe283317a..c663807d933 100644 Binary files a/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-focus-selected-calendar-day-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/datetime/test/custom/datetime.e2e.ts-snapshots/datetime-custom-focus-selected-calendar-day-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts new file mode 100644 index 00000000000..4aae6c29434 --- /dev/null +++ b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts @@ -0,0 +1,128 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * This behavior does not vary across directions + */ +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('datetime: show adjacent days'), () => { + test('should not have visual regressions', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#default'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days`)); + }); + + test('should not have visual regressions with a custom styled calendar', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#custom-calendar-days'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-custom-calendar`)); + }); + + test('should not have visual regressions with specific date disabled', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#specificDate'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-specific-date-disabled`)); + }); + + test('should not have visual regressions with weekends disabled', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#weekends'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-weekends-disabled`)); + }); + + test('should not have visual regressions with date range disabled', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#dateRange'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-date-range-disabled`)); + }); + + test('should not have visual regressions with month disabled', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#month'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-month-disabled`)); + }); + + test('should not have visual regressions with display specified', async ({ page }) => { + await page.goto('/src/components/datetime/test/show-adjacent-days', config); + await page.locator('.datetime-ready').first().waitFor(); + const datetime = page.locator('#display'); + await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-display`)); + }); + + test('should return the same date format on current month days and on adjacent days', async ({ page }) => { + await page.setContent( + ` + + `, + config + ); + + // Wait for the datetime to be ready. + await page.locator('.datetime-ready').waitFor(); + + const ionChange = await page.spyOnEvent('ionChange'); + + const calendarMonthYear = page.locator('ion-datetime .calendar-month-year'); + + /** + * Make sure to exclude adjacent days from the query since + * the previous/next month is rendered hidden. This causes + * the query to possibly return different results: one for + * the current month and one from the hidden previous/next + * month. + */ + const october20Button = page.locator( + '[data-month="10"][data-year="2022"][data-day="20"]:not(.calendar-day-adjacent-day)' + ); + + await october20Button.click(); + + await ionChange.next(); + await expect(ionChange).toHaveReceivedEventDetail({ + value: '2022-10-20T16:22:00', + }); + + const november1Button = page.locator( + '.calendar-day-adjacent-day[data-month="11"][data-year="2022"][data-day="1"]' + ); + + await november1Button.click(); + // Wait for the datetime to change the month since an adjacent day + // was clicked. + await page.waitForChanges(); + + // Wait for the title to update to the new month since it changes + // after the month animation finishes. + await expect(calendarMonthYear).toHaveText('November 2022'); + + await ionChange.next(); + await expect(ionChange).toHaveReceivedEventDetail({ + value: '2022-11-01T16:22:00', + }); + + /** + * Make sure to exclude adjacent days from the query since + * the previous/next month is rendered hidden. This causes + * the query to possibly return different results: one for + * the current month and one from the hidden previous/next + * month. + */ + const november22Button = page.locator( + '[data-month="11"][data-year="2022"][data-day="22"]:not(.calendar-day-adjacent-day)' + ); + + await november22Button.click(); + + await ionChange.next(); + await expect(ionChange).toHaveReceivedEventDetail({ + value: '2022-11-22T16:22:00', + }); + }); + }); +}); diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..1f4d7a23706 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..b901962f864 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..44736664e1d Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..579f4038595 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..edc84a8ac24 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..2716851e092 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-custom-calendar-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..046db126345 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..86f65d91799 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..c7c7a08d841 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..c66553592ad Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..694fdac1321 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..44f14c77ce8 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-date-range-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..0c1ccf53afd Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..d67765af393 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..bb7ae7a5a39 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..16ed7e9b7f4 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9de6935f65a Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..c5da3cf45c0 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-display-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d068b5bf9b8 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..94b2614cdf2 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..df3c27c3c93 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..7fd6fc5a210 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..971a4be2a60 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..2211a5db0aa Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f187be03f48 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..f3086f96207 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..8b1f3522c12 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..c0758a299e2 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..92ae494ccda Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..926419ca28d Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-month-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..db61f894d1e Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..89759eefd67 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..6c739f0d9fd Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a040e2689bc Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..23aff3d1d1d Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..98ed6e57670 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-specific-date-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..c7198f834b7 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..2e38980b08e Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..4d1679eb43d Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f5a990095e8 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..477cd04616a Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..70c94ed0941 Binary files /dev/null and b/core/src/components/datetime/test/show-adjacent-days/datetime.e2e.ts-snapshots/datetime-show-adjacent-days-weekends-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/datetime/test/show-adjacent-days/index.html b/core/src/components/datetime/test/show-adjacent-days/index.html new file mode 100644 index 00000000000..55d6dc2bfc1 --- /dev/null +++ b/core/src/components/datetime/test/show-adjacent-days/index.html @@ -0,0 +1,328 @@ + + + + + Datetime - Show Adjacent Days + + + + + + + + + + + + + Datetime - Show Adjacent Days + + + +
+
+

Inline Grid

+ +
+ +
+

Inline Grid: Custom Styles

+ +
+
+ +
+
+

Disable Specific Date

+ +
+ +
+

Disable Weekends

+ +
+ +
+

Disable Date Range

+ +
+ +
+

Disable Month

+ +
+ +
+

Change firstDayOfWeek

+ + +
+ FirstDayOfWeek: 1 +
+
+ +
+

DateTime format with IonChange Event (console)

+ +
+
+
+
+ + + + +

+ +
+
+
+
+ + + + diff --git a/core/src/components/datetime/utils/data.ts b/core/src/components/datetime/utils/data.ts index ffeb3600f12..bd6f328752e 100644 --- a/core/src/components/datetime/utils/data.ts +++ b/core/src/components/datetime/utils/data.ts @@ -102,8 +102,16 @@ export const getDaysOfWeek = (locale: string, mode: Mode, firstDayOfWeek = 0) => * the firstDayOfWeek value (Sunday by default) * using null values. */ -export const getDaysOfMonth = (month: number, year: number, firstDayOfWeek: number) => { +export const getDaysOfMonth = (month: number, year: number, firstDayOfWeek: number, showAdjacentDays = false) => { const numDays = getNumDaysInMonth(month, year); + let previousNumDays: number; //previous month number of days + if (month === 1) { + // If the current month is January, the previous month should be December of the previous year. + previousNumDays = getNumDaysInMonth(12, year - 1); + } else { + // Otherwise, the previous month should be the current month - 1 of the same year. + previousNumDays = getNumDaysInMonth(month - 1, year); + } const firstOfMonth = new Date(`${month}/1/${year}`).getDay(); /** @@ -128,13 +136,40 @@ export const getDaysOfMonth = (month: number, year: number, firstDayOfWeek: numb const offset = firstOfMonth >= firstDayOfWeek ? firstOfMonth - (firstDayOfWeek + 1) : 6 - (firstDayOfWeek - firstOfMonth); - let days = []; + let days: ( + | { + day: number; + dayOfWeek: number; + isAdjacentDay: boolean; + } + | { + day: null; + dayOfWeek: null; + isAdjacentDay: boolean; + } + )[] = []; for (let i = 1; i <= numDays; i++) { - days.push({ day: i, dayOfWeek: (offset + i) % 7 }); + days.push({ day: i, dayOfWeek: (offset + i) % 7, isAdjacentDay: false }); } - for (let i = 0; i <= offset; i++) { - days = [{ day: null, dayOfWeek: null }, ...days]; + if (showAdjacentDays) { + for (let i = 0; i <= offset; i++) { + // Using offset create previous month adjacent day, starting from last day + days = [{ day: previousNumDays - i, dayOfWeek: (previousNumDays - i) % 7, isAdjacentDay: true }, ...days]; + } + + // Calculate positiveOffset + // The calendar will display 42 days (6 rows of 7 columns) + // Knowing this the offset is 41 (we start at index 0) + // minus (the previous offset + the current month days) + const positiveOffset = 41 - (numDays + offset); + for (let i = 0; i < positiveOffset; i++) { + days.push({ day: i + 1, dayOfWeek: (numDays + offset + i) % 7, isAdjacentDay: true }); + } + } else { + for (let i = 0; i <= offset; i++) { + days = [{ day: null, dayOfWeek: null, isAdjacentDay: false }, ...days]; + } } return days; diff --git a/core/src/components/fab/test/custom-size/fab.e2e.ts-snapshots/fab-custom-size-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/fab/test/custom-size/fab.e2e.ts-snapshots/fab-custom-size-ios-ltr-Mobile-Chrome-linux.png index 14999134819..827736bfb14 100644 Binary files a/core/src/components/fab/test/custom-size/fab.e2e.ts-snapshots/fab-custom-size-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/fab/test/custom-size/fab.e2e.ts-snapshots/fab-custom-size-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/input-otp-interface.ts b/core/src/components/input-otp/input-otp-interface.ts new file mode 100644 index 00000000000..8fb289cfec4 --- /dev/null +++ b/core/src/components/input-otp/input-otp-interface.ts @@ -0,0 +1,23 @@ +/** + * Values are converted to strings when emitted which is + * why we do not have a `number` type here even though the + * `value` prop accepts a `number` type. + */ +export interface InputOtpInputEventDetail { + value?: string | null; + event?: Event; +} +export interface InputOtpChangeEventDetail { + value?: string | null; + event?: Event; +} + +export interface InputOtpCompleteEventDetail { + value?: string | null; + event?: Event; +} + +export interface InputOtpCustomEvent extends CustomEvent { + detail: T; + target: HTMLIonInputOtpElement; +} diff --git a/core/src/components/input-otp/input-otp.ios.scss b/core/src/components/input-otp/input-otp.ios.scss new file mode 100644 index 00000000000..8f0e8176784 --- /dev/null +++ b/core/src/components/input-otp/input-otp.ios.scss @@ -0,0 +1,20 @@ +@import "./input-otp"; +@import "../../themes/ionic.globals.ios"; + +// iOS Input OTP +// -------------------------------------------------- + +:host { + --border-width: #{$hairlines-width}; +} + +:host(.has-focus) .native-input:focus { + --border-width: 1px; +} + +// Fills +// -------------------------------------------------- + +:host(.input-otp-fill-outline) { + --border-color: #{$item-ios-border-color}; +} diff --git a/core/src/components/input-otp/input-otp.md.scss b/core/src/components/input-otp/input-otp.md.scss new file mode 100644 index 00000000000..f4e2b16e97e --- /dev/null +++ b/core/src/components/input-otp/input-otp.md.scss @@ -0,0 +1,20 @@ +@import "./input-otp"; +@import "../../themes/ionic.globals.md"; + +// Material Design Input OTP +// -------------------------------------------------- + +:host { + --border-width: 1px; +} + +:host(.has-focus) .native-input:focus { + --border-width: 2px; +} + +// Fills +// -------------------------------------------------- + +:host(.input-otp-fill-outline) { + --border-color: #{$background-color-step-300}; +} diff --git a/core/src/components/input-otp/input-otp.scss b/core/src/components/input-otp/input-otp.scss new file mode 100644 index 00000000000..2b8b12ee585 --- /dev/null +++ b/core/src/components/input-otp/input-otp.scss @@ -0,0 +1,307 @@ +@import "../../themes/ionic.globals"; + +// Input OTP +// -------------------------------------------------- + +:host { + /** + * @prop --background: Background color of the input boxes + * + * @prop --border-radius: Border radius of the input boxes + * + * @prop --border-width: Border width of the input boxes + * @prop --border-color: Border color of the input boxes + * + * @prop --color: Text color of the input + * + * @prop --margin-top: Top margin of the input group + * @prop --margin-end: Right margin if direction is left-to-right, and left margin if direction is right-to-left of the input group + * @prop --margin-bottom: Bottom margin of the input group + * @prop --margin-start: Left margin if direction is left-to-right, and right margin if direction is right-to-left of the input group + * + * @prop --padding-top: Top padding of the input group + * @prop --padding-end: Right padding if direction is left-to-right, and left padding if direction is right-to-left of the input group + * @prop --padding-bottom: Bottom padding of the input group + * @prop --padding-start: Left padding if direction is left-to-right, and right padding if direction is right-to-left of the input group + * + * @prop --height: Height of input boxes + * @prop --width: Width of input boxes + * @prop --min-width: Minimum width of input boxes + * + * @prop --separator-color: Color of the separator between boxes + * @prop --separator-width: Width of the separator between boxes + * @prop --separator-height: Height of the separator between boxes + * @prop --separator-border-radius: Border radius of the separator between boxes + * + * @prop --highlight-color-focused: The color of the highlight on the input when focused + * @prop --highlight-color-valid: The color of the highlight on the input when valid + * @prop --highlight-color-invalid: The color of the highlight on the input when invalid + */ + --margin-top: 0; + --margin-end: 0; + --margin-bottom: 0; + --margin-start: 0; + --padding-top: 16px; + --padding-end: 0; + --padding-bottom: 16px; + --padding-start: 0; + --color: initial; + --min-width: 40px; + --separator-width: 8px; + --separator-height: var(--separator-width); + --separator-border-radius: 999px; + --separator-color: #{$background-color-step-150}; + --highlight-color-focused: #{ion-color(primary, base)}; + --highlight-color-valid: #{ion-color(success, base)}; + --highlight-color-invalid: #{ion-color(danger, base)}; + + /** + * This is a private API that is used to switch + * out the highlight color based on the state + * of the component without having to write + * different selectors for different fill variants. + */ + --highlight-color: var(--highlight-color-focused); + + display: block; + position: relative; + + font-size: dynamic-font(14px); +} + +.input-otp-group { + @include margin(var(--margin-top), var(--margin-end), var(--margin-bottom), var(--margin-start)); + @include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start)); + + display: flex; + + align-items: center; + justify-content: center; +} + +.native-wrapper { + display: flex; + + align-items: center; + justify-content: center; + + min-width: var(--min-width); +} + +// Native Input +// ---------------------------------------------------------------- + +.native-input { + @include border-radius(var(--border-radius)); + + width: var(--width); + + // Required to shrink the input box width + min-width: inherit; + height: var(--height); + + border-width: var(--border-width); + border-style: solid; + border-color: var(--border-color); + + background: var(--background); + color: var(--color); + + font-size: inherit; + + text-align: center; + appearance: none; +} + +:host(.has-focus) .native-input { + caret-color: var(--highlight-color); +} + +// Input Description +// ---------------------------------------------------------------- + +.input-otp-description { + color: $text-color-step-300; + + font-size: dynamic-font(12px); + + line-height: dynamic-font(20px); + + text-align: center; +} + +.input-otp-description-hidden { + display: none; +} + +// Input Separator +// ---------------------------------------------------------------- + +.input-otp-separator { + @include border-radius(var(--separator-border-radius)); + + flex-shrink: 0; + + width: var(--separator-width); + height: var(--separator-height); + + background: var(--separator-color); +} + +// Sizes +// -------------------------------------------------- + +:host(.input-otp-size-small) { + --width: 40px; + --height: 40px; +} + +:host(.input-otp-size-small) .input-otp-group { + gap: 8px; +} + +:host(.input-otp-size-medium) { + --width: 48px; + --height: 48px; +} + +:host(.input-otp-size-large) { + --width: 56px; + --height: 56px; +} + +:host(.input-otp-size-medium) .input-otp-group, +:host(.input-otp-size-large) .input-otp-group { + gap: 12px; +} + +// Shapes +// -------------------------------------------------- + +:host(.input-otp-shape-round) { + --border-radius: 16px; +} + +:host(.input-otp-shape-soft) { + --border-radius: 8px; +} + +:host(.input-otp-shape-rectangular) { + --border-radius: 0; +} + +// Fills +// -------------------------------------------------- + +:host(.input-otp-fill-outline) { + --background: none; +} + +:host(.input-otp-fill-solid) { + --border-color: #{$background-color-step-50}; + --background: #{$background-color-step-50}; +} + +// States +// -------------------------------------------------- + +:host(.input-otp-disabled) { + --color: #{$text-color-step-650}; +} + +:host(.input-otp-fill-outline.input-otp-disabled) { + --background: #{$background-color-step-50}; + --border-color: #{$background-color-step-100}; +} + +:host(.input-otp-disabled), +:host(.input-otp-disabled) .native-input:disabled { + cursor: not-allowed; +} + +:host(.has-focus) .native-input:focus { + --border-color: var(--highlight-color); + + outline: none; +} + +:host(.input-otp-fill-outline.input-otp-readonly) { + --background: #{$background-color-step-50}; +} + +:host(.input-otp-fill-solid.input-otp-disabled), +:host(.input-otp-fill-solid.input-otp-readonly) { + --border-color: #{$background-color-step-100}; + --background: #{$background-color-step-100}; +} + +// Input Highlight +// ---------------------------------------------------------------- + +:host(.ion-touched.ion-invalid) { + --highlight-color: var(--highlight-color-invalid); +} + +/** + * The component highlight is only shown + * on focus, so we can safely set the valid + * color state when valid. If we + * set it when .has-focus is present then + * the highlight color would change + * from the valid color to the component's + * color during the transition after the + * component loses focus. + */ +:host(.ion-valid) { + --highlight-color: var(--highlight-color-valid); +} + +/** + * If the input has a validity state, the + * border should reflect that as a color. + * The invalid state should show if the input is + * invalid and has already been touched. + * The valid state should show if the input + * is valid, has already been touched, and + * is currently focused. Do not show the valid + * highlight when the input is blurred. + */ +:host(.has-focus.ion-valid), +:host(.ion-touched.ion-invalid) { + --border-color: var(--highlight-color); +} + +// Colors +// ---------------------------------------------------------------- + +:host(.ion-color) { + --highlight-color-focused: #{current-color(base)}; +} + +// Outline border should match the current color +// and the solid border should match when focused +:host(.input-otp-fill-outline.ion-color) .native-input, +:host(.input-otp-fill-solid.ion-color) .native-input:focus { + border-color: current-color(base, 0.6); +} + +// Invalid +:host(.input-otp-fill-outline.ion-color.ion-invalid) .native-input, +:host(.input-otp-fill-solid.ion-color.ion-invalid) .native-input, +:host(.input-otp-fill-outline.ion-color.has-focus.ion-invalid) .native-input, +:host(.input-otp-fill-solid.ion-color.has-focus.ion-invalid) .native-input { + border-color: ion-color(danger, base); +} + +// Valid +:host(.input-otp-fill-outline.ion-color.ion-valid) .native-input, +:host(.input-otp-fill-solid.ion-color.ion-valid) .native-input, +:host(.input-otp-fill-outline.ion-color.has-focus.ion-valid) .native-input, +:host(.input-otp-fill-solid.ion-color.has-focus.ion-valid) .native-input { + border-color: ion-color(success, base); +} + +// Outline & Disabled +:host(.input-otp-fill-outline.input-otp-disabled.ion-color) .native-input { + border-color: current-color(base, 0.3); +} diff --git a/core/src/components/input-otp/input-otp.tsx b/core/src/components/input-otp/input-otp.tsx new file mode 100644 index 00000000000..3e6cc3855b2 --- /dev/null +++ b/core/src/components/input-otp/input-otp.tsx @@ -0,0 +1,808 @@ +import type { ComponentInterface, EventEmitter } from '@stencil/core'; +import { Component, Element, Event, Fragment, Host, Prop, State, h, Watch } from '@stencil/core'; +import type { Attributes } from '@utils/helpers'; +import { inheritAriaAttributes } from '@utils/helpers'; +import { printIonWarning } from '@utils/logging'; +import { isRTL } from '@utils/rtl'; +import { createColorClasses } from '@utils/theme'; +import { Method } from 'ionicons/dist/types/stencil-public-runtime'; + +import { getIonMode } from '../../global/ionic-global'; +import type { Color } from '../../interface'; + +import type { + InputOtpChangeEventDetail, + InputOtpCompleteEventDetail, + InputOtpInputEventDetail, +} from './input-otp-interface'; + +@Component({ + tag: 'ion-input-otp', + styleUrls: { + ios: 'input-otp.ios.scss', + md: 'input-otp.md.scss', + }, + scoped: true, +}) +export class InputOTP implements ComponentInterface { + private inheritedAttributes: Attributes = {}; + private inputRefs: HTMLInputElement[] = []; + private inputId = `ion-input-otp-${inputIds++}`; + private parsedSeparators: number[] = []; + + /** + * Stores the initial value of the input when it receives focus. + * Used to determine if the value changed during the focus session + * to avoid emitting unnecessary change events on blur. + */ + private focusedValue?: string | number | null; + + /** + * Tracks whether the user is navigating through input boxes using keyboard navigation + * (arrow keys, tab) versus mouse clicks. This is used to determine the appropriate + * focus behavior when an input box is focused. + */ + private isKeyboardNavigation = false; + + @Element() el!: HTMLIonInputOtpElement; + + @State() private inputValues: string[] = []; + @State() hasFocus = false; + + /** + * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. + * Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. + */ + @Prop() autocapitalize = 'off'; + + /** + * The color to use from your application's color palette. + * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. + * For more information on colors, see [theming](/docs/theming/basics). + */ + @Prop({ reflect: true }) color?: Color; + + /** + * If `true`, the user cannot interact with the input. + */ + @Prop({ reflect: true }) disabled = false; + + /** + * The fill for the input boxes. If `"solid"` the input boxes will have a background. If + * `"outline"` the input boxes will be transparent with a border. + */ + @Prop() fill?: 'outline' | 'solid' = 'outline'; + + /** + * A hint to the browser for which keyboard to display. + * Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, + * `"email"`, `"numeric"`, `"decimal"`, and `"search"`. + * + * For numbers (type="number"): "numeric" + * For text (type="text"): "text" + */ + @Prop() inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'; + + /** + * The number of input boxes to display. + */ + @Prop() length = 4; + + /** + * A regex pattern string for allowed characters. Defaults based on type. + * + * For numbers (`type="number"`): `"[\p{N}]"` + * For text (`type="text"`): `"[\p{L}\p{N}]"` + */ + @Prop() pattern?: string; + + /** + * If `true`, the user cannot modify the value. + */ + @Prop({ reflect: true }) readonly = false; + + /** + * Where separators should be shown between input boxes. + * Can be a comma-separated string or an array of numbers. + * + * For example: + * `"3"` will show a separator after the 3rd input box. + * `[1,4]` will show a separator after the 1st and 4th input boxes. + * `"all"` will show a separator between every input box. + */ + @Prop() separators?: 'all' | string | number[]; + + /** + * The shape of the input boxes. + * If "round" they will have an increased border radius. + * If "rectangular" they will have no border radius. + * If "soft" they will have a soft border radius. + */ + @Prop() shape: 'round' | 'rectangular' | 'soft' = 'round'; + + /** + * The size of the input boxes. + */ + @Prop() size: 'small' | 'medium' | 'large' = 'medium'; + + /** + * The type of input allowed in the input boxes. + */ + @Prop() type: 'text' | 'number' = 'number'; + + /** + * The value of the input group. + */ + @Prop({ mutable: true }) value?: string | number | null = ''; + + /** + * The `ionInput` event is fired each time the user modifies the input's value. + * Unlike the `ionChange` event, the `ionInput` event is fired for each alteration + * to the input's value. This typically happens for each keystroke as the user types. + * + * For elements that accept text input (`type=text`, `type=tel`, etc.), the interface + * is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others, + * the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event). If + * the input is cleared on edit, the type is `null`. + */ + @Event() ionInput!: EventEmitter; + + /** + * The `ionChange` event is fired when the user modifies the input's value. + * Unlike the `ionInput` event, the `ionChange` event is only fired when changes + * are committed, not as the user types. + * + * The `ionChange` event fires when the `` component loses + * focus after its value has changed. + * + * This event will not emit when programmatically setting the `value` property. + */ + @Event() ionChange!: EventEmitter; + + /** + * Emitted when all input boxes have been filled with valid values. + */ + @Event() ionComplete!: EventEmitter; + + /** + * Emitted when the input group loses focus. + */ + @Event() ionBlur!: EventEmitter; + + /** + * Emitted when the input group has focus. + */ + @Event() ionFocus!: EventEmitter; + + /** + * Sets focus to an input box. + * @param index - The index of the input box to focus (0-based). + * If provided and the input box has a value, the input box at that index will be focused. + * Otherwise, the first empty input box or the last input if all are filled will be focused. + */ + @Method() + async setFocus(index?: number) { + if (typeof index === 'number') { + const validIndex = Math.max(0, Math.min(index, this.length - 1)); + this.inputRefs[validIndex]?.focus(); + } else { + const tabbableIndex = this.getTabbableIndex(); + this.inputRefs[tabbableIndex]?.focus(); + } + } + + @Watch('value') + valueChanged() { + this.initializeValues(); + this.updateTabIndexes(); + } + + /** + * Processes the separators prop into an array of numbers. + * + * If the separators prop is not provided, returns an empty array. + * If the separators prop is 'all', returns an array of all valid positions (1 to length-1). + * If the separators prop is an array, returns it as is. + * If the separators prop is a string, splits it by commas and parses each part as a number. + * + * If the separators are greater than the input length, it will warn and ignore the separators. + */ + @Watch('separators') + @Watch('length') + private processSeparators() { + const { separators, length } = this; + if (separators === undefined) { + this.parsedSeparators = []; + return; + } + + if (typeof separators === 'string' && separators !== 'all') { + const isValidFormat = /^(\d+)(,\d+)*$/.test(separators); + if (!isValidFormat) { + printIonWarning( + `[ion-input-otp] - Invalid separators format. Expected a comma-separated list of numbers, an array of numbers, or "all". Received: ${separators}`, + this.el + ); + this.parsedSeparators = []; + return; + } + } + + let separatorValues: number[]; + if (separators === 'all') { + separatorValues = Array.from({ length: length - 1 }, (_, i) => i + 1); + } else if (Array.isArray(separators)) { + separatorValues = separators; + } else { + separatorValues = separators + .split(',') + .map((pos) => parseInt(pos, 10)) + .filter((pos) => !isNaN(pos)); + } + + // Check for duplicate separator positions + const duplicates = separatorValues.filter((pos, index) => separatorValues.indexOf(pos) !== index); + if (duplicates.length > 0) { + printIonWarning( + `[ion-input-otp] - Duplicate separator positions are not allowed. Received: ${separators}`, + this.el + ); + } + + const invalidSeparators = separatorValues.filter((pos) => pos > length); + if (invalidSeparators.length > 0) { + printIonWarning( + `[ion-input-otp] - The following separator positions are greater than the input length (${length}): ${invalidSeparators.join( + ', ' + )}. These separators will be ignored.`, + this.el + ); + } + + this.parsedSeparators = separatorValues.filter((pos) => pos <= length); + } + + componentWillLoad() { + this.inheritedAttributes = inheritAriaAttributes(this.el); + this.processSeparators(); + this.initializeValues(); + } + + componentDidLoad() { + this.updateTabIndexes(); + } + + /** + * Get the regex pattern for allowed characters. + * If a pattern is provided, use it to create a regex pattern + * Otherwise, use the default regex pattern based on type + */ + private get validKeyPattern(): RegExp { + return new RegExp(`^${this.getPattern()}$`, 'u'); + } + + /** + * Gets the string pattern to pass to the input element + * and use in the regex for allowed characters. + */ + private getPattern(): string { + const { pattern, type } = this; + if (pattern) { + return pattern; + } + return type === 'number' ? '[\\p{N}]' : '[\\p{L}\\p{N}]'; + } + + /** + * Get the default value for inputmode. + * If inputmode is provided, use it. + * Otherwise, use the default inputmode based on type + */ + private getInputmode(): string { + const { inputmode } = this; + if (inputmode) { + return inputmode; + } + + if (this.type == 'number') { + return 'numeric'; + } else { + return 'text'; + } + } + + /** + * Initializes the input values array based on the current value prop. + * This splits the value into individual characters and validates them against + * the allowed pattern. The values are then used as the values in the native + * input boxes and the value of the input group is updated. + */ + private initializeValues() { + // Clear all input values + this.inputValues = Array(this.length).fill(''); + + // If the value is null, undefined, or an empty string, return + if (this.value == null || String(this.value).length === 0) { + return; + } + + // Split the value into individual characters and validate + // them against the allowed pattern + const chars = String(this.value).split('').slice(0, this.length); + chars.forEach((char, index) => { + if (this.validKeyPattern.test(char)) { + this.inputValues[index] = char; + } + }); + // Update the value without emitting events + this.value = this.inputValues.join(''); + } + + /** + * Updates the value of the input group. + * This updates the value of the input group and emits an `ionChange` event. + * If all of the input boxes are filled, it emits an `ionComplete` event. + */ + private updateValue(event: Event) { + const { inputValues, length } = this; + const newValue = inputValues.join(''); + this.value = newValue; + this.emitIonInput(event); + if (newValue.length === length) { + this.ionComplete.emit({ value: newValue }); + } + } + + /** + * Emits an `ionChange` event. + * This API should be called for user committed changes. + * This API should not be used for external value changes. + */ + private emitIonChange(event: Event) { + const { value } = this; + + // Checks for both null and undefined values + const newValue = value == null ? value : value.toString(); + + this.ionChange.emit({ value: newValue, event }); + } + + /** + * Emits an `ionInput` event. + * This is used to emit the input value when the user types, + * backspaces, or pastes. + */ + private emitIonInput(event: Event) { + const { value } = this; + + // Checks for both null and undefined values + const newValue = value == null ? value : value.toString(); + + this.ionInput.emit({ value: newValue, event }); + } + + /** + * Handles the focus behavior for the input OTP component. + * + * Focus behavior: + * 1. Keyboard navigation: Allow normal focus movement + * 2. Mouse click: + * - If clicked box has value: Focus that box + * - If clicked box is empty: Focus first empty box + * + * Emits the `ionFocus` event when the input group gains focus. + */ + private onFocus = (index: number) => (event: FocusEvent) => { + const { inputRefs } = this; + // Only emit ionFocus and set the focusedValue when the + // component first gains focus + if (!this.hasFocus) { + this.ionFocus.emit(event); + this.focusedValue = this.value; + } + this.hasFocus = true; + + let finalIndex = index; + + if (!this.isKeyboardNavigation) { + // If the clicked box has a value, focus it + // Otherwise focus the first empty box + const targetIndex = this.inputValues[index] ? index : this.getFirstEmptyIndex(); + finalIndex = targetIndex === -1 ? this.length - 1 : targetIndex; + + // Focus the target box + this.inputRefs[finalIndex]?.focus(); + } + + // Update tabIndexes to match the focused box + inputRefs.forEach((input, i) => { + input.tabIndex = i === finalIndex ? 0 : -1; + }); + + // Reset the keyboard navigation flag + this.isKeyboardNavigation = false; + }; + + /** + * Handles the blur behavior for the input OTP component. + * Emits the `ionBlur` event when the input group loses focus. + */ + private onBlur = (event: FocusEvent) => { + const { inputRefs } = this; + const relatedTarget = event.relatedTarget as HTMLElement; + + // Do not emit blur if we're moving to another input box in the same component + const isInternalFocus = relatedTarget != null && inputRefs.includes(relatedTarget as HTMLInputElement); + + if (!isInternalFocus) { + this.hasFocus = false; + + // Reset tabIndexes when focus leaves the component + this.updateTabIndexes(); + + // Always emit ionBlur when focus leaves the component + this.ionBlur.emit(event); + + // Only emit ionChange if the value has actually changed + if (this.focusedValue !== this.value) { + this.emitIonChange(event); + } + } + }; + + /** + * Focuses the next input box. + */ + private focusNext(currentIndex: number) { + const { inputRefs, length } = this; + if (currentIndex < length - 1) { + inputRefs[currentIndex + 1]?.focus(); + } + } + + /** + * Focuses the previous input box. + */ + private focusPrevious(currentIndex: number) { + const { inputRefs } = this; + if (currentIndex > 0) { + inputRefs[currentIndex - 1]?.focus(); + } + } + + /** + * Searches through the input values and returns the index + * of the first empty input. + * Returns -1 if all inputs are filled. + */ + private getFirstEmptyIndex() { + const { inputValues, length } = this; + // Create an array of the same length as the input OTP + // and fill it with the input values + const values = Array.from({ length }, (_, i) => inputValues[i] || ''); + return values.findIndex((value) => !value || value === '') ?? -1; + } + + /** + * Returns the index of the input that should be tabbed to. + * If all inputs are filled, returns the last input's index. + * Otherwise, returns the index of the first empty input. + */ + private getTabbableIndex() { + const { length } = this; + const firstEmptyIndex = this.getFirstEmptyIndex(); + return firstEmptyIndex === -1 ? length - 1 : firstEmptyIndex; + } + + /** + * Updates the tabIndexes for the input boxes. + * This is used to ensure that the correct input is + * focused when the user navigates using the tab key. + */ + private updateTabIndexes() { + const { inputRefs, inputValues, length } = this; + + // Find first empty index after any filled boxes + let firstEmptyIndex = -1; + for (let i = 0; i < length; i++) { + if (!inputValues[i] || inputValues[i] === '') { + firstEmptyIndex = i; + break; + } + } + + // Update tabIndex and aria-hidden for all inputs + inputRefs.forEach((input, index) => { + const shouldBeTabbable = firstEmptyIndex === -1 ? index === length - 1 : firstEmptyIndex === index; + + input.tabIndex = shouldBeTabbable ? 0 : -1; + + // If the input is empty and not the first empty input, + // it should be hidden from screen readers. + const isEmpty = !inputValues[index] || inputValues[index] === ''; + input.setAttribute('aria-hidden', isEmpty && !shouldBeTabbable ? 'true' : 'false'); + }); + } + + /** + * Handles keyboard navigation and input for the OTP component. + * + * Navigation: + * - Backspace: Clears current input and moves to previous box if empty + * - Arrow Left/Right: Moves focus between input boxes + * - Tab: Allows normal tab navigation between components + * + * Input Behavior: + * - Validates input against the allowed pattern + * - When entering a key in a filled box: + * - Shifts existing values right if there is room + * - Updates the value of the input group + * - Prevents default behavior to avoid automatic focus shift + */ + private onKeyDown = (index: number) => (event: KeyboardEvent) => { + const { length } = this; + const rtl = isRTL(this.el); + const input = event.target as HTMLInputElement; + + // Meta shortcuts are used to copy, paste, and select text + // We don't want to handle these keys here + const metaShortcuts = ['a', 'c', 'v', 'x', 'r', 'z', 'y']; + const isTextSelection = input.selectionStart !== input.selectionEnd; + + // Return if the key is a meta shortcut or the input value + // text is selected and let the onPaste / onInput handler manage it + if (isTextSelection || ((event.metaKey || event.ctrlKey) && metaShortcuts.includes(event.key.toLowerCase()))) { + return; + } + + if (event.key === 'Backspace') { + if (this.inputValues[index]) { + // Shift all values to the right of the current index left by one + for (let i = index; i < length - 1; i++) { + this.inputValues[i] = this.inputValues[i + 1]; + } + + // Clear the last box + this.inputValues[length - 1] = ''; + + // Update all inputRefs to match inputValues + for (let i = 0; i < length; i++) { + this.inputRefs[i].value = this.inputValues[i] || ''; + } + + this.updateValue(event); + event.preventDefault(); + } else if (!this.inputValues[index] && index > 0) { + // If current input is empty, move to previous input + this.focusPrevious(index); + } + } else if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + this.isKeyboardNavigation = true; + event.preventDefault(); + const isLeft = event.key === 'ArrowLeft'; + const shouldMoveNext = (isLeft && rtl) || (!isLeft && !rtl); + + // Only allow moving to the next input if the current has a value + if (shouldMoveNext) { + if (this.inputValues[index] && index < length - 1) { + this.focusNext(index); + } + } else { + this.focusPrevious(index); + } + } else if (event.key === 'Tab') { + this.isKeyboardNavigation = true; + // Let all tab events proceed normally + return; + } + + // If the input box contains a value and the key being + // entered is a valid key for the input box update the value + // and shift the values to the right if there is room. + if (this.inputValues[index] && this.validKeyPattern.test(event.key)) { + if (!this.inputValues[length - 1]) { + for (let i = length - 1; i > index; i--) { + this.inputValues[i] = this.inputValues[i - 1]; + this.inputRefs[i].value = this.inputValues[i] || ''; + } + } + this.inputValues[index] = event.key; + this.inputRefs[index].value = event.key; + this.updateValue(event); + + // Prevent default to avoid the browser from + // automatically moving the focus to the next input + event.preventDefault(); + } + }; + + private onInput = (index: number) => (event: InputEvent) => { + const { length, validKeyPattern } = this; + const value = (event.target as HTMLInputElement).value; + + // If the value is longer than 1 character (autofill), split it into + // characters and filter out invalid ones + if (value.length > 1) { + const validChars = value + .split('') + .filter((char) => validKeyPattern.test(char)) + .slice(0, length); + + // If there are no valid characters coming from the + // autofill, all input refs have to be cleared after the + // browser has finished the autofill behavior + if (validChars.length === 0) { + requestAnimationFrame(() => { + this.inputRefs.forEach((input) => { + input.value = ''; + }); + }); + } + + // Update the value of the input group and emit the input change event + this.value = validChars.join(''); + this.updateValue(event); + + // Focus the first empty input box or the last input box if all boxes + // are filled after a small delay to ensure the input boxes have been + // updated before moving the focus + setTimeout(() => { + const nextIndex = validChars.length < length ? validChars.length : length - 1; + this.inputRefs[nextIndex]?.focus(); + }, 20); + + return; + } + + // Only allow input if it matches the pattern + if (value.length > 0 && !validKeyPattern.test(value)) { + this.inputRefs[index].value = ''; + this.inputValues[index] = ''; + return; + } + + // For single character input, fill the current box + this.inputValues[index] = value; + this.updateValue(event); + + if (value.length > 0) { + this.focusNext(index); + } + }; + + /** + * Handles pasting text into the input OTP component. + * This function prevents the default paste behavior and + * validates the pasted text against the allowed pattern. + * It then updates the value of the input group and focuses + * the next empty input after pasting. + */ + private onPaste = (event: ClipboardEvent) => { + const { inputRefs, length, validKeyPattern } = this; + + event.preventDefault(); + + const pastedText = event.clipboardData?.getData('text'); + + // If there is no pasted text, still emit the input change event + // because this is how the native input element behaves + // but return early because there is nothing to paste. + if (!pastedText) { + this.emitIonInput(event); + return; + } + + const validChars = pastedText + .split('') + .filter((char) => validKeyPattern.test(char)) + .slice(0, length); + + // Always paste starting at the first box + validChars.forEach((char, index) => { + if (index < length) { + this.inputRefs[index].value = char; + this.inputValues[index] = char; + } + }); + + // Update the value so that all input boxes are updated + this.value = validChars.join(''); + this.updateValue(event); + + // Focus the next empty input after pasting + // If all boxes are filled, focus the last input + const nextEmptyIndex = validChars.length; + if (nextEmptyIndex < length) { + inputRefs[nextEmptyIndex]?.focus(); + } else { + inputRefs[length - 1]?.focus(); + } + }; + + /** + * Determines if a separator should be shown for a given index by + * checking if the index is included in the parsed separators array. + */ + private showSeparator(index: number) { + const { length } = this; + return this.parsedSeparators.includes(index + 1) && index < length - 1; + } + + render() { + const { + autocapitalize, + color, + disabled, + el, + fill, + hasFocus, + inheritedAttributes, + inputId, + inputRefs, + inputValues, + length, + readonly, + shape, + size, + } = this; + const mode = getIonMode(this); + const inputmode = this.getInputmode(); + const tabbableIndex = this.getTabbableIndex(); + const pattern = this.getPattern(); + const hasDescription = el.querySelector('.input-otp-description')?.textContent?.trim() !== ''; + + return ( + +
+ {Array.from({ length }).map((_, index) => ( + <> +
+ (inputRefs[index] = el as HTMLInputElement)} + onInput={this.onInput(index)} + onBlur={this.onBlur} + onFocus={this.onFocus(index)} + onKeyDown={this.onKeyDown(index)} + onPaste={this.onPaste} + /> +
+ {this.showSeparator(index) &&
} + + ))} +
+
+ +
+ + ); + } +} + +let inputIds = 0; diff --git a/core/src/components/input-otp/test/a11y/input-otp.e2e.ts b/core/src/components/input-otp/test/a11y/input-otp.e2e.ts new file mode 100644 index 00000000000..88b0be04800 --- /dev/null +++ b/core/src/components/input-otp/test/a11y/input-otp.e2e.ts @@ -0,0 +1,109 @@ +import AxeBuilder from '@axe-core/playwright'; +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * Functionality is the same across modes + */ +configs().forEach(({ title, config }) => { + test.describe(title('input-otp: a11y'), () => { + test('should not have accessibility violations', async ({ page }) => { + await page.setContent( + ` +
+ +
+ `, + config + ); + + const results = await new AxeBuilder({ page }).analyze(); + expect(results.violations).toEqual([]); + }); + + test('should render with correct aria attributes on initial load', async ({ page }) => { + await page.setContent(``, config); + + const inputOtpGroup = page.locator('ion-input-otp .input-otp-group'); + await expect(inputOtpGroup).toHaveAttribute('aria-label', 'One-time password input'); + + const inputBoxes = page.locator('ion-input-otp input'); + + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-hidden', 'true'); + await expect(inputBoxes.nth(2)).toHaveAttribute('aria-hidden', 'true'); + await expect(inputBoxes.nth(3)).toHaveAttribute('aria-hidden', 'true'); + }); + + test('should update aria-hidden when value is set', async ({ page }) => { + await page.setContent(``, config); + + const inputBoxes = page.locator('ion-input-otp input'); + + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(2)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(3)).toHaveAttribute('aria-hidden', 'true'); + }); + + test('should update aria-hidden when typing a value', async ({ page }) => { + await page.setContent(``, config); + + const inputBoxes = page.locator('ion-input-otp input'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('123'); + + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(2)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(3)).toHaveAttribute('aria-hidden', 'false'); + }); + + test('should update aria-hidden when value is cleared using backspace', async ({ page }) => { + await page.setContent(``, config); + + const inputBoxes = page.locator('ion-input-otp input'); + + await page.keyboard.press('Tab'); + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-hidden', 'true'); + await expect(inputBoxes.nth(2)).toHaveAttribute('aria-hidden', 'true'); + await expect(inputBoxes.nth(3)).toHaveAttribute('aria-hidden', 'true'); + }); + + test('should update aria-hidden when value is set after initialization', async ({ page }) => { + await page.setContent(``, config); + + await page.evaluate(() => { + const inputOtp = document.querySelector('ion-input-otp'); + if (inputOtp) { + inputOtp.value = '12'; + } + }); + + const inputBoxes = page.locator('ion-input-otp input'); + + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(2)).toHaveAttribute('aria-hidden', 'false'); + await expect(inputBoxes.nth(3)).toHaveAttribute('aria-hidden', 'true'); + }); + + test('should update aria-label and aria-labelledby when set on host', async ({ page }) => { + await page.setContent( + ``, + config + ); + + const inputOtpGroup = page.locator('ion-input-otp .input-otp-group'); + await expect(inputOtpGroup).toHaveAttribute('aria-label', 'Custom label'); + await expect(inputOtpGroup).toHaveAttribute('aria-labelledby', 'my-label'); + }); + }); +}); diff --git a/core/src/components/input-otp/test/basic/index.html b/core/src/components/input-otp/test/basic/index.html new file mode 100644 index 00000000000..71e31de7aff --- /dev/null +++ b/core/src/components/input-otp/test/basic/index.html @@ -0,0 +1,125 @@ + + + + + Input OTP - Basic + + + + + + + + + + + + + + + Input OTP - Basic + + + + +
+
+

Default

+ Didn't get a code? Resend the code + Didn't get a code? Resend the code + + Didn't get a code? Resend the code + + Didn't get a code? Resend the code +
+ +
+

Types

+ Numbers only + + Letters and numbers + + + Custom Pattern: a-f and A-F + + + Custom Pattern: D-L + +
+ +
+

Disabled

+ Description + Description + +

Readonly

+ Description + Description +
+ +
+

Invalid / Touched

+ Description + Description +

Valid / Focused

+ Description + Description +
+
+
+ + +
+ + diff --git a/core/src/components/input-otp/test/basic/input-otp.e2e.ts b/core/src/components/input-otp/test/basic/input-otp.e2e.ts new file mode 100644 index 00000000000..2a50c1abd5c --- /dev/null +++ b/core/src/components/input-otp/test/basic/input-otp.e2e.ts @@ -0,0 +1,1053 @@ +import { expect } from '@playwright/test'; +import type { Locator } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * Simulates an autofill event in an input element with the given value + */ +async function simulateAutofill(input: any, value: string) { + await input.evaluate((input: any, value: string) => { + (input as HTMLInputElement).value = value; + input.dispatchEvent(new Event('input', { bubbles: true })); + }, value); +} + +/** + * Simulates a paste event in an input element with the given value + */ +async function simulatePaste(input: any, value: string) { + await input.evaluate((input: any, value: string) => { + const event = new ClipboardEvent('paste', { + bubbles: true, + cancelable: true, + clipboardData: new DataTransfer(), + }); + if (event.clipboardData) { + event.clipboardData.setData('text', value); + } + input.dispatchEvent(event); + }, value); +} + +/** + * Helper function to verify input values in both the input + * boxes and the input-otp component's value property + */ +async function verifyInputValues(inputOtp: Locator, expectedValues: string[]) { + const inputBoxes = inputOtp.locator('input'); + for (let i = 0; i < expectedValues.length; i++) { + await expect(inputBoxes.nth(i)).toHaveValue(expectedValues[i]); + } + + // Concatenate the expected values and check the JS property + const concatenatedValue = expectedValues.join(''); + await expect(inputOtp).toHaveJSProperty('value', concatenatedValue); +} + +/** + * Functionality is the same across modes + */ +configs({ modes: ['ios'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: basic functionality'), () => { + test('should render with 4 input boxes and a default value', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const inputBoxes = page.locator('ion-input-otp input'); + await expect(inputBoxes).toHaveCount(4); + + await verifyInputValues(inputOtp, ['1', '2', '', '']); + }); + + test('should render with 8 input boxes when length is set to 8 and a default value', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const inputBoxes = page.locator('ion-input-otp input'); + await expect(inputBoxes).toHaveCount(8); + + await verifyInputValues(inputOtp, ['1', '2', '3', '4', '5', '6', '7', '8']); + }); + + test('should accept numbers only by default', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('A2e468'); + + await verifyInputValues(inputOtp, ['2', '4', '6', '8']); + }); + + test('should accept Eastern Arabic numerals by default', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('١٢٣٤'); + + // Because Arabic is a right-to-left script, JavaScript's handling of RTL text + // causes the array values to be reversed while input boxes maintain LTR order. + // We reverse our expected values to match this behavior. + await verifyInputValues(inputOtp, ['٤', '٣', '٢', '١'].reverse()); + }); + + test('should accept only Western Arabic numerals when pattern is set to [0-9]', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('12٣٤34'); + + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + }); + + test('should accept Latin characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('A2-B5'); + + await verifyInputValues(inputOtp, ['A', '2', 'B', '5']); + }); + + test('should accept accented Latin characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('áéíó'); + + await verifyInputValues(inputOtp, ['á', 'é', 'í', 'ó']); + }); + + test('should accept Cyrillic characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('АбвГ'); + + await verifyInputValues(inputOtp, ['А', 'б', 'в', 'Г']); + }); + + test('should accept Chinese characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('中国北京'); + + await verifyInputValues(inputOtp, ['中', '国', '北', '京']); + }); + + test('should accept Japanese characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('ひらがな'); + + await verifyInputValues(inputOtp, ['ひ', 'ら', 'が', 'な']); + }); + + test('should accept Korean characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('안녕하세'); + + await verifyInputValues(inputOtp, ['안', '녕', '하', '세']); + }); + + test('should accept Arabic characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('أبجد'); + + // Because Arabic is a right-to-left script, JavaScript's handling of RTL text + // causes the array values to be reversed while input boxes maintain LTR order. + // We reverse our expected values to match this behavior. + await verifyInputValues(inputOtp, ['د', 'ج', 'ب', 'أ'].reverse()); + }); + + test('should accept mixed language characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('A漢字Б'); + + await verifyInputValues(inputOtp, ['A', '漢', '字', 'Б']); + }); + + test('should reject special characters when type is text', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('!@#$%^&*()-,:;./?+'); + + await verifyInputValues(inputOtp, ['', '', '', '']); + }); + + test('should accept custom pattern of lowercase and uppercase letters when pattern is set', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('aGBZfD'); + + await verifyInputValues(inputOtp, ['a', 'B', 'f', 'D']); + }); + + test('should accept custom pattern of uppercase letters only when pattern is set', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('abcdABCDEFG'); + + await verifyInputValues(inputOtp, ['D', 'E', 'F', 'G']); + }); + + test('should accept custom pattern of all characters when pattern is set', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('*#.!'); + + await verifyInputValues(inputOtp, ['*', '#', '.', '!']); + }); + + test('should accept only Latin characters and numbers when pattern is set', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('Ab中国北京12'); + + await verifyInputValues(inputOtp, ['A', 'b', '1', '2']); + }); + + test('should accept only Cyrillic characters when pattern is set', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('АбABC123вГ'); + + await verifyInputValues(inputOtp, ['А', 'б', 'в', 'Г']); + }); + + test('should accept only Chinese characters when pattern is set', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('中国ABC123北京'); + + await verifyInputValues(inputOtp, ['中', '国', '北', '京']); + }); + + test('should accept only Japanese characters when pattern is set', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('ひらABC123がな'); + + await verifyInputValues(inputOtp, ['ひ', 'ら', 'が', 'な']); + }); + + test('should accept only Korean characters when pattern is set', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('안녕ABC123하세'); + + await verifyInputValues(inputOtp, ['안', '녕', '하', '세']); + }); + + test('should accept only Arabic characters when pattern is set', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + // We need to type the numbers separately because the browser + // does not properly handle the script text when mixed with numbers + await page.keyboard.type('123'); + await page.keyboard.type('أبجد'); + + // Because Arabic is a right-to-left script, JavaScript's handling of RTL text + // causes the array values to be reversed while input boxes maintain LTR order. + // We reverse our expected values to match this behavior. + await verifyInputValues(inputOtp, ['د', 'ج', 'ب', 'أ'].reverse()); + }); + }); + + test.describe(title('input-otp: input functionality'), () => { + test('should update the input value when typing 4 digits from the 1st box', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + const inputOtp = page.locator('ion-input-otp'); + + await verifyInputValues(inputOtp, ['', '', '', '']); + + await page.keyboard.type('12'); + + await verifyInputValues(inputOtp, ['1', '2', '', '']); + + await page.keyboard.type('34'); + + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + }); + + test('should update the 1st input value when typing in the 3rd box', async ({ page }) => { + await page.setContent(`Description`, config); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await thirdInput.focus(); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + + await page.keyboard.type('1'); + + await verifyInputValues(inputOtp, ['1', '', '', '']); + + // Focus should be on the 2nd input box + await expect(inputBoxes.nth(1)).toBeFocused(); + }); + + test('should update the 3rd input value and shift the values to the right when typing in the 3rd box containing a value', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await thirdInput.focus(); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + + await page.keyboard.type('9'); + + await verifyInputValues(inputOtp, ['1', '2', '9', '3']); + + // Focus should remain on the 3rd input box + await expect(inputBoxes.nth(2)).toBeFocused(); + }); + + test('should update the 2nd input value when typing in the 2nd box containing a value', async ({ page }) => { + await page.setContent(`Description`, config); + + const secondInput = page.locator('ion-input-otp input').nth(1); + await secondInput.focus(); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + + await page.keyboard.type('9'); + + await verifyInputValues(inputOtp, ['1', '9', '3', '4']); + + // Focus should remain on the 2nd input box + await expect(inputBoxes.nth(1)).toBeFocused(); + }); + + test('should not shift values right when selecting the text in the 2nd input box', async ({ page }) => { + await page.setContent(`Description`, config); + + const secondInput = page.locator('ion-input-otp input').nth(1); + await secondInput.focus(); + await secondInput.selectText(); + + const inputOtp = page.locator('ion-input-otp'); + + await page.keyboard.type('9'); + + await verifyInputValues(inputOtp, ['1', '9', '3', '']); + }); + }); + + test.describe(title('input-otp: autofill functionality'), () => { + test('should handle autofill correctly', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await simulateAutofill(firstInput, '1234'); + + const inputOtp = page.locator('ion-input-otp'); + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + + const lastInput = page.locator('ion-input-otp input').last(); + await expect(lastInput).toBeFocused(); + }); + + test('should handle autofill correctly when it exceeds the length', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await simulateAutofill(firstInput, '123456'); + + const inputOtp = page.locator('ion-input-otp'); + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + + const lastInput = page.locator('ion-input-otp input').last(); + await expect(lastInput).toBeFocused(); + }); + + test('should handle autofill correctly when it is less than the length', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await simulateAutofill(firstInput, '12'); + + const inputOtp = page.locator('ion-input-otp'); + await verifyInputValues(inputOtp, ['1', '2', '', '']); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await expect(thirdInput).toBeFocused(); + }); + + test('should handle autofill correctly when using autofill after typing 1 character', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('9'); + + const secondInput = page.locator('ion-input-otp input').nth(1); + await secondInput.focus(); + + await simulateAutofill(secondInput, '1234'); + + const inputOtp = page.locator('ion-input-otp'); + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + + const lastInput = page.locator('ion-input-otp input').last(); + await expect(lastInput).toBeFocused(); + }); + + test('should handle autofill correctly when autofill value contains invalid characters', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await simulateAutofill(firstInput, '1234'); + + const inputOtp = page.locator('ion-input-otp'); + await verifyInputValues(inputOtp, ['', '', '', '']); + + await expect(firstInput).toBeFocused(); + }); + }); + + test.describe(title('input-otp: focus functionality'), () => { + test('should focus the first input box when tabbed to', async ({ page }) => { + await page.setContent(`Description`, config); + + await page.keyboard.press('Tab'); + + const firstInput = page.locator('ion-input-otp input').first(); + await expect(firstInput).toBeFocused(); + }); + + test('should focus the third input box when tabbed to with a default value of 2 digits', async ({ page }) => { + await page.setContent(`Description`, config); + + await page.keyboard.press('Tab'); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await expect(thirdInput).toBeFocused(); + }); + + test('should focus the last input box when tabbed to with a default value of 4 digits', async ({ page }) => { + await page.setContent(`Description`, config); + + await page.keyboard.press('Tab'); + + const lastInput = page.locator('ion-input-otp input').last(); + await expect(lastInput).toBeFocused(); + }); + + test('should focus the next input otp component when tabbed from the 2nd input box', async ({ page }) => { + await page.setContent( + ` + Description + Description + `, + config + ); + + await page.keyboard.press('Tab'); + await page.keyboard.press('ArrowLeft'); + await page.keyboard.press('Tab'); + + const secondInputOtpFirstInput = page.locator('#second input').first(); + await expect(secondInputOtpFirstInput).toBeFocused(); + }); + + test('should focus the first input box when clicking on the 2nd input box without a value', async ({ page }) => { + await page.setContent(`Description`, config); + + const secondInput = page.locator('ion-input-otp input').nth(1); + await secondInput.click(); + + const firstInput = page.locator('ion-input-otp input').first(); + await expect(firstInput).toBeFocused(); + }); + }); + + test.describe(title('input-otp: backspace functionality'), () => { + test('should backspace the first input box when backspace is pressed twice from the 2nd input box and the first input box has a value', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + await page.keyboard.press('Tab'); + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + + await verifyInputValues(inputOtp, ['', '', '', '']); + }); + + test('should backspace the last input box when backspace is pressed and all values are filled', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + await page.keyboard.press('Tab'); + await page.keyboard.press('Backspace'); + + await verifyInputValues(inputOtp, ['1', '2', '3', '']); + }); + + test('should backspace the 2nd input box and fill it with the 3rd value when backspace is pressed and 3 values are filled', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + await page.keyboard.press('Tab'); + + const isRTL = await page.evaluate(() => document.dir === 'rtl'); + if (isRTL) { + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('ArrowRight'); + } else { + await page.keyboard.press('ArrowLeft'); + await page.keyboard.press('ArrowLeft'); + } + await page.keyboard.press('Backspace'); + + const inputOtp = page.locator('ion-input-otp'); + await verifyInputValues(inputOtp, ['1', '3', '', '']); + }); + }); + + test.describe(title('input-otp: paste functionality'), () => { + test('should paste text into the first and second input box when pasting 2 digits', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await simulatePaste(firstInput, '12'); + + const inputOtp = page.locator('ion-input-otp'); + + const inputBoxes = page.locator('ion-input-otp input'); + await verifyInputValues(inputOtp, ['1', '2', '', '']); + + // Focus should be on the 3rd input box + await expect(inputBoxes.nth(2)).toBeFocused(); + }); + + test('should paste text into all input boxes when pasting 4 digits', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await simulatePaste(firstInput, '1234'); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + + // Focus should be on the 4th input box + await expect(inputBoxes.nth(3)).toBeFocused(); + }); + + test('should paste text into the first and second input box when pasting 2 digits in the 3rd box', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await thirdInput.focus(); + await simulatePaste(thirdInput, '12'); + + const inputOtp = page.locator('ion-input-otp'); + + const inputBoxes = page.locator('ion-input-otp input'); + await verifyInputValues(inputOtp, ['1', '2', '', '']); + + // Focus should be on the 3rd input box + await expect(inputBoxes.nth(2)).toBeFocused(); + }); + + test('should paste text into the first two input boxes when pasting 2 digits after typing 2 digits', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await page.keyboard.type('12'); + await simulatePaste(firstInput, '34'); + + const inputOtp = page.locator('ion-input-otp'); + + await verifyInputValues(inputOtp, ['3', '4', '', '']); + }); + + test('should paste text into all input boxes when pasting 4 digits after typing 4 digits', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await page.keyboard.type('9999'); + await simulatePaste(firstInput, '1234'); + + const inputOtp = page.locator('ion-input-otp'); + + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + }); + + test('should paste mixed language text into all input boxes', async ({ page }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await simulatePaste(firstInput, 'أبجد123'); + + const inputOtp = page.locator('ion-input-otp'); + + await verifyInputValues(inputOtp, ['أ', 'ب', 'ج', 'د', '1', '2']); + }); + }); +}); + +/** + * Events are the same across modes & directions + */ +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: events: ionInput'), () => { + test('should emit ionInput event when typing', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionInput = await page.spyOnEvent('ionInput'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('1'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '1', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(1); + + await page.keyboard.type('2'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '12', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(2); + + await page.keyboard.type('3'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '123', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(3); + + await page.keyboard.type('4'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '1234', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(4); + }); + + test('should emit ionInput event when backspacing', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionInput = await page.spyOnEvent('ionInput'); + + await page.keyboard.press('Tab'); + + await page.keyboard.press('Backspace'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '123', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(1); + + await page.keyboard.press('Backspace'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '12', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(2); + + await page.keyboard.press('Backspace'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '1', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(3); + + await page.keyboard.press('Backspace'); + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '', event: { isTrusted: true } }); + await expect(ionInput).toHaveReceivedEventTimes(4); + }); + + test('should emit ionInput event when pasting', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionInput = await page.spyOnEvent('ionInput'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await simulatePaste(firstInput, '12'); + + await ionInput.next(); + await expect(ionInput).toHaveReceivedEventDetail({ value: '12', event: { isTrusted: false } }); + await expect(ionInput).toHaveReceivedEventTimes(1); + }); + + test('should not emit ionInput event when programmatically setting the value', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionInput = await page.spyOnEvent('ionInput'); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.value = '1234'; + }); + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + + await expect(ionInput).not.toHaveReceivedEvent(); + }); + }); + + test.describe(title('input-otp: events: ionChange'), () => { + test('should not emit ionChange event when typing', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionChange = await page.spyOnEvent('ionChange'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('12'); + + await expect(ionChange).not.toHaveReceivedEvent(); + }); + + test('should emit ionChange event when pasting and then blurring', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionChange = await page.spyOnEvent('ionChange'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + await simulatePaste(firstInput, '12'); + + // Click outside the input to trigger the blur event + await page.mouse.click(0, 0); + + await ionChange.next(); + await expect(ionChange).toHaveReceivedEventDetail({ value: '12', event: { isTrusted: true } }); + await expect(ionChange).toHaveReceivedEventTimes(1); + }); + + test('should emit ionChange event when blurring with a new value', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionChange = await page.spyOnEvent('ionChange'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('12'); + + // Click outside the input to trigger the blur event + await page.mouse.click(0, 0); + + await ionChange.next(); + await expect(ionChange).toHaveReceivedEvent(); + await expect(ionChange).toHaveReceivedEventTimes(1); + }); + + test('should not emit ionChange event when blurring with the same value', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionBlur = await page.spyOnEvent('ionBlur'); + const ionChange = await page.spyOnEvent('ionChange'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + // Click outside the input to trigger the blur event + await page.mouse.click(0, 0); + + await ionBlur.next(); + await expect(ionBlur).toHaveReceivedEvent(); + await expect(ionBlur).toHaveReceivedEventTimes(1); + await expect(ionChange).not.toHaveReceivedEvent(); + }); + + test('should not emit ionChange event when programmatically setting the value', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionChange = await page.spyOnEvent('ionChange'); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.value = '1234'; + }); + await verifyInputValues(inputOtp, ['1', '2', '3', '4']); + + await expect(ionChange).not.toHaveReceivedEvent(); + }); + }); + + test.describe(title('input-otp: events: ionComplete'), () => { + test('should emit ionComplete event when all input boxes are filled', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionComplete = await page.spyOnEvent('ionComplete'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.type('1234'); + + await ionComplete.next(); + await expect(ionComplete).toHaveReceivedEventDetail({ value: '1234' }); + await expect(ionComplete).toHaveReceivedEventTimes(1); + }); + }); + + test.describe(title('input-otp: events: ionFocus'), () => { + test('should emit ionFocus event when input box is focused', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionFocus = await page.spyOnEvent('ionFocus'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await ionFocus.next(); + await expect(ionFocus).toHaveReceivedEvent(); + await expect(ionFocus).toHaveReceivedEventTimes(1); + }); + + test('should not emit ionFocus event when focus is moved to another input in the same component', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + const ionFocus = await page.spyOnEvent('ionFocus'); + + await page.keyboard.press('ArrowRight'); + + await expect(ionFocus).not.toHaveReceivedEvent(); + }); + }); + + test.describe(title('input-otp: events: ionBlur'), () => { + test('should emit ionBlur event when focus leaves the component', async ({ page }) => { + await page.setContent(`Description`, config); + + const ionBlur = await page.spyOnEvent('ionBlur'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + // Click outside the input to trigger the blur event + await page.mouse.click(0, 0); + + await ionBlur.next(); + await expect(ionBlur).toHaveReceivedEvent(); + await expect(ionBlur).toHaveReceivedEventTimes(1); + }); + + test('should not emit ionBlur event when focus is moved to another input in the same component', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const ionBlur = await page.spyOnEvent('ionBlur'); + + const firstInput = page.locator('ion-input-otp input').first(); + await firstInput.focus(); + + await page.keyboard.press('ArrowRight'); + + await expect(ionBlur).not.toHaveReceivedEvent(); + }); + }); +}); + +/** + * Methods are the same across modes & directions + */ +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: setFocus method'), () => { + test('should not focus the specified input box when index is provided and value is not set', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.setFocus(2); + }); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await expect(thirdInput).not.toBeFocused(); + }); + + test('should focus the specified input box when index is provided and value is set', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.setFocus(2); + }); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await expect(thirdInput).toBeFocused(); + }); + + test('should focus first empty input when no index is provided and not all inputs are filled', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.setFocus(); + }); + + const thirdInput = page.locator('ion-input-otp input').nth(2); + await expect(thirdInput).toBeFocused(); + }); + + test('should focus last input when no index is provided and all inputs are filled', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.setFocus(); + }); + + const lastInput = page.locator('ion-input-otp input').last(); + await expect(lastInput).toBeFocused(); + }); + + test('should clamp invalid indices to valid range', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + + // Test negative index + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.setFocus(-1); + }); + const firstInput = page.locator('ion-input-otp input').first(); + await expect(firstInput).toBeFocused(); + + // Test index beyond length + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.setFocus(10); + }); + const lastInput = page.locator('ion-input-otp input').last(); + await expect(lastInput).toBeFocused(); + }); + }); +}); diff --git a/core/src/components/input-otp/test/color/index.html b/core/src/components/input-otp/test/color/index.html new file mode 100644 index 00000000000..68e864270b1 --- /dev/null +++ b/core/src/components/input-otp/test/color/index.html @@ -0,0 +1,91 @@ + + + + + Input OTP - Color + + + + + + + + + + + + + + + Input OTP - Color + + + + +
+
+

Outline Colors

+ + + + + + + + + +
+ +
+

Solid Colors

+ + + + + + + + + +
+ +
+

Disabled

+ Outline + Solid + +

Readonly

+ Outline + Solid +
+ +
+

Invalid / Touched

+ Outline + Solid + +

Valid / Focused

+ Outline + Solid +
+
+
+
+ + diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts b/core/src/components/input-otp/test/color/input-otp.e2e.ts new file mode 100644 index 00000000000..6fb970e838a --- /dev/null +++ b/core/src/components/input-otp/test/color/input-otp.e2e.ts @@ -0,0 +1,81 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +const VALID_FILLS = ['outline', 'solid']; + +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: color'), () => { + // Test all colors with all fills + VALID_FILLS.forEach((fill) => { + test(`color with ${fill} fill should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` +
+ + + + + + + + + +
+ `, + config + ); + + const container = page.locator('#container'); + // Set viewport size to ensure the entire height is visible + await page.setViewportSize({ width: 393, height: 900 }); + await expect(container).toHaveScreenshot(screenshot(`input-otp-color-${fill}`)); + }); + test(`disabled color with ${fill} fill should not have visual regressions`, async ({ page }) => { + await page.setContent( + `npx +
+ + + + + + + + + +
+ `, + config + ); + + const container = page.locator('#container'); + // Set viewport size to ensure the entire height is visible + await page.setViewportSize({ width: 393, height: 900 }); + await expect(container).toHaveScreenshot(screenshot(`input-otp-color-${fill}-disabled`)); + }); + test(`readonly color with ${fill} fill should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` +
+ + + + + + + + + +
+ `, + config + ); + + const container = page.locator('#container'); + // Set viewport size to ensure the entire height is visible + await page.setViewportSize({ width: 393, height: 900 }); + await expect(container).toHaveScreenshot(screenshot(`input-otp-color-${fill}-readonly`)); + }); + }); + }); +}); diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a64c9d0118e Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..a3c63b3185f Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..8186c1222db Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..21a16d72713 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..b06f3b7f357 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..a79e614e733 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..b1fd6e431ec Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..bf3328b3759 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..96b2b4f77be Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..2d084da822d Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..773e0272fe7 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..0a52d6bf6bb Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..b8500c9e870 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..10d4ee2ee85 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..2dd1f5191ff Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..42b2eebae0a Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5222149c647 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..79e57fba2bf Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-outline-readonly-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..1a893d7d583 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..0424a0f43d3 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..9be07ade9a7 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..b6cac792a64 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..0ce562c8ad2 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..bffefc31992 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..698f2d13b57 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..97b9a451b0d Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..6bf737d31cf Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..7ee39197e12 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..11ac4972e64 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..4bd799270e7 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..67283e5e2cc Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..8924670f670 Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..22ff8a2cdab Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..bc29549d83d Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..bd7435bd5bf Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b7b36dc397d Binary files /dev/null and b/core/src/components/input-otp/test/color/input-otp.e2e.ts-snapshots/input-otp-color-solid-readonly-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts b/core/src/components/input-otp/test/fill/input-otp.e2e.ts new file mode 100644 index 00000000000..b20e93a4591 --- /dev/null +++ b/core/src/components/input-otp/test/fill/input-otp.e2e.ts @@ -0,0 +1,44 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +const VALID_FILLS = ['outline', 'solid']; + +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: fill'), () => { + VALID_FILLS.forEach((fill) => { + test(`${fill} fill should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-${fill}`)); + }); + test(`disabled ${fill} fill should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-${fill}-disabled`)); + }); + test(`readonly ${fill} fill should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-${fill}-readonly`)); + }); + }); + }); +}); diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..e01c8fe9f64 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9ac3a1c4023 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..27f5772abf3 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..55871f013c3 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..01d70bd9dd2 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..cbf23ee622a Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4184768d040 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5a8af327847 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..7a53523ec96 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..edc29022ba2 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9d85b961995 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..35ddacfc0ac Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..83f53b9da0f Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..951a40825bf Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..0d23ddd6153 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a01f22bacb2 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..ca98a0c2b09 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..bce64d8f746 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-outline-readonly-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..7f731c481d7 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..a119f1fc3fe Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..df9a223dfbe Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..6c7fd49e9e2 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..068d8eb6fba Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..d99a39479a8 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-disabled-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..8be3fc556be Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9a0a437b0b9 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..76fc6068db2 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..7768dfb1d9e Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..1d73fdc57ab Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..8175f86e5cc Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..0c1edcc392d Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..7e5af9de51e Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..487e5af97bd Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..26cbcb3d3c4 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..a9d9a063bc8 Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..d72ed0b48cb Binary files /dev/null and b/core/src/components/input-otp/test/fill/input-otp.e2e.ts-snapshots/input-otp-solid-readonly-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/index.html b/core/src/components/input-otp/test/separators/index.html new file mode 100644 index 00000000000..8dfb7f9ffa2 --- /dev/null +++ b/core/src/components/input-otp/test/separators/index.html @@ -0,0 +1,106 @@ + + + + + Input OTP - Separators + + + + + + + + + + + + + + + Input OTP - Separators + + + + +
+
+

Separators (small)

+ Enter your one-time password (numbers only) + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + +
+ +
+

Separators (medium)

+ Enter your one-time password (numbers only) + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + +
+ +
+

Separators (large)

+ Enter your one-time password (numbers only) + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + + Enter your one-time password (numbers only) + + +
+
+
+
+ + diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts b/core/src/components/input-otp/test/separators/input-otp.e2e.ts new file mode 100644 index 00000000000..37c9bb8bb4a --- /dev/null +++ b/core/src/components/input-otp/test/separators/input-otp.e2e.ts @@ -0,0 +1,180 @@ +import type { ConsoleMessage } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { E2EPage } from '@utils/test/playwright'; +import { configs, test } from '@utils/test/playwright'; + +const DEFAULT_INPUT_LENGTH = 4; +const VALID_SIZES = ['small', 'medium', 'large']; + +/** + * Helper function to check if the next sibling after + * the input box is a separator + */ +const hasSeparatorAfter = async (page: E2EPage, index: number): Promise => { + const wrappers = page.locator('.input-otp-group > .native-wrapper'); + return await wrappers + .nth(index) + .evaluate((el: Element) => el.nextElementSibling?.classList.contains('input-otp-separator') ?? false); +}; + +/** + * Helper function to collect console warnings + */ +const collectWarnings = async (page: E2EPage): Promise => { + const warnings: string[] = []; + page.on('console', (ev: ConsoleMessage) => { + if (ev.type() === 'warning') { + warnings.push(ev.text()); + } + }); + return warnings; +}; + +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: separators'), () => { + // Test separators with all sizes + VALID_SIZES.forEach((size) => { + test(`one separator with ${size} size should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-separators-one-${size}`)); + }); + + test(`two separators with ${size} size should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-separators-two-${size}`)); + }); + + test(`all separators with ${size} size should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-separators-all-${size}`)); + }); + }); + }); +}); + +/** + * Functionality is the same across modes and directions + */ +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: separators functionality'), () => { + test('should render separators after the first and third input box', async ({ page }) => { + await page.setContent(`Description`, config); + + await expect(await hasSeparatorAfter(page, 0)).toBe(true); + await expect(await hasSeparatorAfter(page, 1)).toBe(false); + await expect(await hasSeparatorAfter(page, 2)).toBe(true); + await expect(await hasSeparatorAfter(page, 3)).toBe(false); + }); + + test('should render separators after the second and third input box', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el: HTMLIonInputOtpElement) => { + el.separators = [2, 3]; + }); + + await expect(await hasSeparatorAfter(page, 0)).toBe(false); + await expect(await hasSeparatorAfter(page, 1)).toBe(true); + await expect(await hasSeparatorAfter(page, 2)).toBe(true); + await expect(await hasSeparatorAfter(page, 3)).toBe(false); + }); + + test('should render all separators', async ({ page }) => { + await page.setContent(`Description`, config); + + await expect(await hasSeparatorAfter(page, 0)).toBe(true); + await expect(await hasSeparatorAfter(page, 1)).toBe(true); + await expect(await hasSeparatorAfter(page, 2)).toBe(true); + await expect(await hasSeparatorAfter(page, 3)).toBe(false); + }); + + test('should handle empty separators string', async ({ page }) => { + await page.setContent(`Description`, config); + + await expect(await hasSeparatorAfter(page, 0)).toBe(false); + await expect(await hasSeparatorAfter(page, 1)).toBe(false); + await expect(await hasSeparatorAfter(page, 2)).toBe(false); + await expect(await hasSeparatorAfter(page, 3)).toBe(false); + }); + + test('should warn when setting separators to a position greater than the input length', async ({ page }) => { + const warnings = await collectWarnings(page); + await page.setContent(`Description`, config); + + expect(warnings.length).toBe(1); + expect(warnings[0]).toContain( + `[Ionic Warning]: [ion-input-otp] - The following separator positions are greater than the input length (${DEFAULT_INPUT_LENGTH}): 5, 6, 7. These separators will be ignored.` + ); + }); + + test('should warn when setting separators to an invalid space-separated string', async ({ page }) => { + const warnings = await collectWarnings(page); + const invalidSeparators = '1 2 3'; + + await page.setContent(`Description`, config); + + expect(warnings.length).toBe(1); + expect(warnings[0]).toContain( + `[Ionic Warning]: [ion-input-otp] - Invalid separators format. Expected a comma-separated list of numbers, an array of numbers, or "all". Received: ${invalidSeparators}` + ); + }); + + test('should warn when setting separators to an invalid comma-separated string', async ({ page }) => { + const warnings = await collectWarnings(page); + const invalidSeparators = '1,d,3'; + + await page.setContent(`Description`, config); + + expect(warnings.length).toBe(1); + expect(warnings[0]).toContain( + `[Ionic Warning]: [ion-input-otp] - Invalid separators format. Expected a comma-separated list of numbers, an array of numbers, or "all". Received: ${invalidSeparators}` + ); + }); + + test('should warn when setting separators to negative numbers', async ({ page }) => { + const warnings = await collectWarnings(page); + const invalidSeparators = '-1,2,3'; + + await page.setContent(`Description`, config); + + expect(warnings.length).toBe(1); + expect(warnings[0]).toContain( + `[Ionic Warning]: [ion-input-otp] - Invalid separators format. Expected a comma-separated list of numbers, an array of numbers, or "all". Received: ${invalidSeparators}` + ); + }); + + test('should warn when setting separators to duplicate positions', async ({ page }) => { + const warnings = await collectWarnings(page); + const invalidSeparators = '1,1,2'; + + await page.setContent(`Description`, config); + + expect(warnings.length).toBe(1); + expect(warnings[0]).toContain( + `[Ionic Warning]: [ion-input-otp] - Duplicate separator positions are not allowed. Received: ${invalidSeparators}` + ); + }); + }); +}); diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..1ca894d0fd1 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..8badc4abec6 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..4e54b58b03f Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..57fba0451d3 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..c5b2cf71587 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..439bbf8193f Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..49d2263e120 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..4c356225aec Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..a4cd2f9cf8a Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f5c29c953e2 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..e7c6eb1c467 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..59028c2b69e Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f2da40c69e2 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..082f915e6bc Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..bb683cf5733 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..720ed3a5971 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..465f49f131a Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..779d05f8995 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-all-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..6964d763a3e Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6ab84b5db4c Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..2e4be5a7ed1 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..14b217fd342 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..525c44305ba Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..edd4f7d6c5e Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..adefb8abdbe Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..4286fac7fa8 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..ff45c57e56c Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f294676a43e Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..68e27384262 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..4cc2c3c9e99 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..c43556c3ba5 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..f964fe53f46 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e4f5b3ba2c9 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..b706a68dd7b Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..b18c3243c35 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..5ad4c7e9740 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-one-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d8cdd0a5645 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..aabd9e3fe1c Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..1b0a79886b1 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..73aecd67ce3 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..1fa560af187 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..1d03b4f1db4 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..42b8bf1bcc2 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..77c628890b7 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..a0bc78458a7 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..fb3673dfe34 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..2268dabf456 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b52eda5ecf6 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4aff0d82b39 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5f92e5bcb30 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..af34f1080e6 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..448ed99bb48 Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6b03474060b Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b148bf911aa Binary files /dev/null and b/core/src/components/input-otp/test/separators/input-otp.e2e.ts-snapshots/input-otp-separators-two-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/index.html b/core/src/components/input-otp/test/shape/index.html new file mode 100644 index 00000000000..ee6fbd45655 --- /dev/null +++ b/core/src/components/input-otp/test/shape/index.html @@ -0,0 +1,76 @@ + + + + + Input OTP - Shape + + + + + + + + + + + + + + + Input OTP - Shape + + + + +
+
+

Default

+ + + + + + + +
+ +
+

Soft

+ + + + + + + +
+ +
+

Rectangular

+ + + + + + + +
+
+
+
+ + diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts b/core/src/components/input-otp/test/shape/input-otp.e2e.ts new file mode 100644 index 00000000000..766f641a6b1 --- /dev/null +++ b/core/src/components/input-otp/test/shape/input-otp.e2e.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +const VALID_SHAPES = ['rectangular', 'round', 'soft']; +const VALID_SIZES = ['small', 'medium', 'large']; + +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: shape'), () => { + // Test all shapes with all sizes + VALID_SHAPES.forEach((shape) => { + VALID_SIZES.forEach((size) => { + test(`${shape} shape with ${size} size should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-${shape}-${size}`)); + }); + }); + }); + }); +}); diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..2e52effd4cc Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9d8f0ddc194 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..1a3dad5e35a Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..da5b3ae65a8 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..1ea53954f8e Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..5c6b0ca649f Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..32e048e493b Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..3a117180687 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..9aa82cded81 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..8977cf362d9 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..80c31a4bbdf Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..4b767841ab4 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..3fdfc670e91 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..747a155fa1d Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..34925656a98 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..12ca2ce5be5 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..168694cc5e1 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..1729fd08a97 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-rectangular-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a2b87099f00 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6968b0ef9d7 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..11c60bfd53a Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f33825b96e5 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..0c90c3b3a04 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..3844be29387 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4184768d040 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5a8af327847 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..7a53523ec96 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..edc29022ba2 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9d85b961995 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..35ddacfc0ac Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..83834028d53 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..da0c78f86c0 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..c991f0d275c Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..8c88ee50678 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..cb2a6a8394e Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..f5d95c22fc0 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-round-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..73866f8fbc4 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6fddfccc773 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..cd263c83544 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..9a3d1d9e68d Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..a6f8944c565 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..3b33de17bd0 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d922eb8997a Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..f4764017e69 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e851d324d2f Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f60a53b2c53 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5332977512a Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..c713bb77c94 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..b6e1a6bb548 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..c9d3bbf8bfe Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..5e10e6c5612 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..3cb0aa32222 Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..af2c6f857ce Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..8fe5638711c Binary files /dev/null and b/core/src/components/input-otp/test/shape/input-otp.e2e.ts-snapshots/input-otp-soft-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/index.html b/core/src/components/input-otp/test/size/index.html new file mode 100644 index 00000000000..8b11e5589e4 --- /dev/null +++ b/core/src/components/input-otp/test/size/index.html @@ -0,0 +1,67 @@ + + + + + Input OTP - Size + + + + + + + + + + + + + + + Input OTP - Size + + + + +
+
+

Small

+ Description + Description + Description + Description +
+ +
+

Medium

+ Description + Description + Description + Description +
+ +
+

Large

+ Description + Description + Description + Description +
+
+
+
+ + diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts b/core/src/components/input-otp/test/size/input-otp.e2e.ts new file mode 100644 index 00000000000..989fc30fb93 --- /dev/null +++ b/core/src/components/input-otp/test/size/input-otp.e2e.ts @@ -0,0 +1,33 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +const VALID_SIZES = ['small', 'medium', 'large']; + +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: size'), () => { + VALID_SIZES.forEach((size) => { + test(`${size} size should not have visual regressions`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-${size}`)); + }); + test(`${size} size should collapse width when viewport is too narrow`, async ({ page }) => { + await page.setContent( + ` + Description + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-${size}-collapsed`)); + }); + }); + }); +}); diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..0069b8cc08b Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..f9796a0ad92 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..ae923999f83 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..3f4c6547c9e Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..e677d327f97 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b4ac26ac4a0 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-collapsed-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a2b87099f00 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6968b0ef9d7 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..11c60bfd53a Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f33825b96e5 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..0c90c3b3a04 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..3844be29387 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-large-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4231388dd0c Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..e840dcd71fa Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..5a4085d5cdf Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..45529cf976e Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..e692c0a9a41 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b1482cf1e91 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-collapsed-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4184768d040 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5a8af327847 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..7a53523ec96 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..edc29022ba2 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..9d85b961995 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..35ddacfc0ac Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-medium-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..57a4aa34c92 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..34ee0ff34ac Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..9c42fa908a3 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4aeeea91167 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..23c41f4d752 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..051fe890e56 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-collapsed-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..83834028d53 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..da0c78f86c0 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..c991f0d275c Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..8c88ee50678 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..cb2a6a8394e Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..f5d95c22fc0 Binary files /dev/null and b/core/src/components/input-otp/test/size/input-otp.e2e.ts-snapshots/input-otp-small-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index c2834138aa8..69281c5e899 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -880,15 +880,6 @@ export class Input implements ComponentInterface { */ ev.preventDefault(); }} - onFocusin={(ev) => { - /** - * Prevent the focusin event from bubbling otherwise it will cause the focusin - * event listener in scroll assist to fire. When this fires, focus will be moved - * back to the input even if the clear button was never tapped. This poses issues - * for screen readers as it means users would be unable to swipe past the clear button. - */ - ev.stopPropagation(); - }} onClick={this.clearTextInput} > diff --git a/core/src/components/popover/test/basic/popover.e2e.ts-snapshots/popover-basic-no-event-popover-ios-ltr-Mobile-Safari-linux.png b/core/src/components/popover/test/basic/popover.e2e.ts-snapshots/popover-basic-no-event-popover-ios-ltr-Mobile-Safari-linux.png index 1ad38380d97..e07a907daf7 100644 Binary files a/core/src/components/popover/test/basic/popover.e2e.ts-snapshots/popover-basic-no-event-popover-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/popover/test/basic/popover.e2e.ts-snapshots/popover-basic-no-event-popover-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/split-pane/test/basic/split-pane.e2e.ts-snapshots/split-pane-ios-rtl-Mobile-Safari-linux.png b/core/src/components/split-pane/test/basic/split-pane.e2e.ts-snapshots/split-pane-ios-rtl-Mobile-Safari-linux.png index e2839798f6d..30fb5ea2535 100644 Binary files a/core/src/components/split-pane/test/basic/split-pane.e2e.ts-snapshots/split-pane-ios-rtl-Mobile-Safari-linux.png and b/core/src/components/split-pane/test/basic/split-pane.e2e.ts-snapshots/split-pane-ios-rtl-Mobile-Safari-linux.png differ diff --git a/core/src/components/toggle/toggle.tsx b/core/src/components/toggle/toggle.tsx index 7760e796b6f..ea63656a2f0 100644 --- a/core/src/components/toggle/toggle.tsx +++ b/core/src/components/toggle/toggle.tsx @@ -3,6 +3,7 @@ import { Component, Element, Event, Host, Prop, State, Watch, h } from '@stencil import { renderHiddenInput, inheritAriaAttributes } from '@utils/helpers'; import type { Attributes } from '@utils/helpers'; import { hapticSelection } from '@utils/native/haptic'; +import { isPlatform } from '@utils/platform'; import { isRTL } from '@utils/rtl'; import { createColorClasses, hostContext } from '@utils/theme'; import { checkmarkOutline, removeOutline, ellipseOutline } from 'ionicons/icons'; @@ -257,6 +258,13 @@ export class Toggle implements ComponentInterface { }; private onClick = (ev: MouseEvent) => { + /** + * The haptics for the toggle on tap is + * an iOS-only feature. As such, it should + * only trigger on iOS. + */ + const enableHaptics = isPlatform('ios'); + if (this.disabled) { return; } @@ -265,6 +273,7 @@ export class Toggle implements ComponentInterface { if (this.lastDrag + 300 < Date.now()) { this.toggleChecked(); + enableHaptics && hapticSelection(); } }; diff --git a/core/src/css/structure.scss b/core/src/css/structure.scss index 1e39d0296ea..d15940c01c8 100644 --- a/core/src/css/structure.scss +++ b/core/src/css/structure.scss @@ -22,10 +22,6 @@ html { text-size-adjust: 100%; } -html:not(.hydrated) body { - display: none; -} - html.ion-ce body { display: block; } diff --git a/core/src/interface.d.ts b/core/src/interface.d.ts index 6432a506a72..3dcfb6aec2f 100644 --- a/core/src/interface.d.ts +++ b/core/src/interface.d.ts @@ -12,6 +12,7 @@ export { CheckboxCustomEvent } from './components/checkbox/checkbox-interface'; export { DatetimeCustomEvent, DatetimeHighlightStyle } from './components/datetime/datetime-interface'; export { InfiniteScrollCustomEvent } from './components/infinite-scroll/infinite-scroll-interface'; export { InputCustomEvent } from './components/input/input-interface'; +export { InputOtpCustomEvent } from './components/input-otp/input-otp-interface'; export { CounterFormatter } from './components/item/item-interface'; export { ItemSlidingCustomEvent } from './components/item-sliding/item-sliding-interface'; export { LoadingOptions } from './components/loading/loading-interface'; diff --git a/core/src/utils/input-shims/hacks/scroll-assist.ts b/core/src/utils/input-shims/hacks/scroll-assist.ts index 293f1d1d26d..fb2b1900202 100644 --- a/core/src/utils/input-shims/hacks/scroll-assist.ts +++ b/core/src/utils/input-shims/hacks/scroll-assist.ts @@ -181,6 +181,30 @@ const setManualFocus = (el: HTMLElement) => { return; } + /** + * Optimization for scenarios where the currently focused element is a sibling + * of the target element. In such cases, we avoid setting `SKIP_SCROLL_ASSIST`. + * + * This is crucial for accessibility: input elements can now contain focusable + * siblings (e.g., clear buttons, slotted elements). If we didn't skip setting + * the attribute here, screen readers would be unable to navigate to and interact + * with these sibling elements. + * + * Without this check, we would need to call `ev.stopPropagation()` on the + * 'focusin' event of each focusable sibling to prevent the scroll assist + * listener from incorrectly moving focus back to the input. That approach + * would be less maintainable and more error-prone. + */ + const inputId = el.getAttribute('id'); + const label = el.closest(`label[for="${inputId}"]`); + const activeElLabel = document.activeElement?.closest(`label[for="${inputId}"]`); + + if (label !== null && label === activeElLabel) { + // If the label is the same as the active element label, then + // we don't need to set the `SKIP_SCROLL_ASSIST` and reset focus. + return; + } + el.setAttribute(SKIP_SCROLL_ASSIST, 'true'); el.focus(); }; diff --git a/core/stencil.config.ts b/core/stencil.config.ts index f2e10d10119..edb5de73469 100644 --- a/core/stencil.config.ts +++ b/core/stencil.config.ts @@ -54,6 +54,7 @@ const getAngularOutputTargets = () => { * Value Accessors are manually implemented in the `@ionic/angular/standalone` package. */ 'ion-input', + 'ion-input-otp', 'ion-textarea', 'ion-searchbar', 'ion-datetime', @@ -202,7 +203,7 @@ export const config: Config = { event: 'ion-change', }, { - elements: ['ion-input', 'ion-searchbar', 'ion-textarea', 'ion-range'], + elements: ['ion-input', 'ion-input-otp', 'ion-searchbar', 'ion-textarea', 'ion-range'], targetAttr: 'value', event: 'ion-input', } @@ -243,7 +244,6 @@ export const config: Config = { // }, ...getAngularOutputTargets(), ], - buildEs5: 'prod', testing: { moduleNameMapper: { "@utils/test": ["/src/utils/test/utils"], diff --git a/core/tsconfig.json b/core/tsconfig.json index acdd4094006..6e2271dfde6 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -10,6 +10,7 @@ "assumeChangesOnlyAffectDirectDependencies": true, "jsx": "react", "jsxFactory": "h", + "jsxFragmentFactory": "Fragment", "lib": [ "dom", "dom.iterable", diff --git a/docs/component-guide.md b/docs/component-guide.md index 25b17ec003e..03b3ab9f761 100644 --- a/docs/component-guide.md +++ b/docs/component-guide.md @@ -14,10 +14,18 @@ * [Switch](#switch) * [Accordion](#accordion) - [Rendering Anchor or Button](#rendering-anchor-or-button) - * [Example Components](#example-components-1) + * [Example Components](#example-components-4) * [Component Structure](#component-structure-1) - [Converting Scoped to Shadow](#converting-scoped-to-shadow) - [RTL](#rtl) +- [Adding New Components with Native Input Support](#adding-new-components-with-native-input-support) + * [Angular Integration](#angular-integration) + * [Angular Tests](#angular-tests) + * [Vue Integration](#vue-integration) + * [Vue Tests](#vue-tests) + * [React Integration](#react-integration) + * [React Tests](#react-tests) + * [Interface Exports](#interface-exports) ## Button States @@ -749,3 +757,212 @@ class={{ transform-origin: right center; } ``` + +## Adding New Components with Native Input Support + +When creating a new component that renders native input elements (such as `` or `