diff --git a/BREAKING.md b/BREAKING.md
index 9583abf975..7d3233f1f4 100644
--- a/BREAKING.md
+++ b/BREAKING.md
@@ -15,6 +15,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Browser and Platform Support](#version-7x-browser-platform-support)
- [Components](#version-7x-components)
- [Accordion Group](#version-7x-accordion-group)
+ - [Checkbox](#version-7x-checkbox)
- [Input](#version-7x-input)
- [Overlays](#version-7x-overlays)
- [Range](#version-7x-range)
@@ -56,6 +57,10 @@ This section details the desktop browser, JavaScript framework, and mobile platf
`ionChange` is no longer emitted when the `value` of `ion-accordion-group` is modified externally. `ionChange` is only emitted from user committed changes, such as clicking or tapping the accordion header.
+
Checkbox
+
+`ionChange` is no longer emitted when the `checked` property of `ion-checkbox` is modified externally. `ionChange` is only emitted from user committed changes, such as clicking or tapping the checkbox.
+
`ionChange` is no longer emitted when the `value` of `ion-input` is modified externally. `ionChange` is only emitted from user committed changes, such as typing in the input and the input losing focus or from clicking the clear action within the input.
diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts
index c30a4d98fd..cb4e3c71c1 100644
--- a/angular/src/directives/proxies.ts
+++ b/angular/src/directives/proxies.ts
@@ -388,7 +388,10 @@ export class IonCardTitle {
import type { CheckboxChangeEventDetail as ICheckboxCheckboxChangeEventDetail } from '@ionic/core';
export declare interface IonCheckbox extends Components.IonCheckbox {
/**
- * Emitted when the checked property has changed.
+ * Emitted when the checked property has changed
+as a result of a user action such as a click.
+This event will not emit when programmatically
+setting the checked property.
*/
ionChange: EventEmitter>;
/**
diff --git a/angular/test/base/e2e/src/inputs.spec.ts b/angular/test/base/e2e/src/inputs.spec.ts
index c04296c567..9991ad234e 100644
--- a/angular/test/base/e2e/src/inputs.spec.ts
+++ b/angular/test/base/e2e/src/inputs.spec.ts
@@ -38,7 +38,7 @@ describe('Inputs', () => {
it('change values should update angular', () => {
cy.get('#reset-button').click();
- cy.get('ion-checkbox').invoke('prop', 'checked', true);
+ cy.get('ion-checkbox#first-checkbox').click();
cy.get('ion-toggle').invoke('prop', 'checked', true);
cy.get('ion-input').eq(0).type('hola');
diff --git a/angular/test/base/src/app/inputs/inputs.component.html b/angular/test/base/src/app/inputs/inputs.component.html
index 5e6ff4a9c4..fa65a16dcb 100644
--- a/angular/test/base/src/app/inputs/inputs.component.html
+++ b/angular/test/base/src/app/inputs/inputs.component.html
@@ -75,7 +75,7 @@
Checkbox
-
+
{{checkbox}}
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index 00fee7860e..c9a38ccdb3 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -4314,7 +4314,7 @@ declare namespace LocalJSX {
*/
"onIonBlur"?: (event: IonCheckboxCustomEvent) => void;
/**
- * Emitted when the checked property has changed.
+ * Emitted when the checked property has changed as a result of a user action such as a click. This event will not emit when programmatically setting the checked property.
*/
"onIonChange"?: (event: IonCheckboxCustomEvent) => void;
/**
diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx
index 5d8e482b78..239494d0b1 100644
--- a/core/src/components/checkbox/checkbox.tsx
+++ b/core/src/components/checkbox/checkbox.tsx
@@ -63,7 +63,10 @@ export class Checkbox implements ComponentInterface {
@Prop() value: any | null = 'on';
/**
- * Emitted when the checked property has changed.
+ * Emitted when the checked property has changed
+ * as a result of a user action such as a click.
+ * This event will not emit when programmatically
+ * setting the checked property.
*/
@Event() ionChange!: EventEmitter;
@@ -88,11 +91,7 @@ export class Checkbox implements ComponentInterface {
}
@Watch('checked')
- checkedChanged(isChecked: boolean) {
- this.ionChange.emit({
- checked: isChecked,
- value: this.value,
- });
+ checkedChanged() {
this.emitStyle();
}
@@ -114,11 +113,25 @@ export class Checkbox implements ComponentInterface {
}
}
- private onClick = (ev: any) => {
+ /**
+ * Sets the checked property and emits
+ * the ionChange event. Use this to update the
+ * checked state in response to user-generated
+ * actions such as a click.
+ */
+ private setChecked = (state: boolean) => {
+ const isChecked = (this.checked = state);
+ this.ionChange.emit({
+ checked: isChecked,
+ value: this.value,
+ });
+ };
+
+ private toggleChecked = (ev: any) => {
ev.preventDefault();
this.setFocus();
- this.checked = !this.checked;
+ this.setChecked(!this.checked);
this.indeterminate = false;
};
@@ -153,7 +166,6 @@ export class Checkbox implements ComponentInterface {
return (
this.onFocus()}
onBlur={() => this.onBlur()}
ref={(focusEl) => (this.focusEl = focusEl)}
diff --git a/core/src/components/checkbox/test/basic/checkbox.e2e.ts b/core/src/components/checkbox/test/basic/checkbox.e2e.ts
index ac8efcfd2a..6181e94408 100644
--- a/core/src/components/checkbox/test/basic/checkbox.e2e.ts
+++ b/core/src/components/checkbox/test/basic/checkbox.e2e.ts
@@ -10,3 +10,52 @@ test.describe('checkbox: basic', () => {
expect(await page.screenshot()).toMatchSnapshot(`checkbox-basic-${page.getSnapshotSettings()}.png`);
});
});
+
+test.describe('checkbox: ionChange', () => {
+ test.beforeEach(({ skip }) => {
+ skip.rtl();
+ });
+ test('should fire ionChange when interacting with checkbox', async ({ page }) => {
+ await page.setContent(`
+
+ `);
+
+ const ionChange = await page.spyOnEvent('ionChange');
+ const checkbox = page.locator('ion-checkbox');
+
+ await checkbox.click();
+ await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
+
+ await checkbox.click();
+ await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
+ });
+
+ test('should fire ionChange when interacting with checkbox in item', async ({ page }) => {
+ await page.setContent(`
+
+
+
+ `);
+
+ const ionChange = await page.spyOnEvent('ionChange');
+ const item = page.locator('ion-item');
+
+ await item.click();
+ await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: true });
+
+ await item.click();
+ await expect(ionChange).toHaveReceivedEventDetail({ value: 'my-checkbox', checked: false });
+ });
+
+ test('should not fire when programmatically setting a value', async ({ page }) => {
+ await page.setContent(`
+
+ `);
+
+ const ionChange = await page.spyOnEvent('ionChange');
+ const checkbox = page.locator('ion-checkbox');
+
+ await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
+ await expect(ionChange).not.toHaveReceivedEvent();
+ });
+});
diff --git a/core/src/components/datetime/test/color/datetime.e2e.ts b/core/src/components/datetime/test/color/datetime.e2e.ts
index 35543ece3e..c7c6c668b6 100644
--- a/core/src/components/datetime/test/color/datetime.e2e.ts
+++ b/core/src/components/datetime/test/color/datetime.e2e.ts
@@ -6,10 +6,9 @@ test.describe('datetime: color', () => {
await page.goto('/src/components/datetime/test/color');
const colorSelect = page.locator('ion-select');
- const darkModeToggle = page.locator('ion-checkbox');
const datetime = page.locator('ion-datetime');
- await darkModeToggle.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
+ await page.evaluate(() => document.body.classList.toggle('dark'));
await page.waitForChanges();
expect(await datetime.first().screenshot()).toMatchSnapshot(
@@ -19,7 +18,7 @@ test.describe('datetime: color', () => {
`datetime-color-custom-dark-${page.getSnapshotSettings()}.png`
);
- await darkModeToggle.evaluate((el: HTMLIonCheckboxElement) => (el.checked = false));
+ await page.evaluate(() => document.body.classList.toggle('dark'));
await colorSelect.evaluate((el: HTMLIonSelectElement) => (el.value = 'danger'));
await page.waitForChanges();
@@ -30,7 +29,7 @@ test.describe('datetime: color', () => {
`datetime-color-custom-light-color-${page.getSnapshotSettings()}.png`
);
- await darkModeToggle.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
+ await page.evaluate(() => document.body.classList.toggle('dark'));
await page.waitForChanges();
expect(await datetime.first().screenshot()).toMatchSnapshot(