From ec0ca8901691d7a5347792833c00f628de3a7bce Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Thu, 18 May 2023 15:13:41 -0400 Subject: [PATCH 1/4] fix(checkbox,radio): click event is triggered once --- core/src/components/checkbox/checkbox.tsx | 9 ++++++++ .../checkbox/test/checkbox.spec.tsx | 21 +++++++++++++++++++ core/src/components/radio/radio.tsx | 18 +++++++++++++++- .../test/{radio.spec.ts => radio.spec.tsx} | 21 +++++++++++++++++-- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 core/src/components/checkbox/test/checkbox.spec.tsx rename core/src/components/radio/test/{radio.spec.ts => radio.spec.tsx} (69%) diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index 7a2386f01a9..8ef75214f46 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -204,6 +204,14 @@ export class Checkbox implements ComponentInterface { this.ionBlur.emit(); }; + private onClick = (ev: MouseEvent) => { + // The modern control syntax renders a label as a parent element + // to the checkbox, which means when the label is clicked, the + // checkbox receives this event. To prevent a duplicate click event, + // we need to stop the event from bubbling. + ev.stopPropagation(); + }; + // TODO(FW-3100): run contents of renderCheckbox directly instead render() { const { legacyFormController } = this; @@ -256,6 +264,7 @@ export class Checkbox implements ComponentInterface { id={inputId} onChange={this.toggleChecked} onFocus={() => this.onFocus()} + onClick={this.onClick} onBlur={() => this.onBlur()} ref={(focusEl) => (this.focusEl = focusEl)} {...inheritedAttributes} diff --git a/core/src/components/checkbox/test/checkbox.spec.tsx b/core/src/components/checkbox/test/checkbox.spec.tsx new file mode 100644 index 00000000000..ca8bc3a5d56 --- /dev/null +++ b/core/src/components/checkbox/test/checkbox.spec.tsx @@ -0,0 +1,21 @@ +import { h } from '@stencil/core'; +import { newSpecPage } from '@stencil/core/testing'; + +import { Checkbox } from '../Checkbox'; + +describe('ion-checkbox', () => { + it('click event is triggered once', async () => { + const mockOnClick = jest.fn(); + + const page = await newSpecPage({ + components: [Checkbox], + template: () => , + }); + + const checkbox = page.body.querySelector('ion-checkbox'); + + await checkbox.click(); + + expect(mockOnClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/core/src/components/radio/radio.tsx b/core/src/components/radio/radio.tsx index 38f9faaadb8..3238a3219e9 100644 --- a/core/src/components/radio/radio.tsx +++ b/core/src/components/radio/radio.tsx @@ -200,7 +200,7 @@ export class Radio implements ComponentInterface { }; private onClick = () => { - this.checked = this.nativeInput.checked; + this.setChecked(); }; private onFocus = () => { @@ -211,6 +211,14 @@ export class Radio implements ComponentInterface { this.ionBlur.emit(); }; + private toggleChecked = () => { + this.setChecked(); + }; + + private setChecked() { + this.checked = this.nativeInput.checked; + } + private get hasLabel() { return this.el.textContent !== ''; } @@ -262,6 +270,14 @@ export class Radio implements ComponentInterface { id={inputId} ref={(nativeEl) => (this.nativeInput = nativeEl as HTMLInputElement)} {...inheritedAttributes} + onChange={this.toggleChecked} + onClick={(ev) => { + // The modern control syntax renders a label as a parent element + // to the radio, which means when the label is clicked, the + // radio receives this event. To prevent a duplicate click event, + // we need to stop the event from bubbling. + ev.stopPropagation(); + }} />
{ it('should set a default value', async () => { const radio = new Radio(); @@ -30,4 +32,19 @@ describe('ion-radio', () => { expect(radio.classList.contains('radio-checked')).toBe(true); }); + + it('click event is triggered once', async () => { + const mockOnClick = jest.fn(); + + const page = await newSpecPage({ + components: [Radio], + template: () => Radio, + }); + + const radio = page.body.querySelector('ion-radio'); + + await radio.click(); + + expect(mockOnClick).toHaveBeenCalledTimes(1); + }); }); From cdf981b19680d0e15932d8aa22486988b61b9dd9 Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Thu, 18 May 2023 15:25:35 -0400 Subject: [PATCH 2/4] chore: fix path --- core/src/components/checkbox/test/checkbox.spec.tsx | 2 +- core/src/components/radio/test/radio.spec.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/components/checkbox/test/checkbox.spec.tsx b/core/src/components/checkbox/test/checkbox.spec.tsx index ca8bc3a5d56..e707dc47794 100644 --- a/core/src/components/checkbox/test/checkbox.spec.tsx +++ b/core/src/components/checkbox/test/checkbox.spec.tsx @@ -1,7 +1,7 @@ import { h } from '@stencil/core'; import { newSpecPage } from '@stencil/core/testing'; -import { Checkbox } from '../Checkbox'; +import { Checkbox } from '../checkbox'; describe('ion-checkbox', () => { it('click event is triggered once', async () => { diff --git a/core/src/components/radio/test/radio.spec.tsx b/core/src/components/radio/test/radio.spec.tsx index abf66416f16..fa2ba5d025f 100644 --- a/core/src/components/radio/test/radio.spec.tsx +++ b/core/src/components/radio/test/radio.spec.tsx @@ -2,7 +2,7 @@ import { h } from '@stencil/core'; import { newSpecPage } from '@stencil/core/testing'; import { RadioGroup } from '../../radio-group/radio-group.tsx'; -import { Radio } from '../radio.tsx'; +import { Radio } from '../radio'; describe('ion-radio', () => { it('should set a default value', async () => { From 37c95087ecaab19293ceca1850612c8998d8176b Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Wed, 31 May 2023 15:04:30 -0400 Subject: [PATCH 3/4] chore: remove invalid tests --- .../checkbox/test/checkbox.spec.tsx | 21 ----------------- .../test/{radio.spec.tsx => radio.spec.ts} | 23 +++---------------- 2 files changed, 3 insertions(+), 41 deletions(-) delete mode 100644 core/src/components/checkbox/test/checkbox.spec.tsx rename core/src/components/radio/test/{radio.spec.tsx => radio.spec.ts} (66%) diff --git a/core/src/components/checkbox/test/checkbox.spec.tsx b/core/src/components/checkbox/test/checkbox.spec.tsx deleted file mode 100644 index e707dc47794..00000000000 --- a/core/src/components/checkbox/test/checkbox.spec.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { h } from '@stencil/core'; -import { newSpecPage } from '@stencil/core/testing'; - -import { Checkbox } from '../checkbox'; - -describe('ion-checkbox', () => { - it('click event is triggered once', async () => { - const mockOnClick = jest.fn(); - - const page = await newSpecPage({ - components: [Checkbox], - template: () => , - }); - - const checkbox = page.body.querySelector('ion-checkbox'); - - await checkbox.click(); - - expect(mockOnClick).toHaveBeenCalledTimes(1); - }); -}); diff --git a/core/src/components/radio/test/radio.spec.tsx b/core/src/components/radio/test/radio.spec.ts similarity index 66% rename from core/src/components/radio/test/radio.spec.tsx rename to core/src/components/radio/test/radio.spec.ts index fa2ba5d025f..a017caa9aa2 100644 --- a/core/src/components/radio/test/radio.spec.tsx +++ b/core/src/components/radio/test/radio.spec.ts @@ -1,8 +1,6 @@ -import { h } from '@stencil/core'; -import { newSpecPage } from '@stencil/core/testing'; - +import { Radio } from '../radio.tsx'; import { RadioGroup } from '../../radio-group/radio-group.tsx'; -import { Radio } from '../radio'; +import { newSpecPage } from '@stencil/core/testing'; describe('ion-radio', () => { it('should set a default value', async () => { @@ -32,19 +30,4 @@ describe('ion-radio', () => { expect(radio.classList.contains('radio-checked')).toBe(true); }); - - it('click event is triggered once', async () => { - const mockOnClick = jest.fn(); - - const page = await newSpecPage({ - components: [Radio], - template: () => Radio, - }); - - const radio = page.body.querySelector('ion-radio'); - - await radio.click(); - - expect(mockOnClick).toHaveBeenCalledTimes(1); - }); -}); +}); \ No newline at end of file From bc633a1ecd4086cd85def17adf92bde2c17f6e43 Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Thu, 1 Jun 2023 12:03:11 -0400 Subject: [PATCH 4/4] chore: prettier formatting --- core/src/components/radio/test/radio.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/radio/test/radio.spec.ts b/core/src/components/radio/test/radio.spec.ts index a017caa9aa2..88b45471094 100644 --- a/core/src/components/radio/test/radio.spec.ts +++ b/core/src/components/radio/test/radio.spec.ts @@ -30,4 +30,4 @@ describe('ion-radio', () => { expect(radio.classList.contains('radio-checked')).toBe(true); }); -}); \ No newline at end of file +});