diff --git a/core/api.txt b/core/api.txt index 745d82786af..f9dc93375b1 100644 --- a/core/api.txt +++ b/core/api.txt @@ -784,9 +784,12 @@ 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,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,mode,"ios" | "md",undefined,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 diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 7721801eb60..073af3da096 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1457,11 +1457,19 @@ 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 and displayed when no error is detected. + */ + "helperText"?: string; /** * 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" */ @@ -1471,6 +1479,10 @@ export namespace Components { * @default 4 */ "length": number; + /** + * The mode determines which platform styles to use. + */ + "mode"?: "ios" | "md"; /** * 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}]"` */ @@ -6739,11 +6751,19 @@ 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 and displayed when no error is detected. + */ + "helperText"?: string; /** * 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" */ @@ -6753,6 +6773,10 @@ declare namespace LocalJSX { * @default 4 */ "length"?: number; + /** + * The mode determines which platform styles to use. + */ + "mode"?: "ios" | "md"; /** * Emitted when the input group loses focus. */ diff --git a/core/src/components/input-otp/input-otp.scss b/core/src/components/input-otp/input-otp.scss index 2b8b12ee585..b44bab1b9df 100644 --- a/core/src/components/input-otp/input-otp.scss +++ b/core/src/components/input-otp/input-otp.scss @@ -120,7 +120,15 @@ // Input Description // ---------------------------------------------------------------- -.input-otp-description { +.input-otp-description-hidden { + display: none; +} + +// Input Description & Bottom Content +// ---------------------------------------------------------------- + +.input-otp-description, +.input-otp-bottom { color: $text-color-step-300; font-size: dynamic-font(12px); @@ -130,10 +138,6 @@ text-align: center; } -.input-otp-description-hidden { - display: none; -} - // Input Separator // ---------------------------------------------------------------- @@ -271,6 +275,34 @@ --border-color: var(--highlight-color); } +// Input Hint Text +// ---------------------------------------------------------------- + +/** + * Error text should only be shown when .ion-invalid is + * present on the input. Otherwise the helper text should + * be shown. + */ +.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; +} + // Colors // ---------------------------------------------------------------- diff --git a/core/src/components/input-otp/input-otp.tsx b/core/src/components/input-otp/input-otp.tsx index a93eabd926d..1310fab2df7 100644 --- a/core/src/components/input-otp/input-otp.tsx +++ b/core/src/components/input-otp/input-otp.tsx @@ -1,5 +1,5 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; -import { Component, Element, Event, Fragment, Host, Prop, State, h, Watch } from '@stencil/core'; +import { Build, Component, Element, Event, Fragment, Host, Prop, State, h, Watch, forceUpdate } from '@stencil/core'; import type { Attributes } from '@utils/helpers'; import { inheritAriaAttributes } from '@utils/helpers'; import { printIonWarning } from '@utils/logging'; @@ -16,6 +16,10 @@ import type { InputOtpInputEventDetail, } from './input-otp-interface'; +/** + * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use. + * @slot - The default slot is for the input-otp's description. + */ @Component({ tag: 'ion-input-otp', styleUrls: { @@ -28,7 +32,10 @@ export class InputOTP implements ComponentInterface { private inheritedAttributes: Attributes = {}; private inputRefs: HTMLInputElement[] = []; private inputId = `ion-input-otp-${inputIds++}`; + private helperTextId = `${this.inputId}-helper-text`; + private errorTextId = `${this.inputId}-error-text`; private parsedSeparators: number[] = []; + private validationObserver?: MutationObserver; /** * Stores the initial value of the input when it receives focus. @@ -50,6 +57,11 @@ export class InputOTP implements ComponentInterface { @State() hasFocus = false; @State() private previousInputValues: string[] = []; + /** + * Track validation state for proper aria-live announcements + */ + @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"`. @@ -136,6 +148,16 @@ export class InputOTP implements ComponentInterface { */ @Prop({ mutable: true }) value?: string | number | null = ''; + /** + * Text that is placed under the input and displayed when no error is detected. + */ + @Prop() helperText?: string; + + /** + * Text that is placed under the input boxes and displayed when an error is detected. + */ + @Prop() errorText?: string; + /** * 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 @@ -263,6 +285,30 @@ export class InputOTP implements ComponentInterface { this.parsedSeparators = separatorValues.filter((pos) => pos <= length); } + connectedCallback() { + const { el } = this; + + // Watch for class changes to update validation state + if (Build.isBrowser && typeof MutationObserver !== 'undefined') { + this.validationObserver = new MutationObserver(() => { + const newIsInvalid = this.checkInvalidState(); + 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'], + }); + } + + // Always set initial state + this.isInvalid = this.checkInvalidState(); + } + componentWillLoad() { this.inheritedAttributes = inheritAriaAttributes(this.el); this.processSeparators(); @@ -781,6 +827,70 @@ export class InputOTP implements ComponentInterface { return this.parsedSeparators.includes(index + 1) && index < length - 1; } + /** + * Renders the helper text or error text values + */ + private renderHintText() { + const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this; + + return [ +
+ {!isInvalid ? helperText : null} +
, + , + ]; + } + + private getHintTextID(): string | undefined { + const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this; + + if (isInvalid && errorText) { + return errorTextId; + } + + if (helperText) { + return helperTextId; + } + + return undefined; + } + + /** + * Responsible for rendering helper text and + * error text. This element should only + * be rendered if hint text is set. + * It will not conflict with + * the description slot since the description + * will always be rendered regardless of + * whether helper or error text is present. + */ + private renderBottomContent() { + const { helperText, errorText } = this; + + /** + * undefined and empty string values should + * be treated as not having helper/error text. + */ + const hasHintText = !!helperText || !!errorText; + if (!hasHintText) { + return; + } + + return
{this.renderHintText()}
; + } + + /** + * Checks if the input otp is in an invalid state based on Ionic validation classes + */ + private checkInvalidState(): boolean { + const hasIonTouched = this.el.classList.contains('ion-touched'); + const hasIonInvalid = this.el.classList.contains('ion-invalid'); + + return hasIonTouched && hasIonInvalid; + } + render() { const { autocapitalize, @@ -816,7 +926,14 @@ export class InputOTP implements ComponentInterface { 'input-otp-readonly': readonly, })} > -
+
{Array.from({ length }).map((_, index) => ( <>
@@ -853,6 +970,7 @@ export class InputOTP implements ComponentInterface { >
+ {this.renderBottomContent()} ); } diff --git a/core/src/components/input-otp/test/bottom-content/index.html b/core/src/components/input-otp/test/bottom-content/index.html new file mode 100644 index 00000000000..505f4c96e03 --- /dev/null +++ b/core/src/components/input-otp/test/bottom-content/index.html @@ -0,0 +1,97 @@ + + + + + Input OTP - Bottom Content + + + + + + + + + + + + + + Input OTP - Bottom Content + + + + +
+
+

Description: No Hint

+ Description +
+ +
+

Description: Helper Text

+ Description +
+ +
+

Description: Error Text

+ Description +
+
+ +
+
+

No Description: No Hint

+ +
+ +
+

No Description: Helper Text

+ +
+ +
+

No Description: Error Text

+ +
+
+ + + + +
+
+ + 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..f02014ebf1b --- /dev/null +++ b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts @@ -0,0 +1,137 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * Rendering is different across modes + */ +configs({ modes: ['ios', 'md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('input-otp: helper text rendering'), () => { + test('should not have visual regressions when rendering helper text with no description', async ({ page }) => { + await page.setContent(``, config); + + const bottomEl = page.locator('ion-input-otp'); + await expect(bottomEl).toHaveScreenshot(screenshot(`input-otp-helper-text-no-description`)); + }); + test('should not have visual regressions when rendering helper text with description', async ({ page }) => { + await page.setContent(`Description`, config); + + const bottomEl = page.locator('ion-input-otp'); + await expect(bottomEl).toHaveScreenshot(screenshot(`input-otp-helper-text-description`)); + }); + }); + + test.describe(title('input: error text rendering'), () => { + test('should not have visual regressions when rendering error text with no description', async ({ page }) => { + await page.setContent( + ``, + config + ); + + const bottomEl = page.locator('ion-input-otp'); + await expect(bottomEl).toHaveScreenshot(screenshot(`input-otp-error-text-no-description`)); + }); + test('should not have visual regressions when rendering error text with description', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const bottomEl = page.locator('ion-input-otp'); + await expect(bottomEl).toHaveScreenshot(screenshot(`input-otp-error-text-description`)); + }); + }); +}); + +/** + * Functionality is the same across modes & directions + */ +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('input-otp: bottom content functionality'), () => { + test('should not render bottom content if no hint is enabled', async ({ page }) => { + await page.setContent(` Description `, config); + + const bottomEl = page.locator('ion-input-otp .input-otp-bottom'); + await expect(bottomEl).toHaveCount(0); + }); + test('helper text should be visible initially', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const helperText = page.locator('ion-input-otp .helper-text'); + const errorText = page.locator('ion-input-otp .error-text'); + await expect(helperText).toBeVisible(); + await expect(helperText).toHaveText('Helper text'); + await expect(errorText).toBeHidden(); + }); + test('input-otp should have an aria-describedby attribute when helper text is present', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtpGroup = page.locator('ion-input-otp [role="group"]'); + const helperText = page.locator('ion-input-otp .helper-text'); + const helperTextId = await helperText.getAttribute('id'); + const ariaDescribedBy = await inputOtpGroup.getAttribute('aria-describedby'); + + expect(ariaDescribedBy).toBe(helperTextId); + }); + test('error text should be visible when input-otp is invalid', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const helperText = page.locator('ion-input-otp .helper-text'); + const errorText = page.locator('ion-input-otp .error-text'); + await expect(helperText).toBeHidden(); + await expect(errorText).toBeVisible(); + await expect(errorText).toHaveText('Error text'); + }); + + test('input-otp should have an aria-describedby attribute when error text is present', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtpGroup = page.locator('ion-input-otp [role="group"]'); + const errorText = page.locator('ion-input-otp .error-text'); + const errorTextId = await errorText.getAttribute('id'); + const ariaDescribedBy = await inputOtpGroup.getAttribute('aria-describedby'); + + expect(ariaDescribedBy).toBe(errorTextId); + }); + test('input-otp should have aria-invalid attribute when input-otp is invalid', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtpGroup = page.locator('ion-input-otp [role="group"]'); + + await expect(inputOtpGroup).toHaveAttribute('aria-invalid'); + }); + test('input-otp should not have aria-invalid attribute when input-otp is valid', async ({ page }) => { + await page.setContent( + `Description`, + config + ); + + const inputOtpGroup = page.locator('ion-input-otp [role="group"]'); + + await expect(inputOtpGroup).not.toHaveAttribute('aria-invalid'); + }); + test('input-otp should not have aria-describedby attribute when no hint or error text is present', async ({ + page, + }) => { + await page.setContent(`Description`, config); + + const inputOtpGroup = page.locator('ion-input-otp [role="group"]'); + + await expect(inputOtpGroup).not.toHaveAttribute('aria-describedby'); + }); + }); +}); diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f973d47425f Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..a80d37f6e3f Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..ef850f2434b Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-ios-ltr-Mobile-Safari-linux.png differ 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 00000000000..da58d502ec9 Binary files /dev/null and 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 differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..5ebea3a8210 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-description-md-ltr-Mobile-Firefox-linux.png differ 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 00000000000..da42311d651 Binary files /dev/null and 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 differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..5a06a1ac647 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..ebf36e03cf0 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..f486fb1cf89 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-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-no-description-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..231362827bc Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-md-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..7eddda03292 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-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-no-description-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..1d0ef032352 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-error-text-no-description-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..bcb42d50226 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..700ca7e4d4f Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..66390ceee64 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..1e80de674ba Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-description-md-ltr-Mobile-Chrome-linux.png differ 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 00000000000..7fc362d6222 Binary files /dev/null and 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 differ 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 00000000000..e9251f33a91 Binary files /dev/null and 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 differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..01a0a3f7e84 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..083f1d060cb Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Safari-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..48b091da064 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-md-ltr-Mobile-Chrome-linux.png b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..22a94825328 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-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-no-description-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..fffebab5794 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-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-no-description-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..9afa393c244 Binary files /dev/null and b/core/src/components/input-otp/test/bottom-content/input-otp.e2e.ts-snapshots/input-otp-helper-text-no-description-md-ltr-Mobile-Safari-linux.png differ diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index c81ac8f8571..36942fa5a70 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1018,7 +1018,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', 'inputmode', 'length', 'mode', 'pattern', 'readonly', 'separators', 'shape', 'size', 'type', 'value'], methods: ['setFocus'] }) @Component({ @@ -1026,7 +1026,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', 'inputmode', 'length', 'mode', 'pattern', 'readonly', 'separators', 'shape', 'size', 'type', 'value'], }) export class IonInputOtp { protected el: HTMLIonInputOtpElement; diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index a0c23232908..2f00685a18b 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -506,6 +506,8 @@ export const IonInputOtp: StencilVueComponent