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/api.txt b/core/api.txt index 428926db860..c792eb050a1 100644 --- a/core/api.txt +++ b/core/api.txt @@ -780,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/src/components.d.ts b/core/src/components.d.ts index 72733ac654b..c58e8dc4234 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"; @@ -1319,6 +1321,65 @@ export namespace Components { */ "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"`. + */ + "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. + */ + "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. + */ + "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. + */ + "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. + */ + "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. + */ + "shape": 'round' | 'rectangular' | 'soft'; + /** + * The size of the input boxes. + */ + "size": 'small' | 'medium' | 'large'; + /** + * The type of input allowed in the input boxes. + */ + "type": 'text' | 'number'; + /** + * The value of the input group. + */ + "value"?: string | number | null; + } interface IonInputPasswordToggle { /** * 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). @@ -3404,6 +3465,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; @@ -3933,6 +3998,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: { @@ -4792,6 +4878,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; @@ -6178,6 +6265,80 @@ declare namespace LocalJSX { */ "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"`. + */ + "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. + */ + "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. + */ + "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. + */ + "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. + */ + "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. + */ + "shape"?: 'round' | 'rectangular' | 'soft'; + /** + * The size of the input boxes. + */ + "size"?: 'small' | 'medium' | 'large'; + /** + * The type of input allowed in the input boxes. + */ + "type"?: 'text' | 'number'; + /** + * The value of the input group. + */ + "value"?: string | number | null; + } interface IonInputPasswordToggle { /** * 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). @@ -8309,6 +8470,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; @@ -8411,6 +8573,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/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..55c52d6ee91 --- /dev/null +++ b/core/src/components/input-otp/input-otp.tsx @@ -0,0 +1,789 @@ +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; + + const isPasteShortcut = (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'v'; + const isTextSelection = input.selectionStart !== input.selectionEnd; + + // Return if the key is the paste shortcut or the input value + // text is selected and let the onPaste / onInput handler manage it + if (isPasteShortcut || isTextSelection) { + 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 { validKeyPattern } = this; + + const value = (event.target as HTMLInputElement).value; + + // Only allow input if it's a single character and matches the pattern + if (value.length > 1 || (value.length > 0 && !validKeyPattern.test(value))) { + // Reset the input value if not valid + this.inputRefs[index].value = ''; + this.inputValues[index] = ''; + return; + } + + // Find the first empty box before or at the current index + let targetIndex = index; + for (let i = 0; i < index; i++) { + if (!this.inputValues[i] || this.inputValues[i] === '') { + targetIndex = i; + break; + } + } + + // Set the value at the target index + this.inputValues[targetIndex] = value; + + // If the value was entered in a later box, clear the current box + if (targetIndex !== index) { + this.inputRefs[index].value = ''; + } + + if (value.length > 0) { + this.focusNext(targetIndex); + } + this.updateValue(event); + }; + + /** + * 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..3dfb627589a --- /dev/null +++ b/core/src/components/input-otp/test/basic/input-otp.e2e.ts @@ -0,0 +1,947 @@ +import { expect } from '@playwright/test'; +import type { Locator } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * 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(); + + await page.keyboard.type('أبجد123'); + + // 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: 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']); + }); + }); +}); + +/** + * 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/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/stencil.config.ts b/core/stencil.config.ts index 2bdcb4efa29..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', } 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 `