Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dec3411
fix(radio-button): prevent invalid/warn states when disabled/readonly
anuanto966 Oct 27, 2025
f7cdbc4
fix(time-picker): prevent invalid/warn states when disabled/readonly
anuanto966 Oct 28, 2025
5c8cdf6
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 28, 2025
fca3b42
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 28, 2025
fc5eb1a
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 28, 2025
8dadd97
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 28, 2025
38d5d72
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 29, 2025
c378b4c
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 30, 2025
62587ca
chore: update snapshots
heloiselui Oct 30, 2025
d3dd1be
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 30, 2025
55c4628
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 30, 2025
444bda6
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Oct 31, 2025
0998c7d
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Nov 4, 2025
ae6b5ca
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Nov 5, 2025
8a8242c
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Nov 6, 2025
4f0e5d2
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Nov 6, 2025
46744b1
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Nov 6, 2025
5a18680
Merge branch 'main' into fix-20730-disallow-invalid-warn
riddhybansal Nov 6, 2025
a007621
Merge branch 'main' into fix-20730-disallow-invalid-warn
tay1orjones Nov 6, 2025
3d79aef
Merge branch 'main' into fix-20730-disallow-invalid-warn
riddhybansal Nov 6, 2025
20455f8
Merge branch 'main' into fix-20730-disallow-invalid-warn
anuanto966 Nov 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -327,5 +327,121 @@ describe('RadioButtonGroup', () => {
expect(screen.getByDisplayValue('option-1')).toBeRequired();
expect(screen.getByDisplayValue('option-2')).toBeRequired();
});

describe('Invalid and Warning States', () => {
it('should apply invalid class when invalid prop is true', () => {
const { container } = render(
<RadioButtonGroup
invalid={true}
invalidText="Invalid selection"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = container.querySelector('fieldset');
expect(fieldset).toHaveClass(`${prefix}--radio-button-group--invalid`);
expect(screen.getByText('Invalid selection')).toBeInTheDocument();
});

it('should apply warning class when warn prop is true', () => {
const { container } = render(
<RadioButtonGroup
warn={true}
warnText="Warning message"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = container.querySelector('fieldset');
expect(fieldset).toHaveClass(`${prefix}--radio-button-group--warning`);
expect(screen.getByText('Warning message')).toBeInTheDocument();
});

it('should not apply invalid class or show invalid text when disabled and invalid', () => {
const { container } = render(
<RadioButtonGroup
disabled={true}
invalid={true}
invalidText="Invalid selection"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = container.querySelector('fieldset');
expect(fieldset).not.toHaveClass(
`${prefix}--radio-button-group--invalid`
);
expect(screen.queryByText('Invalid selection')).not.toBeInTheDocument();
});

it('should not apply warning class or show warning text when disabled and warn', () => {
const { container } = render(
<RadioButtonGroup
disabled={true}
warn={true}
warnText="Warning message"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = container.querySelector('fieldset');
expect(fieldset).not.toHaveClass(
`${prefix}--radio-button-group--warning`
);
expect(screen.queryByText('Warning message')).not.toBeInTheDocument();
});

it('should not apply invalid class or show invalid text when readOnly and invalid', () => {
const { container } = render(
<RadioButtonGroup
readOnly={true}
invalid={true}
invalidText="Invalid selection"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = container.querySelector('fieldset');
expect(fieldset).not.toHaveClass(
`${prefix}--radio-button-group--invalid`
);
expect(screen.queryByText('Invalid selection')).not.toBeInTheDocument();
});

it('should not apply warning class or show warning text when readOnly and warn', () => {
const { container } = render(
<RadioButtonGroup
readOnly={true}
warn={true}
warnText="Warning message"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = container.querySelector('fieldset');
expect(fieldset).not.toHaveClass(
`${prefix}--radio-button-group--warning`
);
expect(screen.queryByText('Warning message')).not.toBeInTheDocument();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ const RadioButtonGroup = React.forwardRef(
}
}

const showWarning = !readOnly && !invalid && warn;
const showWarning = !readOnly && !disabled && !invalid && warn;
const showHelper = !invalid && !disabled && !warn;

const wrapperClasses = classNames(`${prefix}--form-item`, className);
Expand All @@ -228,7 +228,8 @@ const RadioButtonGroup = React.forwardRef(
orientation === 'vertical',
[`${prefix}--radio-button-group--label-${labelPosition}`]: labelPosition,
[`${prefix}--radio-button-group--readonly`]: readOnly,
[`${prefix}--radio-button-group--invalid`]: !readOnly && invalid,
[`${prefix}--radio-button-group--invalid`]:
!readOnly && !disabled && invalid,
[`${prefix}--radio-button-group--warning`]: showWarning,
[`${prefix}--radio-button-group--slug`]: slug,
[`${prefix}--radio-button-group--decorator`]: decorator,
Expand Down Expand Up @@ -283,7 +284,7 @@ const RadioButtonGroup = React.forwardRef(
{getRadioButtons()}
</fieldset>
<div className={`${prefix}--radio-button__validation-msg`}>
{!readOnly && invalid && (
{!readOnly && !disabled && invalid && (
<>
<WarningFilled
className={`${prefix}--radio-button__invalid-icon`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,121 @@ describe('cds-radio-button-group', () => {
expect(rb1?.checked).to.be.false;
expect(rb2?.checked).to.be.true;
});
describe('Invalid and Warning States', () => {
it('should show invalid message when invalid prop is true', async () => {
const el = await fixture(html`
<cds-radio-button-group invalid invalid-text="Invalid selection">
<cds-radio-button value="test-1"></cds-radio-button>
<cds-radio-button value="test-2"></cds-radio-button>
</cds-radio-button-group>
`);

const invalidMessage = el.shadowRoot?.querySelector(
'.cds--form-requirement'
);
expect(invalidMessage).to.exist;
expect(invalidMessage?.textContent).to.contain('Invalid selection');

const fieldset = el.shadowRoot?.querySelector('fieldset');
expect(fieldset?.classList.contains('cds--radio-button-group--invalid'))
.to.be.true;
});

it('should show warning message when warn prop is true', async () => {
const el = await fixture(html`
<cds-radio-button-group warn warn-text="Warning message">
<cds-radio-button value="test-1"></cds-radio-button>
<cds-radio-button value="test-2"></cds-radio-button>
</cds-radio-button-group>
`);

const warnMessage = el.shadowRoot?.querySelector(
'.cds--form-requirement'
);
expect(warnMessage).to.exist;
expect(warnMessage?.textContent).to.contain('Warning message');

const fieldset = el.shadowRoot?.querySelector('fieldset');
expect(fieldset?.classList.contains('cds--radio-button-group--warning'))
.to.be.true;
});

it('should not show invalid message or class when disabled and invalid', async () => {
const el = await fixture(html`
<cds-radio-button-group
disabled
invalid
invalid-text="Invalid selection">
<cds-radio-button value="test-1"></cds-radio-button>
<cds-radio-button value="test-2"></cds-radio-button>
</cds-radio-button-group>
`);

const invalidMessage = el.shadowRoot?.querySelector(
'.cds--form-requirement'
);
expect(invalidMessage).to.not.exist;

const fieldset = el.shadowRoot?.querySelector('fieldset');
expect(fieldset?.classList.contains('cds--radio-button-group--invalid'))
.to.be.false;
});

it('should not show warning message or class when disabled and warn', async () => {
const el = await fixture(html`
<cds-radio-button-group disabled warn warn-text="Warning message">
<cds-radio-button value="test-1"></cds-radio-button>
<cds-radio-button value="test-2"></cds-radio-button>
</cds-radio-button-group>
`);

const warnMessage = el.shadowRoot?.querySelector(
'.cds--form-requirement'
);
expect(warnMessage).to.not.exist;

const fieldset = el.shadowRoot?.querySelector('fieldset');
expect(fieldset?.classList.contains('cds--radio-button-group--warning'))
.to.be.false;
});

it('should not show invalid message or class when readonly and invalid', async () => {
const el = await fixture(html`
<cds-radio-button-group
readonly
invalid
invalid-text="Invalid selection">
<cds-radio-button value="test-1"></cds-radio-button>
<cds-radio-button value="test-2"></cds-radio-button>
</cds-radio-button-group>
`);

const invalidMessage = el.shadowRoot?.querySelector(
'.cds--form-requirement'
);
expect(invalidMessage).to.not.exist;

const fieldset = el.shadowRoot?.querySelector('fieldset');
expect(fieldset?.classList.contains('cds--radio-button-group--invalid'))
.to.be.false;
});

it('should not show warning message or class when readonly and warn', async () => {
const el = await fixture(html`
<cds-radio-button-group readonly warn warn-text="Warning message">
<cds-radio-button value="test-1"></cds-radio-button>
<cds-radio-button value="test-2"></cds-radio-button>
</cds-radio-button-group>
`);

const warnMessage = el.shadowRoot?.querySelector(
'.cds--form-requirement'
);
expect(warnMessage).to.not.exist;

const fieldset = el.shadowRoot?.querySelector('fieldset');
expect(fieldset?.classList.contains('cds--radio-button-group--warning'))
.to.be.false;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class CDSRadioButtonGroup extends FormMixin(HostListenerMixin(LitElement)) {
_handleSlotChange: handleSlotChange,
} = this;

const showWarning = !readOnly && !invalid && warn;
const showWarning = !readOnly && !disabled && !invalid && warn;
const showHelper = !invalid && !disabled && !warn;

const invalidIcon = iconLoader(WarningFilled16, {
Expand All @@ -262,6 +262,9 @@ class CDSRadioButtonGroup extends FormMixin(HostListenerMixin(LitElement)) {
[`${prefix}--radio-button-group--readonly`]: readOnly,
[`${prefix}--radio-button-group--${orientation}`]:
orientation === 'vertical',
[`${prefix}--radio-button-group--invalid`]:
!readOnly && !disabled && invalid,
[`${prefix}--radio-button-group--warning`]: showWarning,
[`${prefix}--radio-button-group--slug`]: hasAILabel,
});

Expand All @@ -279,7 +282,7 @@ class CDSRadioButtonGroup extends FormMixin(HostListenerMixin(LitElement)) {
<slot></slot>
</fieldset>
<div class="${prefix}--radio-button__validation-msg">
${!readOnly && invalid
${!readOnly && !disabled && invalid
? html`
${invalidIcon}
<div class="${prefix}--form-requirement">${invalidText}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ $css--plex: true !default;
@extend .#{$prefix}--radio-button-group--vertical;
}

:host(#{$prefix}-radio-button-group[invalid]),
:host(#{$prefix}-radio-button-group[warn]) {
:host(#{$prefix}-radio-button-group[invalid]:not([disabled])),
:host(#{$prefix}-radio-button-group[warn]:not([disabled])) {
.#{$prefix}--radio-button__validation-msg {
display: flex;

Expand All @@ -45,7 +45,8 @@ $css--plex: true !default;
}
}

:host(#{$prefix}-radio-button-group[invalid]) .#{$prefix}--form-requirement {
:host(#{$prefix}-radio-button-group[invalid]:not([disabled]))
.#{$prefix}--form-requirement {
color: $text-error;
}

Expand All @@ -68,7 +69,11 @@ $css--plex: true !default;
margin-inline-end: 0;
}

:host(#{$prefix}-radio-button[invalid]:not([readOnly]))
:host(
#{$prefix}-radio-button[invalid]:not([readOnly]):not([disabled]):not(
[disabledItem]
)
)
.#{$prefix}--radio-button__appearance {
border-color: $support-error !important; /* stylelint-disable-line declaration-no-important */
}
Expand Down
Loading