diff --git a/core/src/components/radio-group/radio-group.tsx b/core/src/components/radio-group/radio-group.tsx
index 37e0f0da0c9..e667448d957 100644
--- a/core/src/components/radio-group/radio-group.tsx
+++ b/core/src/components/radio-group/radio-group.tsx
@@ -1,6 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Listen, Method, Prop, State, Watch, h } from '@stencil/core';
-import { checkInvalidState } from '@utils/forms';
+import { checkInvalidState, isOptionSelected } from '@utils/forms';
import { renderHiddenInput } from '@utils/helpers';
import { getIonMode } from '../../global/ionic-global';
@@ -104,7 +104,7 @@ export class RadioGroup implements ComponentInterface {
// Get the first radio that is not disabled and the checked one
const first = radios.find((radio) => !radio.disabled);
- const checked = radios.find((radio) => radio.value === value && !radio.disabled);
+ const checked = radios.find((radio) => !radio.disabled && isOptionSelected(value, radio.value, this.compareWith));
if (!first && !checked) {
return;
@@ -219,7 +219,8 @@ export class RadioGroup implements ComponentInterface {
if (selectedRadio && !selectedRadio.disabled) {
const currentValue = this.value;
const newValue = selectedRadio.value;
- if (newValue !== currentValue) {
+ const isSelected = isOptionSelected(currentValue, newValue, this.compareWith);
+ if (!isSelected) {
this.value = newValue;
this.emitValueChange(ev);
} else if (this.allowEmptySelection) {
@@ -275,8 +276,9 @@ export class RadioGroup implements ComponentInterface {
// space bar on top of a selected radio
if ([' '].includes(ev.key)) {
const previousValue = this.value;
- this.value = this.allowEmptySelection && this.value !== undefined ? undefined : current.value;
- if (previousValue !== this.value || this.allowEmptySelection) {
+ const isSelected = isOptionSelected(this.value, current.value, this.compareWith);
+ this.value = this.allowEmptySelection && isSelected ? undefined : current.value;
+ if (!isOptionSelected(previousValue, this.value, this.compareWith) || this.allowEmptySelection) {
/**
* Value change should only be emitted if the value is different,
* such as selecting a new radio with the space bar or if
diff --git a/core/src/components/radio-group/test/basic/radio-group.e2e.ts b/core/src/components/radio-group/test/basic/radio-group.e2e.ts
index cea527e62d6..54ca11118f2 100644
--- a/core/src/components/radio-group/test/basic/radio-group.e2e.ts
+++ b/core/src/components/radio-group/test/basic/radio-group.e2e.ts
@@ -135,5 +135,65 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await expect(radioOne).not.toHaveClass(/radio-checked/);
await expect(radioTwo).toHaveClass(/radio-checked/);
});
+
+ test('click should deselect with allowEmptySelection when using compareWith and object values', async ({ page }, testInfo) => {
+ testInfo.annotations.push({
+ type: 'issue',
+ description: 'https://github.com/ionic-team/ionic-framework/issues/30560',
+ });
+
+ await page.setContent(
+ `
+
+
+ One
+
+
+
+ `,
+ config
+ );
+
+ await radioFixture.checkRadio('mouse');
+ await radioFixture.expectChecked(false);
+ const radioGroup = page.locator('ion-radio-group');
+ await expect(radioGroup).toHaveJSProperty('value', undefined);
+ });
+
+ test('should not throw error when value is null and compareWith is used', async ({ page }, testInfo) => {
+ testInfo.annotations.push({
+ type: 'issue',
+ description: 'https://github.com/ionic-team/ionic-framework/issues/30560',
+ });
+
+ await page.setContent(
+ `
+
+
+ One
+
+
+
+ `,
+ config
+ );
+
+ await radioFixture.checkRadio('mouse');
+ await radioFixture.expectChecked(true);
+ const radioGroup = page.locator('ion-radio-group');
+ await expect(radioGroup).toHaveJSProperty('value', { id: 1, name: 'One' });
+ });
});
});
diff --git a/core/src/utils/forms/compare-with-utils.ts b/core/src/utils/forms/compare-with-utils.ts
index 2ab1fd0237b..a1c65717599 100644
--- a/core/src/utils/forms/compare-with-utils.ts
+++ b/core/src/utils/forms/compare-with-utils.ts
@@ -15,6 +15,9 @@ export const compareOptions = (
if (typeof compareWith === 'function') {
return compareWith(currentValue, compareValue);
} else if (typeof compareWith === 'string') {
+ if (currentValue == null || compareValue == null) {
+ return currentValue === compareValue;
+ }
return currentValue[compareWith] === compareValue[compareWith];
} else {
return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;