From 1c6621e7d2f01777d8d136c15d4ef3e2ecfc0dfd Mon Sep 17 00:00:00 2001 From: ShaneK Date: Tue, 27 Oct 2026 13:12:25 -0700 Subject: [PATCH 1/4] feat(input-otp): add helper and error text and input label formatter --- core/api.txt | 3 + core/src/components.d.ts | 30 ++- .../input-otp/input-otp-interface.ts | 2 + .../components/input-otp/input-otp.ios.scss | 2 +- .../components/input-otp/input-otp.md.scss | 2 +- core/src/components/input-otp/input-otp.scss | 44 +++- core/src/components/input-otp/input-otp.tsx | 219 +++++++++++++++++- .../components/input-otp/test/a11y/index.html | 64 +++++ .../input-otp/test/a11y/input-otp.e2e.ts | 113 +++++++++ .../input-otp/test/basic/index.html | 20 +- .../test/bottom-content/input-otp.e2e.ts | 104 +++++++++ .../input-otp/test/fill/input-otp.e2e.ts | 55 +++++ .../input-otp/test/input-otp.spec.ts | 215 +++++++++++++++++ .../angular/src/lazy/directives/proxies.ts | 4 +- .../src/standalone/directives/input-otp.ts | 3 + packages/vue/src/proxies.ts | 3 + 16 files changed, 860 insertions(+), 23 deletions(-) create mode 100644 core/src/components/input-otp/test/a11y/index.html create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts create mode 100644 core/src/components/input-otp/test/input-otp.spec.ts diff --git a/core/api.txt b/core/api.txt index 5a79c3903aa..074a30b2035 100644 --- a/core/api.txt +++ b/core/api.txt @@ -794,7 +794,10 @@ 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,errorText,string | undefined,undefined,false,false ion-input-otp,prop,fill,"outline" | "solid" | undefined,'outline',false,false +ion-input-otp,prop,helperText,string | undefined,undefined,false,false +ion-input-otp,prop,inputLabelFormatter,((index: number, length: number) => string) | undefined,undefined,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 diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 22e751460f0..2816528dc72 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -18,7 +18,7 @@ import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-int import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimeParts, 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 { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, InputOtpLabelFormatter } from "./components/input-otp/input-otp-interface"; import { MenuChangeEventDetail, MenuCloseEventDetail, MenuType, Side } from "./components/menu/menu-interface"; import { ModalBreakpointChangeEventDetail, ModalDragEventDetail, ModalHandleBehavior } from "./components/modal/modal-interface"; import { NavComponent, NavComponentWithProps, NavOptions, RouterOutletOptions, SwipeGestureHandler, TransitionDoneFn, TransitionInstruction } from "./components/nav/nav-interface"; @@ -55,7 +55,7 @@ export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-int export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimeParts, 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 { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, InputOtpLabelFormatter } from "./components/input-otp/input-otp-interface"; export { MenuChangeEventDetail, MenuCloseEventDetail, MenuType, Side } from "./components/menu/menu-interface"; export { ModalBreakpointChangeEventDetail, ModalDragEventDetail, ModalHandleBehavior } from "./components/modal/modal-interface"; export { NavComponent, NavComponentWithProps, NavOptions, RouterOutletOptions, SwipeGestureHandler, TransitionDoneFn, TransitionInstruction } from "./components/nav/nav-interface"; @@ -1468,11 +1468,23 @@ export namespace Components { * @default false */ "disabled": boolean; + /** + * Text that is placed under the input boxes and displayed when an error is detected. + */ + "errorText"?: string; /** * The fill for the input boxes. If `"solid"` the input boxes will have a background. If `"outline"` the input boxes will be transparent with a border. * @default 'outline' */ "fill"?: 'outline' | 'solid'; + /** + * Text that is placed under the input boxes and displayed when no error is detected. + */ + "helperText"?: string; + /** + * A callback used to format the accessible name of each input box. It receives the 0-based `index` of the box and the total `length`. By default the name is "Input 1 of 4", or "PIN, 1 of 4" with an `aria-label` of "PIN". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + */ + "inputLabelFormatter"?: InputOtpLabelFormatter; /** * 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" */ @@ -6628,11 +6640,23 @@ declare namespace LocalJSX { * @default false */ "disabled"?: boolean; + /** + * Text that is placed under the input boxes and displayed when an error is detected. + */ + "errorText"?: string; /** * The fill for the input boxes. If `"solid"` the input boxes will have a background. If `"outline"` the input boxes will be transparent with a border. * @default 'outline' */ "fill"?: 'outline' | 'solid'; + /** + * Text that is placed under the input boxes and displayed when no error is detected. + */ + "helperText"?: string; + /** + * A callback used to format the accessible name of each input box. It receives the 0-based `index` of the box and the total `length`. By default the name is "Input 1 of 4", or "PIN, 1 of 4" with an `aria-label` of "PIN". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + */ + "inputLabelFormatter"?: InputOtpLabelFormatter; /** * 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" */ @@ -9236,7 +9260,9 @@ declare namespace LocalJSX { "autocapitalize": string; "color": Color; "disabled": boolean; + "errorText": string; "fill": 'outline' | 'solid'; + "helperText": string; "inputmode": 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'; "length": number; "pattern": string; diff --git a/core/src/components/input-otp/input-otp-interface.ts b/core/src/components/input-otp/input-otp-interface.ts index 8fb289cfec4..8d7daf48011 100644 --- a/core/src/components/input-otp/input-otp-interface.ts +++ b/core/src/components/input-otp/input-otp-interface.ts @@ -17,6 +17,8 @@ export interface InputOtpCompleteEventDetail { event?: Event; } +export type InputOtpLabelFormatter = (index: number, length: number) => string; + 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 index 8f0e8176784..fd4eaa6b96c 100644 --- a/core/src/components/input-otp/input-otp.ios.scss +++ b/core/src/components/input-otp/input-otp.ios.scss @@ -16,5 +16,5 @@ // -------------------------------------------------- :host(.input-otp-fill-outline) { - --border-color: #{$item-ios-border-color}; + --internal-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 index f4e2b16e97e..60563673fa8 100644 --- a/core/src/components/input-otp/input-otp.md.scss +++ b/core/src/components/input-otp/input-otp.md.scss @@ -16,5 +16,5 @@ // -------------------------------------------------- :host(.input-otp-fill-outline) { - --border-color: #{$background-color-step-300}; + --internal-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 index 2b8b12ee585..a47bd460652 100644 --- a/core/src/components/input-otp/input-otp.scss +++ b/core/src/components/input-otp/input-otp.scss @@ -63,6 +63,9 @@ */ --highlight-color: var(--highlight-color-focused); + // Don't inherit `--border-color` from ancestors like `ion-modal` + --border-color: initial; + display: block; position: relative; @@ -102,7 +105,9 @@ border-width: var(--border-width); border-style: solid; - border-color: var(--border-color); + // The defaults use `--internal-border-color` since a host + // selector can't beat `:host(.input-otp-fill-*)` + border-color: var(--border-color, var(--internal-border-color)); background: var(--background); color: var(--color); @@ -134,6 +139,37 @@ display: none; } +// Input Hint Text +// ---------------------------------------------------------------- + +.input-otp-bottom { + font-size: dynamic-font(12px); + + line-height: dynamic-font(20px); + + text-align: center; +} + +.input-otp-bottom .error-text { + display: none; + + color: var(--highlight-color-invalid); +} + +.input-otp-bottom .helper-text { + display: block; + + color: $text-color-step-300; +} + +:host(.ion-touched.ion-invalid) .input-otp-bottom .error-text { + display: block; +} + +:host(.ion-touched.ion-invalid) .input-otp-bottom .helper-text { + display: none; +} + // Input Separator // ---------------------------------------------------------------- @@ -198,7 +234,7 @@ } :host(.input-otp-fill-solid) { - --border-color: #{$background-color-step-50}; + --internal-border-color: #{$background-color-step-50}; --background: #{$background-color-step-50}; } @@ -211,7 +247,7 @@ :host(.input-otp-fill-outline.input-otp-disabled) { --background: #{$background-color-step-50}; - --border-color: #{$background-color-step-100}; + --internal-border-color: #{$background-color-step-100}; } :host(.input-otp-disabled), @@ -231,7 +267,7 @@ :host(.input-otp-fill-solid.input-otp-disabled), :host(.input-otp-fill-solid.input-otp-readonly) { - --border-color: #{$background-color-step-100}; + --internal-border-color: #{$background-color-step-100}; --background: #{$background-color-step-100}; } diff --git a/core/src/components/input-otp/input-otp.tsx b/core/src/components/input-otp/input-otp.tsx index 1c84b3c9a42..59955488d45 100644 --- a/core/src/components/input-otp/input-otp.tsx +++ b/core/src/components/input-otp/input-otp.tsx @@ -1,8 +1,24 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; -import { Component, Element, Event, Fragment, Host, Method, Prop, State, h, Watch } from '@stencil/core'; +import { + Build, + Component, + Element, + Event, + Fragment, + Host, + Method, + Prop, + State, + forceUpdate, + h, + Watch, +} from '@stencil/core'; +import type { AttributeController } from '@utils/attribute-controller'; +import { createAttributeController } from '@utils/attribute-controller'; +import { checkInvalidState } from '@utils/forms'; import type { Attributes } from '@utils/helpers'; -import { inheritAriaAttributes } from '@utils/helpers'; -import { printIonWarning } from '@utils/logging'; +import { ariaAttributes } from '@utils/helpers'; +import { printIonError, printIonWarning } from '@utils/logging'; import { isRTL } from '@utils/rtl'; import { createColorClasses } from '@utils/theme'; @@ -13,6 +29,7 @@ import type { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, + InputOtpLabelFormatter, } from './input-otp-interface'; @Component({ @@ -24,9 +41,13 @@ import type { scoped: true, }) export class InputOTP implements ComponentInterface { - private inheritedAttributes: Attributes = {}; + private ariaController?: AttributeController; private inputRefs: HTMLInputElement[] = []; private inputId = `ion-input-otp-${inputIds++}`; + private helperTextId = `${this.inputId}-helper-text`; + private errorTextId = `${this.inputId}-error-text`; + private descriptionId = `${this.inputId}-description`; + private validationObserver?: MutationObserver; private parsedSeparators: number[] = []; /** @@ -55,6 +76,8 @@ export class InputOTP implements ComponentInterface { @State() hasFocus = false; @State() private previousInputValues: string[] = []; + @State() isInvalid = 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"`. @@ -73,12 +96,32 @@ export class InputOTP implements ComponentInterface { */ @Prop({ reflect: true }) disabled = false; + /** + * Text that is placed under the input boxes and displayed when an error is detected. + */ + @Prop() errorText?: string; + /** * 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'; + /** + * Text that is placed under the input boxes and displayed when no error is detected. + */ + @Prop() helperText?: string; + + /** + * A callback used to format the accessible name of each input box. It + * receives the 0-based `index` of the box and the total `length`. By default + * the name is "Input 1 of 4", or "PIN, 1 of 4" with an `aria-label` of "PIN". + * + * See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this + * if you need to access `this` from within the callback. + */ + @Prop() inputLabelFormatter?: InputOtpLabelFormatter; + /** * A hint to the browser for which keyboard to display. * Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, @@ -197,6 +240,13 @@ export class InputOTP implements ComponentInterface { } } + // The host keeps these (see `HOST_ARIA_ATTRIBUTES`), so re-render when they change + @Watch('aria-describedby') + @Watch('aria-invalid') + onHostAriaChange() { + forceUpdate(this); + } + @Watch('value') valueChanged() { this.initializeValues(); @@ -274,14 +324,59 @@ export class InputOTP implements ComponentInterface { this.parsedSeparators = separatorValues.filter((pos) => pos <= length); } + connectedCallback() { + const { el } = this; + + if (Build.isBrowser && typeof MutationObserver !== 'undefined') { + this.validationObserver = new MutationObserver(() => { + const newIsInvalid = checkInvalidState(el); + if (this.isInvalid !== newIsInvalid) { + this.isInvalid = newIsInvalid; + // Force a re-render to update aria-describedby immediately + forceUpdate(this); + } + }); + + this.validationObserver.observe(el, { + attributes: true, + attributeFilter: ['class'], + }); + } + + this.isInvalid = checkInvalidState(el); + + // Resumes watching after a move, since `componentWillLoad` only runs once + this.ariaController?.init(); + } + + disconnectedCallback() { + this.ariaController?.destroy(); + + if (this.validationObserver) { + this.validationObserver.disconnect(); + this.validationObserver = undefined; + } + } + componentWillLoad() { - this.inheritedAttributes = inheritAriaAttributes(this.el); + // Only read `role` at load so a later host write isn't copied to the group + this.ariaController = createAttributeController( + this.el, + ariaAttributes.filter((attr) => !HOST_ARIA_ATTRIBUTES.includes(attr)), + () => forceUpdate(this), + ['role'] + ); this.processSeparators(); this.initializeValues(); } componentDidLoad() { this.updateTabIndexes(); + + // The description only exists after the first render, so render again to reference it + if (this.getDescriptionText()) { + forceUpdate(this); + } } componentDidRender() { @@ -818,18 +913,90 @@ export class InputOTP implements ComponentInterface { return this.parsedSeparators.includes(index + 1) && index < length - 1; } + /** + * Returns `undefined` until the first render creates the description container. + */ + private getDescriptionText() { + return this.el.querySelector('.input-otp-description')?.textContent?.trim(); + } + + private getHintTextId(): string | undefined { + const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this; + + if (isInvalid && errorText) { + return errorTextId; + } + + if (helperText) { + return helperTextId; + } + + return undefined; + } + + private renderHintText() { + const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this; + + return [ +
+ {!isInvalid ? helperText : null} +
, + , + ]; + } + + private renderBottomContent() { + const { helperText, errorText } = this; + + if (!helperText && !errorText) { + return; + } + + return
{this.renderHintText()}
; + } + + /** + * A custom formatter is used as is so translators control the whole name. + * With a host `aria-labelledby`, each box also references itself so its + * position is still read. + */ + private getInputLabelAttributes(index: number, hostAttributes: Attributes) { + const { inputId, inputLabelFormatter, length } = this; + const hostLabel = hostAttributes['aria-label']; + const hostLabelledBy = hostAttributes['aria-labelledby']; + const position = `${index + 1} of ${length}`; + + if (inputLabelFormatter !== undefined) { + try { + return { 'aria-label': inputLabelFormatter(index, length) }; + } catch (e) { + printIonError('[ion-input-otp] - Exception in provided `inputLabelFormatter`:', e); + } + } + + if (hostLabelledBy) { + return { 'aria-label': position, 'aria-labelledby': `${hostLabelledBy} ${inputId}-${index}` }; + } + + return { 'aria-label': hostLabel ? `${hostLabel}, ${position}` : `Input ${position}` }; + } + render() { const { + ariaController, autocapitalize, color, + descriptionId, disabled, el, fill, hasFocus, - inheritedAttributes, inputId, inputRefs, inputValues, + isInvalid, length, readonly, shape, @@ -839,7 +1006,22 @@ export class InputOTP implements ComponentInterface { const inputmode = this.getInputmode(); const tabbableIndex = this.getTabbableIndex(); const pattern = this.getPattern(); - const hasDescription = el.querySelector('.input-otp-description')?.textContent?.trim() !== ''; + const descriptionText = this.getDescriptionText(); + // Keep the description visible until the first render can check it + const hasDescription = descriptionText !== ''; + + /** + * Screen readers only announce these from the focused input. The + * description stays on the group so it isn't repeated for every input. + */ + const { + 'aria-errormessage': ariaErrorMessage, + 'aria-required': ariaRequired, + ...groupAttributes + } = ariaController?.attributes ?? {}; + const hostDescribedBy = el.getAttribute('aria-describedby'); + const hostInvalid = el.getAttribute('aria-invalid') ?? undefined; + const describedBy = [hostDescribedBy, this.getHintTextId()].filter(Boolean).join(' '); return ( -
+
{Array.from({ length }).map((_, index) => ( <>
+ {this.renderBottomContent()} ); } } let inputIds = 0; + +/** + * The attribute controller removes these from the host, so an app could never + * clear them. They stay on the host instead, which only works for global + * attributes since the host has no role. + */ +const HOST_ARIA_ATTRIBUTES = ['aria-describedby', 'aria-invalid']; diff --git a/core/src/components/input-otp/test/a11y/index.html b/core/src/components/input-otp/test/a11y/index.html new file mode 100644 index 00000000000..5b3cdc71c47 --- /dev/null +++ b/core/src/components/input-otp/test/a11y/index.html @@ -0,0 +1,64 @@ + + + + + Input OTP - a11y + + + + + + + + + +
+

Input OTP

+ +

Host aria-label, description and hint text

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

Host aria-labelledby

+

Verification code

+ + +

Label formatter

+ + +

Host aria-describedby and aria-invalid

+ +

The server rejected this code

+ +
+ + + + 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 index 7cc5374d8ef..63f2085e66c 100644 --- a/core/src/components/input-otp/test/a11y/input-otp.e2e.ts +++ b/core/src/components/input-otp/test/a11y/input-otp.e2e.ts @@ -128,3 +128,116 @@ configs().forEach(({ title, config }) => { }); }); }); + +/** + * This behavior does not vary across modes/directions + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: host aria attributes'), () => { + test('should forward aria-invalid and aria-describedby set on the host after load', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent(``, config); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + + await inputOtp.evaluate((el) => { + el.setAttribute('aria-invalid', 'true'); + el.setAttribute('aria-describedby', 'server-error'); + }); + + for (const input of await inputBoxes.all()) { + await expect(input).toHaveAttribute('aria-invalid', 'true'); + await expect(input).toHaveAttribute('aria-describedby', 'server-error'); + } + }); + + test('should clear aria-invalid and aria-describedby from the inputs when removed from the host', async ({ + page, + }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent( + ``, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + + await inputOtp.evaluate((el) => { + el.removeAttribute('aria-invalid'); + el.removeAttribute('aria-describedby'); + }); + + for (const input of await inputBoxes.all()) { + await expect(input).not.toHaveAttribute('aria-invalid'); + await expect(input).not.toHaveAttribute('aria-describedby'); + } + }); + + test('should rename the inputs when the host aria-label changes', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent(``, config); + + await page.locator('ion-input-otp').evaluate((el) => el.setAttribute('aria-label', 'Code')); + + const inputBoxes = page.locator('ion-input-otp input'); + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-label', 'Code, 1 of 2'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-label', 'Code, 2 of 2'); + await expect(page.locator('ion-input-otp .input-otp-group')).toHaveAttribute('aria-label', 'Code'); + }); + + test('should describe the group, not each input, with the slotted description', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent(`Didn't get a code?`, config); + + const description = page.locator('ion-input-otp .input-otp-description'); + await expect(description).toBeVisible(); + const descriptionId = await description.getAttribute('id'); + + await expect(page.locator('ion-input-otp .input-otp-group')).toHaveAttribute('aria-describedby', descriptionId!); + for (const input of await page.locator('ion-input-otp input').all()) { + await expect(input).not.toHaveAttribute('aria-describedby'); + } + }); + + test('should restore the default labels when a host aria-label set after load is removed', async ({ + page, + }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent(``, config); + + const inputOtp = page.locator('ion-input-otp'); + await inputOtp.evaluate((el) => el.setAttribute('aria-label', 'PIN')); + await expect(page.locator('ion-input-otp input').nth(0)).toHaveAttribute('aria-label', 'PIN, 1 of 2'); + + await inputOtp.evaluate((el) => el.removeAttribute('aria-label')); + + await expect(page.locator('ion-input-otp .input-otp-group')).toHaveAttribute( + 'aria-label', + 'One-time password input' + ); + await expect(page.locator('ion-input-otp input').nth(0)).toHaveAttribute('aria-label', 'Input 1 of 2'); + }); + }); +}); diff --git a/core/src/components/input-otp/test/basic/index.html b/core/src/components/input-otp/test/basic/index.html index 71e31de7aff..c43f0c21c17 100644 --- a/core/src/components/input-otp/test/basic/index.html +++ b/core/src/components/input-otp/test/basic/index.html @@ -77,11 +77,23 @@

Readonly

Invalid / Touched

- Description - Description + Description + Description

Valid / Focused

- Description - Description + Description + Description
diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts new file mode 100644 index 00000000000..70dd0d90ea5 --- /dev/null +++ b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts @@ -0,0 +1,104 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * The hint text styles are the same across modes and directions + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: hint text rendering'), () => { + test('should not have visual regressions when rendering helper text with a description', async ({ page }) => { + await page.setContent(`Description`, config); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-helper-text-description`)); + }); + + test('should not have visual regressions when rendering helper text with wrapping text', async ({ page }) => { + await page.setContent( + ``, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-helper-text-wrapping`)); + }); + + test('should not have visual regressions when rendering error text', async ({ page }) => { + await page.setContent( + ``, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-error-text`)); + }); + }); +}); + +/** + * Customizing supporting text is the same across modes and directions + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: hint text customization'), () => { + test('should not have visual regressions when rendering error text with a custom css variable', async ({ + page, + }) => { + await page.setContent( + ` + + + `, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-error-text-custom-css-var`)); + }); + }); +}); + +/** + * This behavior does not vary across modes/directions + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: hint text functionality'), () => { + test('should switch every input between helper and error text as the validation classes change', async ({ + page, + }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent( + ``, + config + ); + + const inputOtp = page.locator('ion-input-otp'); + const inputBoxes = page.locator('ion-input-otp input'); + const helperTextId = await page.locator('ion-input-otp .helper-text').getAttribute('id'); + const errorTextId = await page.locator('ion-input-otp .error-text').getAttribute('id'); + + await inputOtp.evaluate((el) => el.classList.add('ion-touched', 'ion-invalid')); + + for (const input of await inputBoxes.all()) { + await expect(input).toHaveAttribute('aria-describedby', errorTextId!); + await expect(input).toHaveAttribute('aria-invalid', 'true'); + } + await expect(page.locator('ion-input-otp .error-text')).toHaveText('Wrong code'); + + await inputOtp.evaluate((el) => el.classList.remove('ion-invalid')); + + for (const input of await inputBoxes.all()) { + await expect(input).toHaveAttribute('aria-describedby', helperTextId!); + await expect(input).not.toHaveAttribute('aria-invalid'); + } + await expect(page.locator('ion-input-otp .helper-text')).toHaveText('Check your email'); + }); + }); +}); 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 index b20e93a4591..dbf49f91557 100644 --- a/core/src/components/input-otp/test/fill/input-otp.e2e.ts +++ b/core/src/components/input-otp/test/fill/input-otp.e2e.ts @@ -42,3 +42,58 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { }); }); }); + +/** + * The fill defaults differ between modes but not directions. + * These check computed colors since the value that wins is what's under test. + */ +configs({ directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: fill border color'), () => { + VALID_FILLS.forEach((fill) => { + test(`${fill} fill should use a --border-color set on the host`, async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent( + ` + + + `, + config + ); + + const input = page.locator('ion-input-otp input').first(); + await expect(input).toHaveCSS('border-top-color', 'rgb(255, 0, 0)'); + }); + + test(`${fill} fill should not inherit --border-color from an ancestor`, async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/31619', + }); + + await page.setContent( + ` + +
+ +
+ `, + config + ); + + const referenceColor = await page + .locator('#reference input') + .first() + .evaluate((el) => getComputedStyle(el).borderTopColor); + await expect(page.locator('#nested input').first()).toHaveCSS('border-top-color', referenceColor); + }); + }); + }); +}); diff --git a/core/src/components/input-otp/test/input-otp.spec.ts b/core/src/components/input-otp/test/input-otp.spec.ts new file mode 100644 index 00000000000..39abe131198 --- /dev/null +++ b/core/src/components/input-otp/test/input-otp.spec.ts @@ -0,0 +1,215 @@ +import { newSpecPage } from '@stencil/core/testing'; + +import { InputOTP } from '../input-otp'; + +const getInputLabels = (page: { body: HTMLElement }) => + Array.from(page.body.querySelectorAll('ion-input-otp input')).map((input) => input.getAttribute('aria-label')); + +describe('input-otp: input labels', () => { + it('should label each input with its position by default', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + expect(getInputLabels(page)).toEqual(['Input 1 of 4', 'Input 2 of 4', 'Input 3 of 4', 'Input 4 of 4']); + }); + + it('should prefix each input label with the host aria-label', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + expect(getInputLabels(page)).toEqual(['PIN, 1 of 4', 'PIN, 2 of 4', 'PIN, 3 of 4', 'PIN, 4 of 4']); + }); + + it('should label each input with the output of inputLabelFormatter', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const inputOtp = page.body.querySelector('ion-input-otp')!; + inputOtp.inputLabelFormatter = (index: number, length: number) => `Ziffer ${index + 1} von ${length}`; + await page.waitForChanges(); + + expect(getInputLabels(page)).toEqual(['Ziffer 1 von 4', 'Ziffer 2 von 4', 'Ziffer 3 von 4', 'Ziffer 4 von 4']); + }); + + it('should fall back to the default label when inputLabelFormatter throws', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + + const inputOtp = page.body.querySelector('ion-input-otp')!; + inputOtp.inputLabelFormatter = () => { + throw new Error('formatter error'); + }; + await page.waitForChanges(); + + expect(getInputLabels(page)).toEqual(['PIN, 1 of 2', 'PIN, 2 of 2']); + expect(consoleError).toHaveBeenCalled(); + consoleError.mockRestore(); + }); +}); + +describe('input-otp: hint text', () => { + it('should describe every input with the helper text', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const helperText = page.body.querySelector('ion-input-otp .helper-text')!; + expect(helperText.textContent).toBe('Check your email'); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-describedby')).toBe(helperText.id); + }); + }); +}); + +describe('input-otp: error text', () => { + it('should describe every input with the error text and mark it invalid when touched and invalid', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const errorText = page.body.querySelector('ion-input-otp .error-text')!; + expect(errorText.textContent).toBe('Wrong code'); + expect(page.body.querySelector('ion-input-otp .helper-text')!.textContent).toBe(''); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-describedby')).toBe(errorText.id); + expect(input.getAttribute('aria-invalid')).toBe('true'); + }); + }); + + it('should not mark the inputs invalid when not touched', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const helperText = page.body.querySelector('ion-input-otp .helper-text')!; + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-describedby')).toBe(helperText.id); + expect(input.hasAttribute('aria-invalid')).toBe(false); + }); + }); +}); + +describe('input-otp: description', () => { + it('should describe the group, not each input, with the slotted description', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: `Didn't get a code?`, + }); + await page.waitForChanges(); + + const description = page.body.querySelector('ion-input-otp .input-otp-description')!; + const helperText = page.body.querySelector('ion-input-otp .helper-text')!; + expect(description.id).not.toBe(''); + + const group = page.body.querySelector('ion-input-otp .input-otp-group')!; + expect(group.getAttribute('aria-describedby')).toBe(description.id); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-describedby')).toBe(helperText.id); + }); + }); + + it('should not reference the description when it is empty', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const group = page.body.querySelector('ion-input-otp .input-otp-group')!; + expect(group.hasAttribute('aria-describedby')).toBe(false); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.hasAttribute('aria-describedby')).toBe(false); + }); + }); +}); + +describe('input-otp: host aria attributes', () => { + it('should merge the host aria-describedby with the hint text on every input', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const helperText = page.body.querySelector('ion-input-otp .helper-text')!; + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-describedby')).toBe(`server-error ${helperText.id}`); + }); + }); + + it('should forward the host aria-invalid to every input', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-invalid')).toBe('true'); + }); + }); + + it('should forward aria-errormessage and aria-required to every input instead of the group', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const group = page.body.querySelector('ion-input-otp .input-otp-group')!; + expect(group.hasAttribute('aria-errormessage')).toBe(false); + expect(group.hasAttribute('aria-required')).toBe(false); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + inputs.forEach((input) => { + expect(input.getAttribute('aria-errormessage')).toBe('server-error'); + expect(input.getAttribute('aria-required')).toBe('true'); + }); + }); + + it('should name every input from the host aria-labelledby followed by its position', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const group = page.body.querySelector('ion-input-otp .input-otp-group')!; + expect(group.getAttribute('aria-labelledby')).toBe('pin-label'); + + const inputs = Array.from(page.body.querySelectorAll('ion-input-otp input')); + expect(inputs.map((input) => input.getAttribute('aria-label'))).toEqual(['1 of 2', '2 of 2']); + inputs.forEach((input) => { + expect(input.getAttribute('aria-labelledby')).toBe(`pin-label ${input.id}`); + }); + }); + + it('should keep the group label when the host has an aria-label', async () => { + const page = await newSpecPage({ + components: [InputOTP], + html: ``, + }); + + const group = page.body.querySelector('ion-input-otp .input-otp-group')!; + expect(group.getAttribute('role')).toBe('group'); + expect(group.getAttribute('aria-label')).toBe('PIN'); + }); +}); diff --git a/packages/angular/src/lazy/directives/proxies.ts b/packages/angular/src/lazy/directives/proxies.ts index b6be5b42f03..ba57340314c 100644 --- a/packages/angular/src/lazy/directives/proxies.ts +++ b/packages/angular/src/lazy/directives/proxies.ts @@ -1115,7 +1115,7 @@ This event will not emit when programmatically setting the `value` property. @ProxyCmp({ - inputs: ['autocapitalize', 'color', 'disabled', 'fill', 'inputmode', 'length', 'pattern', 'readonly', 'separators', 'shape', 'size', 'type', 'value'], + inputs: ['autocapitalize', 'color', 'disabled', 'errorText', 'fill', 'helperText', 'inputLabelFormatter', 'inputmode', 'length', 'pattern', 'readonly', 'separators', 'shape', 'size', 'type', 'value'], methods: ['setFocus'] }) @Component({ @@ -1123,7 +1123,7 @@ This event will not emit when programmatically setting the `value` property. changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['autocapitalize', 'color', 'disabled', 'fill', 'inputmode', 'length', 'pattern', 'readonly', 'separators', 'shape', 'size', 'type', 'value'], + inputs: ['autocapitalize', 'color', 'disabled', 'errorText', 'fill', 'helperText', 'inputLabelFormatter', 'inputmode', 'length', 'pattern', 'readonly', 'separators', 'shape', 'size', 'type', 'value'], outputs: ['ionInput', 'ionChange', 'ionComplete', 'ionBlur', 'ionFocus'], standalone: false }) diff --git a/packages/angular/src/standalone/directives/input-otp.ts b/packages/angular/src/standalone/directives/input-otp.ts index fe6b983174f..9c1508950ff 100644 --- a/packages/angular/src/standalone/directives/input-otp.ts +++ b/packages/angular/src/standalone/directives/input-otp.ts @@ -25,7 +25,10 @@ const INPUT_OTP_INPUTS = [ 'autocapitalize', 'color', 'disabled', + 'errorText', 'fill', + 'helperText', + 'inputLabelFormatter', 'inputmode', 'length', 'pattern', diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index f3a1d3bede3..0831646d978 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -498,7 +498,10 @@ export const IonInputOtp: StencilVueComponent Date: Wed, 28 Oct 2026 14:11:23 +0000 Subject: [PATCH 2/4] chore(): add updated snapshots --- ...custom-css-var-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 2189 bytes ...ustom-css-var-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 3124 bytes ...custom-css-var-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 2815 bytes ...otp-error-text-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 2247 bytes ...tp-error-text-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 3162 bytes ...otp-error-text-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 2793 bytes ...xt-description-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 2988 bytes ...t-description-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 3989 bytes ...xt-description-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 3866 bytes ...-text-wrapping-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 3426 bytes ...text-wrapping-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 5047 bytes ...-text-wrapping-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 5700 bytes 12 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Safari-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Safari-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Safari-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..db20b7650d784fde418a4fde14b801c4439580e7 GIT binary patch literal 2189 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 M%^3uI<>Yto54J>X#Q*>R literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..be492fcd56c5fd8994234681fe0ea22259b1ac04 GIT binary patch literal 3124 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 literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..7b5f852fc3f9f4182d0be333bfd93d457b9e280e GIT binary patch literal 2247 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 fEiz- literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..6eb5bd9992f90b4ef1cc041a2035d78b161ecbd0 GIT binary patch literal 3162 zcmeHKXH-+!8pV+kKsw4$RR~2|5b+6;7$LzBf&(H|>Afd_7=!>CdY1@Mo>BxEsvt<{ z3Bn*qKp~2NfF_6(r3FF}5qNiI&090`{=Kzky@$1af@BH4}1UptTGdP@maHn{(cDj)4bk_r7OO$83%Jtj^#dm?kord_+`CY?u2C znOjUu+6^r#mVNv;=>9jrhFi0(XC243PpUV@-W-=`dE%ha5UUlwAzT{5*(9PYi#7+# zEG)Rs>Bz{hY#*MaQa1+u1ScVbcJbAtoQ*`~DdWc1x{{AHFKo}^rzUD4#9lb4;}J4~ zo3BghQQ9xefhA^o&>Y51<7x4%D=+=SBoX8A4iw|7scA77hr-?7YpHp*yGD>dKCwZd zx}%#-JHPu_)_cQcpmaPa&9l|%FNvX&RBYWrWop_r)AwNUZ(gMob?vPx z-U5gLQQ56nZ}8ZmR{I~$h=>3zoAAUPb?A{M!qu_L=0Auw+rppSCS%uo)y4X>b(I9O z7Zp#{i~a_4pY|szyD8>E%>Oj`8?-cN3$zzkvD)sE{Qsi==|P;ud8qM{AH=s67Setu z{`|#XuD#+6uZ>jGpTB$hJaTD2Wixa6VQodxrl)phmO|Q|YDch*Z?az9w5U~^%z-)q&AM7|Qw~u>eo7ns%i{zM zFJD@p^k09!RR>Th9Yl4ltwXOhFKpUApNV<<4A9UMKtqmpN$W+X;Cxu>=B@no@VwKK zM~*Aal5IPDg8s*DQUJ-jr98zwZh_6nNOo*toa4q6OPT2xht{p%@BM^ zepVKO(d@DfxRfT~QgzHPKEmxnw%imJ+q=VyMUK|;nc%ApoqBzt?-TiV?XI0dh%O$vuAmFvVJ<$3NC6piD zZDn=})kTZeT{{3J#;s?ykRE@6u6Yj_l~BZ~_&{UB*w`{C>{l1(v2z;iCgJjkIVNfj zZ3j9f6p-_a9nbp3mq~Lq{RRoer5cx3KO}+jH-79Yxc8YfHxqM%_G-u$2%vPlxc0Cm zLD*+b^ZfSDcosZRhj>&*kQq;7gM{Plnm|c+^hSrD*#;*(9!RZ(h!lNa3Mz*c#IaUA zMC-ny-#EuM+X87eDpv0_II2NL%0B6kEe{hXIen1Uv$gK>etaI|L`C>bMz>QG z*&u;3(Uu{?T>LCP#3X!=fP3gzb%vh`+z@DT3RROr(gu9%k9o;fE4zN z_QuSCPE(8Ayb%vODG^*y@GUYRr-qq)Qw%Zn{*;-ElGOnWF(=qQHC*%l+2x^NaguB`9T_DR=}|WMISrXEH9Q+DT+#WC=*dL@YtGnCVy> z{W2}j;RTHoGe)mw&&$Q%8xCSGF-5#&nZ`zR_XfSw4;LCxqGVm!(pp+3=`Ey$IRzP- z9J-nAjGm|Y^S>G>$ofV!Zs2g@ycp1H`RBHGx{+%h=-XB?HDKXh5u^2%-dRL6h_F#3>^6qSdjamrf5@SNTZEic;D@gI8C34=D7#-@8KUv7fK6>Q`K{;{ zRFx2f@q^7bZP!&pcCb1uLQP}LjK{((o?@6t+krC_N-Fq2A7_l8*}-ltBQ~V`=38b8 znbC{;jt=5C-uU?8dBiYMzDJunoZU2bo&FwwA&Td~C*yq%8hN-P)kf*Py8?E{V^MGS)&Z9G ziaz!s5yaS}=WdvG)wjT-p{hTj!lGgB=OU$O{Lz%~KS=&rS(Po8oBSeNW9ai}iDf%M zh|Y}UOL6cU4-mhg{18@#_W=`j4G7cjmJ}>JR0Ud@n`XBKT3UL1E;{nK>n9z#3t7gXyZ7twQL- zX0YY3Y{8B_{^ZCYt@aW{%BXs}S(i3;+&m{}G9CD%o9^j6c&XJ*DV1D3?3^}_+W{e| zt-PhR)LW_YZK8#Zs5Xp24o<`PPT50FH@_OQ;c!_QSc$MV4a(oO+g%Xv%TzW(A`|V! gkx0+v|Ac#dcG5>8wQe?ZfI9;h+Tjj=27Uyh!^BLDyZ literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce1d9f0dcaddc12fe11643b23255181e882f9 GIT binary patch literal 2793 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 i`#sDpjN<2mwJPlx$Ey2t^kmN*ARF;zkgH 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 e%?|T8ht=C$Nc{(IwY8A| literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..227ee9e66136d324381c58d806cea1da1a5cbe92 GIT binary patch literal 3989 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 QaNP#!=@@F0;E#g;4nv~KJpcdz literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..7df4224d4edc2c3eaa0dc92278b70e0c04948c7d GIT binary patch literal 3866 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 literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..0e8607d0831c8e9b7d4178f03c03161ec347ae30 GIT binary patch literal 3426 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 literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-wrapping-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ee1b625dad57ebd4a22cfb0922afc90a478e20c7 GIT binary patch literal 5047 zcmeHLXHXPvmqjIKiGl>l!iYr4nITFLL^4CpC?Gk%ELxy2shMbc` zhYSPE5Wjirt9omzc7N>F*8bZceY>ik+fVg9_w+fpJN}i98W}MoF%Aw6nT9%0AA7#U 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 literal 0 HcmV?d00001 From 0955a7f73c6fa9091227ee585c23921c7a0a9862 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Tue, 3 Nov 2026 13:17:36 -0800 Subject: [PATCH 3/4] fix(input-otp): keep aria-required and aria-errormessage on host and drop comma from label --- core/src/components.d.ts | 4 +- core/src/components/input-otp/input-otp.tsx | 25 ++--- .../components/input-otp/test/a11y/index.html | 5 +- .../input-otp/test/a11y/input-otp.e2e.ts | 93 +++++++++---------- .../test/bottom-content/input-otp.e2e.ts | 18 +++- .../input-otp/test/fill/input-otp.e2e.ts | 23 ++++- .../input-otp/test/input-otp.spec.ts | 5 +- 7 files changed, 96 insertions(+), 77 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 2816528dc72..d739d9715d0 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1482,7 +1482,7 @@ export namespace Components { */ "helperText"?: string; /** - * A callback used to format the accessible name of each input box. It receives the 0-based `index` of the box and the total `length`. By default the name is "Input 1 of 4", or "PIN, 1 of 4" with an `aria-label` of "PIN". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + * A callback used to format the accessible name of each input box. It receives the 0-based `index` of the box and the total `length`. By default the name is "Input 1 of 4", or "PIN 1 of 4" with an `aria-label` of "PIN". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. */ "inputLabelFormatter"?: InputOtpLabelFormatter; /** @@ -6654,7 +6654,7 @@ declare namespace LocalJSX { */ "helperText"?: string; /** - * A callback used to format the accessible name of each input box. It receives the 0-based `index` of the box and the total `length`. By default the name is "Input 1 of 4", or "PIN, 1 of 4" with an `aria-label` of "PIN". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. + * A callback used to format the accessible name of each input box. It receives the 0-based `index` of the box and the total `length`. By default the name is "Input 1 of 4", or "PIN 1 of 4" with an `aria-label` of "PIN". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback. */ "inputLabelFormatter"?: InputOtpLabelFormatter; /** diff --git a/core/src/components/input-otp/input-otp.tsx b/core/src/components/input-otp/input-otp.tsx index 59955488d45..22dabe2a99a 100644 --- a/core/src/components/input-otp/input-otp.tsx +++ b/core/src/components/input-otp/input-otp.tsx @@ -115,7 +115,7 @@ export class InputOTP implements ComponentInterface { /** * A callback used to format the accessible name of each input box. It * receives the 0-based `index` of the box and the total `length`. By default - * the name is "Input 1 of 4", or "PIN, 1 of 4" with an `aria-label` of "PIN". + * the name is "Input 1 of 4", or "PIN 1 of 4" with an `aria-label` of "PIN". * * See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this * if you need to access `this` from within the callback. @@ -242,7 +242,9 @@ export class InputOTP implements ComponentInterface { // The host keeps these (see `HOST_ARIA_ATTRIBUTES`), so re-render when they change @Watch('aria-describedby') + @Watch('aria-errormessage') @Watch('aria-invalid') + @Watch('aria-required') onHostAriaChange() { forceUpdate(this); } @@ -980,7 +982,7 @@ export class InputOTP implements ComponentInterface { return { 'aria-label': position, 'aria-labelledby': `${hostLabelledBy} ${inputId}-${index}` }; } - return { 'aria-label': hostLabel ? `${hostLabel}, ${position}` : `Input ${position}` }; + return { 'aria-label': hostLabel ? `${hostLabel} ${position}` : `Input ${position}` }; } render() { @@ -1010,17 +1012,16 @@ export class InputOTP implements ComponentInterface { // Keep the description visible until the first render can check it const hasDescription = descriptionText !== ''; + const groupAttributes = ariaController?.attributes ?? {}; + /** * Screen readers only announce these from the focused input. The * description stays on the group so it isn't repeated for every input. */ - const { - 'aria-errormessage': ariaErrorMessage, - 'aria-required': ariaRequired, - ...groupAttributes - } = ariaController?.attributes ?? {}; const hostDescribedBy = el.getAttribute('aria-describedby'); + const hostErrorMessage = el.getAttribute('aria-errormessage') ?? undefined; const hostInvalid = el.getAttribute('aria-invalid') ?? undefined; + const hostRequired = el.getAttribute('aria-required') ?? undefined; const describedBy = [hostDescribedBy, this.getHintTextId()].filter(Boolean).join(' '); return ( @@ -1050,9 +1051,9 @@ export class InputOTP implements ComponentInterface { id={`${inputId}-${index}`} {...this.getInputLabelAttributes(index, groupAttributes)} aria-describedby={describedBy || undefined} - aria-errormessage={ariaErrorMessage} + aria-errormessage={hostErrorMessage} aria-invalid={isInvalid ? 'true' : hostInvalid} - aria-required={ariaRequired} + aria-required={hostRequired} type="text" autoCapitalize={autocapitalize} inputmode={inputmode} @@ -1100,7 +1101,7 @@ let inputIds = 0; /** * The attribute controller removes these from the host, so an app could never - * clear them. They stay on the host instead, which only works for global - * attributes since the host has no role. + * clear them. They stay on the host instead, which has no role, so + * `aria-required` is ignored there. */ -const HOST_ARIA_ATTRIBUTES = ['aria-describedby', 'aria-invalid']; +const HOST_ARIA_ATTRIBUTES = ['aria-describedby', 'aria-errormessage', 'aria-invalid', 'aria-required']; diff --git a/core/src/components/input-otp/test/a11y/index.html b/core/src/components/input-otp/test/a11y/index.html index 5b3cdc71c47..b3ea9903233 100644 --- a/core/src/components/input-otp/test/a11y/index.html +++ b/core/src/components/input-otp/test/a11y/index.html @@ -36,7 +36,7 @@

Label formatter

Host aria-describedby and aria-invalid

-

The server rejected this code

+ @@ -50,13 +50,16 @@

Host aria-describedby and aria-invalid

document.querySelector('#formatter').inputLabelFormatter = (index, length) => `Ziffer ${index + 1} von ${length}`; const hostAria = document.querySelector('#host-aria'); + const serverError = document.querySelector('#server-error'); document.querySelector('#toggle-host-aria').addEventListener('click', () => { if (hostAria.hasAttribute('aria-invalid')) { hostAria.removeAttribute('aria-invalid'); hostAria.removeAttribute('aria-describedby'); + serverError.hidden = true; } else { hostAria.setAttribute('aria-invalid', 'true'); hostAria.setAttribute('aria-describedby', 'server-error'); + serverError.hidden = false; } }); 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 index 63f2085e66c..6579894e781 100644 --- a/core/src/components/input-otp/test/a11y/input-otp.e2e.ts +++ b/core/src/components/input-otp/test/a11y/input-otp.e2e.ts @@ -21,6 +21,27 @@ configs().forEach(({ title, config }) => { expect(results.violations).toEqual([]); }); + test('should not have accessibility violations with the host ARIA attributes set', async ({ page }) => { + await page.setContent( + ` +
+ +

The server rejected this code

+
+ `, + 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); @@ -134,12 +155,7 @@ configs().forEach(({ title, config }) => { */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('input-otp: host aria attributes'), () => { - test('should forward aria-invalid and aria-describedby set on the host after load', async ({ page }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/31619', - }); - + test('should forward the host ARIA attributes set after load', async ({ page }) => { await page.setContent(``, config); const inputOtp = page.locator('ion-input-otp'); @@ -148,24 +164,27 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await inputOtp.evaluate((el) => { el.setAttribute('aria-invalid', 'true'); el.setAttribute('aria-describedby', 'server-error'); + el.setAttribute('aria-errormessage', 'server-error'); + el.setAttribute('aria-required', 'true'); }); for (const input of await inputBoxes.all()) { await expect(input).toHaveAttribute('aria-invalid', 'true'); await expect(input).toHaveAttribute('aria-describedby', 'server-error'); + await expect(input).toHaveAttribute('aria-errormessage', 'server-error'); + await expect(input).toHaveAttribute('aria-required', 'true'); } }); - test('should clear aria-invalid and aria-describedby from the inputs when removed from the host', async ({ - page, - }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/31619', - }); - + test('should clear the host ARIA attributes from the inputs when removed from the host', async ({ page }) => { await page.setContent( - ``, + ``, config ); @@ -175,61 +194,35 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await inputOtp.evaluate((el) => { el.removeAttribute('aria-invalid'); el.removeAttribute('aria-describedby'); + el.removeAttribute('aria-errormessage'); + el.removeAttribute('aria-required'); }); for (const input of await inputBoxes.all()) { await expect(input).not.toHaveAttribute('aria-invalid'); await expect(input).not.toHaveAttribute('aria-describedby'); + await expect(input).not.toHaveAttribute('aria-errormessage'); + await expect(input).not.toHaveAttribute('aria-required'); } }); - test('should rename the inputs when the host aria-label changes', async ({ page }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/31619', - }); - + test('should rename the inputs when the host aria-label changes', async ({ page }) => { await page.setContent(``, config); await page.locator('ion-input-otp').evaluate((el) => el.setAttribute('aria-label', 'Code')); const inputBoxes = page.locator('ion-input-otp input'); - await expect(inputBoxes.nth(0)).toHaveAttribute('aria-label', 'Code, 1 of 2'); - await expect(inputBoxes.nth(1)).toHaveAttribute('aria-label', 'Code, 2 of 2'); + await expect(inputBoxes.nth(0)).toHaveAttribute('aria-label', 'Code 1 of 2'); + await expect(inputBoxes.nth(1)).toHaveAttribute('aria-label', 'Code 2 of 2'); await expect(page.locator('ion-input-otp .input-otp-group')).toHaveAttribute('aria-label', 'Code'); }); - test('should describe the group, not each input, with the slotted description', async ({ page }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/31619', - }); - - await page.setContent(`Didn't get a code?`, config); - - const description = page.locator('ion-input-otp .input-otp-description'); - await expect(description).toBeVisible(); - const descriptionId = await description.getAttribute('id'); - - await expect(page.locator('ion-input-otp .input-otp-group')).toHaveAttribute('aria-describedby', descriptionId!); - for (const input of await page.locator('ion-input-otp input').all()) { - await expect(input).not.toHaveAttribute('aria-describedby'); - } - }); - - test('should restore the default labels when a host aria-label set after load is removed', async ({ - page, - }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/31619', - }); - + test('should restore the default labels when a host aria-label set after load is removed', async ({ page }) => { await page.setContent(``, config); const inputOtp = page.locator('ion-input-otp'); await inputOtp.evaluate((el) => el.setAttribute('aria-label', 'PIN')); - await expect(page.locator('ion-input-otp input').nth(0)).toHaveAttribute('aria-label', 'PIN, 1 of 2'); + await expect(page.locator('ion-input-otp input').nth(0)).toHaveAttribute('aria-label', 'PIN 1 of 2'); await inputOtp.evaluate((el) => el.removeAttribute('aria-label')); diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts index 70dd0d90ea5..1641cb27166 100644 --- a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts +++ b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts @@ -7,7 +7,10 @@ import { configs, test } from '@utils/test/playwright'; configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { test.describe(title('input-otp: hint text rendering'), () => { test('should not have visual regressions when rendering helper text with a description', async ({ page }) => { - await page.setContent(`Description`, config); + await page.setContent( + `Description`, + config + ); const inputOtp = page.locator('ion-input-otp'); await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-helper-text-description`)); @@ -23,14 +26,19 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-helper-text-wrapping`)); }); - test('should not have visual regressions when rendering error text', async ({ page }) => { + test('should not have visual regressions when rendering error text with a description', async ({ page }) => { await page.setContent( - ``, + `Description`, config ); const inputOtp = page.locator('ion-input-otp'); - await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-error-text`)); + await expect(inputOtp).toHaveScreenshot(screenshot(`input-otp-error-text-description`)); }); }); }); @@ -50,7 +58,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co --highlight-color-invalid: purple; } - + `, config ); 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 index dbf49f91557..fc90b4aefd7 100644 --- a/core/src/components/input-otp/test/fill/input-otp.e2e.ts +++ b/core/src/components/input-otp/test/fill/input-otp.e2e.ts @@ -72,12 +72,25 @@ configs({ directions: ['ltr'] }).forEach(({ title, config }) => { await expect(input).toHaveCSS('border-top-color', 'rgb(255, 0, 0)'); }); - test(`${fill} fill should not inherit --border-color from an ancestor`, async ({ page }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/31619', - }); + test(`${fill} fill should keep the invalid border over a --border-color set on the host`, async ({ page }) => { + await page.setContent( + ` + + + `, + config + ); + + const input = page.locator('ion-input-otp input').first(); + await expect(input).toHaveCSS('border-top-color', 'rgb(0, 0, 255)'); + }); + test(`${fill} fill should not inherit --border-color from an ancestor`, async ({ page }) => { await page.setContent( ` diff --git a/core/src/components/input-otp/test/input-otp.spec.ts b/core/src/components/input-otp/test/input-otp.spec.ts index 39abe131198..ba0f52e55aa 100644 --- a/core/src/components/input-otp/test/input-otp.spec.ts +++ b/core/src/components/input-otp/test/input-otp.spec.ts @@ -21,7 +21,7 @@ describe('input-otp: input labels', () => { html: ``, }); - expect(getInputLabels(page)).toEqual(['PIN, 1 of 4', 'PIN, 2 of 4', 'PIN, 3 of 4', 'PIN, 4 of 4']); + expect(getInputLabels(page)).toEqual(['PIN 1 of 4', 'PIN 2 of 4', 'PIN 3 of 4', 'PIN 4 of 4']); }); it('should label each input with the output of inputLabelFormatter', async () => { @@ -50,7 +50,7 @@ describe('input-otp: input labels', () => { }; await page.waitForChanges(); - expect(getInputLabels(page)).toEqual(['PIN, 1 of 2', 'PIN, 2 of 2']); + expect(getInputLabels(page)).toEqual(['PIN 1 of 2', 'PIN 2 of 2']); expect(consoleError).toHaveBeenCalled(); consoleError.mockRestore(); }); @@ -117,6 +117,7 @@ describe('input-otp: description', () => { const description = page.body.querySelector('ion-input-otp .input-otp-description')!; const helperText = page.body.querySelector('ion-input-otp .helper-text')!; expect(description.id).not.toBe(''); + expect(description.classList.contains('input-otp-description-hidden')).toBe(false); const group = page.body.querySelector('ion-input-otp .input-otp-group')!; expect(group.getAttribute('aria-describedby')).toBe(description.id); From 916a54977bb4c24328c2fcd22049104f37ab8ab6 Mon Sep 17 00:00:00 2001 From: ionitron Date: Tue, 3 Nov 2026 21:48:11 +0000 Subject: [PATCH 4/4] chore(): add updated snapshots --- ...tom-css-var-md-ltr-Mobile-Chrome-linux.png | Bin 2189 -> 2062 bytes ...om-css-var-md-ltr-Mobile-Firefox-linux.png | Bin 3124 -> 2809 bytes ...tom-css-var-md-ltr-Mobile-Safari-linux.png | Bin 2815 -> 2570 bytes ...description-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 3230 bytes ...escription-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 4025 bytes ...description-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 3807 bytes ...description-md-ltr-Mobile-Chrome-linux.png | Bin 2988 -> 2804 bytes ...escription-md-ltr-Mobile-Firefox-linux.png | Bin 3989 -> 3557 bytes ...description-md-ltr-Mobile-Safari-linux.png | Bin 3866 -> 3656 bytes 9 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Chrome-linux.png index db20b7650d784fde418a4fde14b801c4439580e7..36fd8a872648aea64568846cf8a3ed7018e6c54a 100644 GIT binary patch literal 2062 zcmbW2dpHw}9>-UUdM8wrh};Ujmet(e#aguSx+Y_+9GBP@i^?{aT$)RBODV)OB$-(l)$;rLx1?)c)K;tscr{-Z$-84V^^kAK3M@7#UX zQ1Jrtj;H!r6OivT*ONb7`w4dAUgEcet-V`h_cWGPS0u1Gm~+kW&wyV%b^we#Fo8gv zg4KVIJjhz3w=MIUuMdU*wj4N|Q24dS{?@VZLMv>ITmOtFnXGwdmws|;v_6T7SD6{- zy(dJz%bPX%sXw*v=!}v0Qp0_wa<5P zWIy!GWL&2*Qf;HPsTpjW)rc?(4`BNybxK=krv6&44ShP@T2oWzfIQ| zoBH~PANEM^7hJ9}n4uS60j`_v=9;>=Lw1+gW}og`wq!B0u{=Sht}yXXh&>*=RgV;n4Rv;5 z`@9p|$>6$ioH_h8%T zxZq-}-FovyNBRPglTsMf?4+5|^SsrXoqRI%K$2X`8u1OwgWjd}2aa#g8L^(FZ(Qtk zmTHcM>f;VUrHY~+XItBEiqLf$oIT)2sAK`#R8t+&oa5~dDd(<-SwQUYqIyDyjRI9U zZ|{dw*rfjfs6s2(`5?>Zzw?F+7W1E2+*DLTPID zIr3rd>sEmY%?vdl82tT3R+r#k6Q7H#!dPPm5NE$Pr?)866WU9#c1c%mOZP+99i&B; z2FHIBjzz?V?cT_Ql5M~Cy>dD6AO>mgGl}`f4h2ag;4|Q1Ld|Ewt1KCs!!@xwYGnn- z3{2}K^^^n^k32254E~z~ZybbfbXrjhu6k5m-@TA;_uV^Z1UP0MEm2>X?J7>-u??vP zY(Kf6TM7U2Eyvx3fU-l`W0DtGZ4i*-bMx*LQC836ZKozTqZ;Iv6judc{pky31Enq! z(K7^v=QX}N8&h&Kgf{^Yy`?lT91xMo-H{I^n)Vj~mrUQCT>C%9`M>brMgIdKhX#~8thQOp;%L5 ztC=k0$9g0HVZMqPmEE8fZhX|ta`f{1+(B~m#$nFo0<-XS_m+gPt05|VJrIZ~we;ox z<%$0=kq79?g*nbX79>*k_CsTExv_)N0??OzvJ=CXWR4b%A_HuiW~YS+$+gQrY5+ux z^@{1Yw~pIl`Aa=s5uk(We&;=H%;Ffqu(kG(?^TO;7gyGvz0? zPMAa7Atj%iXaO^S3%uG|hDw{hzeZYiiOPGnGit1@m=rx)B$%n=Be=_jT-#OgqZB0- ze*W-W(c`bzWE7X0!w^Hs>l9gI(^~~8AC`yzF!G! zT~zq_NhauvZ>_C=th7zY=w|4l`c}ro=hYgFaKTNX7`i1qf( zCz+=uDzLCw4H^)5GkGSn${2wdiLZl^PUDdmQkP@4Y}#XmT{5^};$>j9M}B$ytqEE; zEeK6Jiw?!=R8)2n&tYk(G?=RF#SCe9ZgMrjT$)h}OWDY=5eGb!FrJws)u6aF^(_$U z<7@6ZRr4J33GSuvOZkzkk9{AztPLJsw-L%J>~Z%q;LP|BUV})qH@jcEGM8N2?Xzo= zS>nQ=|9XZMwpm*-QS-vT;#0?Tu)l?GnHQNu>9hZFGp6XRiY7dD-yct&HbwU)JYE!Yq ztC%>Fjhlb?)NI^{zLBn%ZQrZVyt)z0E{kxM{MF~HTONylkhiEO56W$%f6GkS764## jQ^-G4Cr@?7zsETcFcr|XFI<>Qkt+ZvlsmH4J}~(&3cKwa literal 2189 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 M%^3uI<>Yto54J>X#Q*>R diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-custom-css-var-md-ltr-Mobile-Firefox-linux.png index be492fcd56c5fd8994234681fe0ea22259b1ac04..2eaa426709cdc4f2367806ff19604eb502c23462 100644 GIT binary patch literal 2809 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 A1poj5 literal 3124 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 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..296c4903e3b8654dc35a0ac3fd8d88beec5f09d5 GIT binary patch literal 3230 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 X6!I4HBzi}3Nlu}sWdyF$coy+5ADZB* literal 0 HcmV?d00001 diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..d221e9c5d911cf3c1385775dbc4828a75eaafe8e GIT binary patch literal 3807 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 e%?|T8ht=C$Nc{(IwY8A| diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Firefox-linux.png index 227ee9e66136d324381c58d806cea1da1a5cbe92..16737ef2c40503403bea1a6d58f60817aee9812a 100644 GIT binary patch literal 3557 zcmcgvX*iqP7N*2ZP(!OksG3idI#o5D7-}r4iq=@=sHs}Bm_x--6{^j_p~f0w3MEky zLr}vhryPQ+s9D7nQ#I86dhR{<{<_b-_s{+DefxR#exLQOz4lt~+B?PC(wLV^jEjkh ziPzM`&;}S6fG?bb4fxOPxHJLt)ux7U+i;ile9k9AMnXwc&KT z?{+OK9KOh}UM$e*C|Cj)(oW9&iOGlsa$jBQjN$F4)lS@l5)V@Xli4_Efyt@G@|b(y z$}g$U52@W8ZDiygwtF;aecXA4wb>iSR&5|ug!oWoeGXN4GE%0SPe?{8sSwJk$_g?~ zmXbHo=Yhio-8s0j_zkSliD!@ig#X_|WR&~bPrfheVWsGxZj=KMpIs%qoPx7M%qb*`#H_&HNYaQ{H zRrYnTcX=9m*s4)bjAygoq;%}@9_F?8=(QoOP2!5C-reX)k|$jH_%IgPee`gBswr@_ z3{z$Qx}(|uL-58_v%e#?O?}PrTYa=uP!hCg8gP5yxxGsz+1A``<%vBIjWYM1RFY8o zUK7iQ1J`1tb_BgtNJyx{i@1po3N#EHiGsnObA`5~OQnnjB z2TO03TMil6k5gcOV$#oPd`|W89>l$f3*&yQKhof7Uha+~HTN&<^X6Z9B>gJ}BZsHP zM5YYZi*!ck3oF@~SXx?+^pK}B_nNND;sYuwD&&b?!;qHMS9yMT8=f=nc%LydA4;u+ z&I=BL=HS;Uxtkv}UO$@eeVBhWq!bwU9s61A7A*6xmVse1EF!!Pt_%+!MiSsDxyBJc zzI-!*^+D|H%&mq32?OVGif+ynsfb7fI$g~Jj5-Z$02nScH$w1bfRvE^5x@v}bo`%)g%^$vcgW7Ip+8Dy{P@CTm$31% z)i~oq7ZSw%k8s}Kp6j{WT?SX*g2u~eu2JeRPl0;8MY~}B;=VX!oO1d5$sm!)i-M-o zf{y4b!7FIm1kPY5N`?$nwE832HQzi7f2#G}yLZo2VlqwCOrlPX4{`-(tFX0ZRTFs{ z0pqr#)ph=AP0xjM2V`&icL>6|w`Y( z$mDB;{Qz6 zIxfMSM2w1SflVsD;neED3z&12XAPi0qn@{2TRjkrYp0v%>^V=QllI*h2%B6M9?|+Yj~T(Du2Hy3 z5d(J#A{s*usSx8{^JQ<|Z2witBu-9dy!v*D3!Ofwk^xb%0z{=4ue6o;4kYR{>wCk2 zN=L0n=ALj0itEKVjn_DqdkC4F zYFXFP#Y{s(n_;iAL9s{MDBa5VL+M&ayMkOmO7pd8MB<8v%n>FYzxp2DDuO6t!R z^yhcE^w|V()y0S6E{b}3t~z5M!>B*#Aa<=I7c9CDj(10`M&5+@M$m9?)e%P>@hpo; z=N_QPu=azcrq4V!rdtr=?&OyJ^$*w|khJgzY80%!)zZ7X@(6`3XP~}b0B-aL4eKmN(n~B*$laVQ)|QxK zgoGuazPEH4v`drqcN2(PUEB?Tf}hn2t>zMV8yFO{cifr4zBuXi@_EW>IUiK)mrtlY zo$f35dU=F3h~lhcMF#9G2Q!gnK4Vpg>viATI#xxdyyt)fOn%j@LmjQ-sl~`dy|H4X_JlRhyq~e4nT&NRWgAkb*twT9-d-Fdk$gzfT5ane=Dnw7MyAN8NdUhOOPx;mmRrPn6Xg#j_xovd=I{ufFv;*) z=4h*<_`e4xbp1}gahE|?UQr23UrHy8zF;bGZE0blLiA3(Tl|vc>c>_*37RdT71}py zrFTrl=gZm!mvIUrd$&X=DRL$}5!>_3+tMGA&ys^&Eb={TI!-~7mP#F#%uCme0P)6 z4&{?_&amB6eFb!dk+#UNyl{p%P!4Jv+{2#)MILO7MV4R0WT2Fv%Uqm!ZYJ>%O56I#7P zkl7&2^9g$pC$1(d&kC1@fu*JUq#Q(~f9q*PvF}WE&j|||vIS^( z^zrMU-i=Jy@&<5&)6xef@+RA3V|nij`&u2LkQOW{;eFw_5TK`v*rT<=HnK4;2vei8 z!w~WZLlEaCN2W1Ro&?VO;jikV;Z2;xwfwyau`1q&oq{dw*CA^WyuZDY&0_{$K{`4?<5W>^-VT< z`Nm=IH2HYfD@+G=h^rOoh{T(nF4ZXDqsdwPQs{z*C$J&RSyb)o+V=0Hm#_?TN_DXb z?7#qZyF|OUTrLyxPlIp^i8`o&2Nxd3&$O27Tbokb(A$Xe_eSpaj>7pDjnYIaS2E{W z(loc;^z`(+kx!(%gGAx2z~$%FjOuv77jBUEFO9r<{VnVu?=xI#JB3hQ1NhI7zn(zd ZdQ>Kwv~o1_L*V(wWNKt-SgY@b{2Rt3!Xp3x literal 3989 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 QaNP#!=@@F0;E#g;4nv~KJpcdz diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Safari-linux.png index 7df4224d4edc2c3eaa0dc92278b70e0c04948c7d..67e8bea5b50f40bbbdebb90ffb72b0a28d1544fd 100644 GIT binary patch literal 3656 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