From eaf28d56abf37ac6672df36687ef83f93a0c89ba Mon Sep 17 00:00:00 2001 From: rudrakshagarwal4200-ai Date: Sat, 17 Oct 2026 02:15:47 +0530 Subject: [PATCH 1/3] Add null checks in compareWith utility functionfix(forms): guard compareOptions against null values Add null checks for currentValue and compareValue in compareWith function. --- core/src/utils/forms/compare-with-utils.ts | 3 +++ 1 file changed, 3 insertions(+) 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; From 8c669fc384508ecc0271dc29a98382747d5aba5a Mon Sep 17 00:00:00 2001 From: rudrakshagarwal4200-ai Date: Sat, 17 Oct 2026 02:16:44 +0530 Subject: [PATCH 2/3] Refactor radio selection logic with isOptionSelected --- core/src/components/radio-group/radio-group.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 From afed743296d817bde45ae86fd64ba967f39f2103 Mon Sep 17 00:00:00 2001 From: rudrakshagarwal4200-ai Date: Sat, 17 Oct 2026 02:17:18 +0530 Subject: [PATCH 3/3] Enhance radio group e2e tests for selection scenarios Add tests for radio group behavior with empty selection and null values. --- .../radio-group/test/basic/radio-group.e2e.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) 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' }); + }); }); });