diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index 3b9c3e6724a..4a9132665fd 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -199,6 +199,14 @@ export class Checkbox implements ComponentInterface { this.toggleChecked(ev); }; + /** + * Stops propagation when the display label is clicked, + * otherwise, two clicks will be triggered. + */ + private onDivLabelClick = (ev: MouseEvent) => { + ev.stopPropagation(); + }; + private getHintTextID(): string | undefined { const { el, helperText, errorText, helperTextId, errorTextId } = this; @@ -314,6 +322,7 @@ export class Checkbox implements ComponentInterface { }} part="label" id={this.inputLabelId} + onClick={this.onDivLabelClick} > {this.renderHintText()} diff --git a/core/src/components/checkbox/test/basic/checkbox.e2e.ts b/core/src/components/checkbox/test/basic/checkbox.e2e.ts index 1a41b339599..159e86f9a30 100644 --- a/core/src/components/checkbox/test/basic/checkbox.e2e.ts +++ b/core/src/components/checkbox/test/basic/checkbox.e2e.ts @@ -99,4 +99,38 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => expect(ionChange).not.toHaveReceivedEvent(); }); }); + + test.describe(title('checkbox: 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(`Test Checkbox`, config); + + // Track calls to the exposed function + let clickCount = 0; + page.on('console', (msg) => { + if (msg.text().includes('click called')) { + clickCount++; + } + }); + + const input = page.locator('div.label-text-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(clickCount).toBe(1); + }); + }); }); diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 5f4b1257141..c2834138aa8 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -720,6 +720,18 @@ export class Input 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". @@ -815,9 +827,9 @@ export class Input implements ComponentInterface { * interactable, clicking the label would focus that instead * since it comes before the input in the DOM. */} -