diff --git a/core/src/components/select/test/basic/select.e2e.ts b/core/src/components/select/test/basic/select.e2e.ts
index 63f1c8ca10a..8be07e84d68 100644
--- a/core/src/components/select/test/basic/select.e2e.ts
+++ b/core/src/components/select/test/basic/select.e2e.ts
@@ -1,6 +1,6 @@
import { expect } from '@playwright/test';
-import { configs, test } from '@utils/test/playwright';
import type { E2ELocator } from '@utils/test/playwright';
+import { configs, test } from '@utils/test/playwright';
/**
* This checks that certain overlays open correctly. While the
@@ -150,6 +150,45 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
expect(alerts.length).toBe(1);
});
});
+
+ test.describe(title('select: click'), () => {
+ test('should trigger onclick only once when clicking the label', async ({ page }, testInfo) => {
+ testInfo.annotations.push({
+ type: 'issue',
+ description: 'https://github.com/ionic-team/ionic-framework/issues/30165',
+ });
+ // Create a spy function in page context
+ await page.setContent(
+ `
+
+ Apple
+ Banana
+
+ `,
+ config
+ );
+
+ // Track calls to the exposed function
+ const clickEvent = await page.spyOnEvent('click');
+ const input = page.locator('label.select-wrapper');
+
+ // Use position to make sure we click into the label enough to trigger
+ // what would be the double click
+ await input.click({
+ position: {
+ x: 5,
+ y: 5,
+ },
+ });
+
+ // Verify the click was triggered exactly once
+ expect(clickEvent).toHaveReceivedEventTimes(1);
+
+ // Verify that the event target is the checkbox and not the item
+ const event = clickEvent.events[0];
+ expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-select');
+ });
+ });
});
/**
diff --git a/core/src/components/textarea/test/basic/textarea.e2e.ts b/core/src/components/textarea/test/basic/textarea.e2e.ts
new file mode 100644
index 00000000000..fcb4bab29c7
--- /dev/null
+++ b/core/src/components/textarea/test/basic/textarea.e2e.ts
@@ -0,0 +1,35 @@
+import { expect } from '@playwright/test';
+import { configs, test } from '@utils/test/playwright';
+
+configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('textarea: click'), () => {
+ test('should trigger onclick only once when clicking the label', async ({ page }, testInfo) => {
+ testInfo.annotations.push({
+ type: 'issue',
+ description: 'https://github.com/ionic-team/ionic-framework/issues/30165',
+ });
+ // Create a spy function in page context
+ await page.setContent(`
`, config);
+
+ // Track calls to the exposed function
+ const clickEvent = await page.spyOnEvent('click');
+ const input = page.locator('label.textarea-wrapper');
+
+ // Use position to make sure we click into the label enough to trigger
+ // what would be the double click
+ await input.click({
+ position: {
+ x: 5,
+ y: 5,
+ },
+ });
+
+ // Verify the click was triggered exactly once
+ expect(clickEvent).toHaveReceivedEventTimes(1);
+
+ // Verify that the event target is the checkbox and not the item
+ const event = clickEvent.events[0];
+ expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-textarea');
+ });
+ });
+});
diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx
index 78541e9d66b..64ff00c9225 100644
--- a/core/src/components/textarea/textarea.tsx
+++ b/core/src/components/textarea/textarea.tsx
@@ -572,6 +572,18 @@ export class Textarea implements ComponentInterface {
return this.label !== undefined || this.labelSlot !== null;
}
+ /**
+ * Stops propagation when the label is clicked,
+ * otherwise, two clicks will be triggered.
+ */
+ private onLabelClick = (ev: MouseEvent) => {
+ // Only stop propagation if the click was directly on the label
+ // and not on the input or other child elements
+ if (ev.target === ev.currentTarget) {
+ ev.stopPropagation();
+ }
+ };
+
/**
* Renders the border container when fill="outline".
*/
@@ -726,7 +738,7 @@ export class Textarea implements ComponentInterface {
* interactable, clicking the label would focus that instead
* since it comes before the textarea in the DOM.
*/}
-