From 3d0434bbb2740ce69bf59ef9cedf91d69a96b785 Mon Sep 17 00:00:00 2001 From: Davos <58948657+Elements2720@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:14:15 +0300 Subject: [PATCH] fix(textarea): place initial caret at end --- .../textarea/test/basic/textarea.e2e.ts | 36 +++++++++++++++++++ core/src/components/textarea/textarea.tsx | 5 ++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/core/src/components/textarea/test/basic/textarea.e2e.ts b/core/src/components/textarea/test/basic/textarea.e2e.ts index d3d43361f67..be3809b4d18 100644 --- a/core/src/components/textarea/test/basic/textarea.e2e.ts +++ b/core/src/components/textarea/test/basic/textarea.e2e.ts @@ -6,6 +6,42 @@ import { configs, test } from '@utils/test/playwright'; */ configs({ directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('textarea: basic'), () => { + // Regression coverage for https://github.com/ionic-team/ionic-framework/issues/31579 + test('should place the caret at the end when focused with an initial value', async ({ page }) => { + await page.setContent(``, config); + + const textarea = page.locator('ion-textarea'); + const nativeTextarea = textarea.locator('textarea'); + + await textarea.evaluate(async (el: HTMLIonTextareaElement) => { + await el.setFocus(); + }); + + await expect(nativeTextarea).toBeFocused(); + await expect + .poll(() => nativeTextarea.evaluate((el: HTMLTextAreaElement) => [el.selectionStart, el.selectionEnd])) + .toEqual([11, 11]); + }); + + test('should preserve an existing selection when an unrelated render occurs', async ({ page }) => { + await page.setContent(``, config); + + const textarea = page.locator('ion-textarea'); + const nativeTextarea = textarea.locator('textarea'); + + await textarea.evaluate(async (el: HTMLIonTextareaElement) => { + await el.setFocus(); + }); + await nativeTextarea.evaluate((el: HTMLTextAreaElement) => el.setSelectionRange(1, 4)); + + await textarea.evaluate((el) => el.setAttribute('helper-text', 'Updated helper text')); + await page.waitForChanges(); + + await expect + .poll(() => nativeTextarea.evaluate((el: HTMLTextAreaElement) => [el.selectionStart, el.selectionEnd])) + .toEqual([1, 4]); + }); + test('should stretch to fill height when min-height is set on the host', async ({ page }) => { await page.setContent( ` diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 297b5cdebc3..01a33f8d840 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -804,6 +804,7 @@ export class Textarea implements ComponentInterface { cols={this.cols} rows={this.rows} wrap={this.wrap} + value={value} onInput={this.onInput} onChange={this.onChange} onBlur={this.onBlur} @@ -812,9 +813,7 @@ export class Textarea implements ComponentInterface { aria-describedby={this.getHintTextID()} aria-invalid={this.isInvalid ? 'true' : undefined} {...this.inheritedAttributes} - > - {value} - + />
(this.endContainerEl = el)}>