Skip to content
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Bug Fixes

- `RadioControl`: Add support for disabled radio group [#77127](https://github.com/WordPress/gutenberg/pull/77127).
- `Textarea`: Fix disabled styles [#77129](https://github.com/WordPress/gutenberg/pull/77129).
- `Autocomplete`: Fix value comparison to avoid resetting block inserter in RTC ([#76980](https://github.com/WordPress/gutenberg/pull/76980)).
- `ValidatedRangeControl`: Fix `aria-label` rendered as `[object Object]` when `required` or `markWhenOptional` is set ([#77042](https://github.com/WordPress/gutenberg/pull/77042)).
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/radio-control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export function RadioControl(
onChange,
onClick,
hideLabelFromVision,
disabled,
options = [],
id: preferredId,
...additionalProps
Expand All @@ -89,6 +90,7 @@ export function RadioControl(
id={ id }
role="radiogroup"
className={ clsx( className, 'components-radio-control' ) }
disabled={ disabled }
aria-describedby={ !! help ? generateHelpId( id ) : undefined }
Comment on lines 88 to 94
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR introduces a new disabled prop/state (fieldset disabled + .is-disabled styling), but the existing RadioControl test suite doesn’t cover disabled behavior. Please add tests to verify radios are disabled (and onChange isn’t called) when disabled is true, including mouse click and keyboard interaction paths.

Copilot uses AI. Check for mistakes.
>
{ hideLabelFromVision ? (
Expand Down
23 changes: 21 additions & 2 deletions packages/components/src/radio-control/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

font-family: $default-font;
font-size: $default-font-size;

}

.components-radio-control__group-wrapper.has-help {
Expand All @@ -34,7 +35,10 @@
margin: 0;
padding: 0;
appearance: none;
cursor: pointer;

&:not(:disabled) {
cursor: pointer;
}

&:focus {
@include button-style-outset__focus(var(--wp-admin-theme-color));
Expand All @@ -49,13 +53,28 @@
border-radius: $radius-round;
}
}

&:disabled {
background: $components-color-gray-100;
border: 1px solid $components-color-gray-300;
opacity: 1; // Override style from wp-admin forms.css.

&:checked::before {
border-color: $components-color-gray-300;
opacity: 1; // Override style from wp-admin forms.css.

}
}
}

.components-radio-control__label {
grid-column: 2;
grid-row: 1;

cursor: pointer;
.components-radio-control:not(:disabled) & {
cursor: pointer;
}

line-height: $radio-input-size-sm;

@include break-small() {
Expand Down
10 changes: 10 additions & 0 deletions packages/components/src/radio-control/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ describe.each( [
).toBeVisible();
} );

it( 'should disable the radio group when `disabled` is true', () => {
render(
<Component { ...defaultProps } disabled onChange={ () => {} } />
);

expect(
screen.getByRole( 'radiogroup', { name: defaultProps.label } )
).toBeDisabled();
} );

it( 'should describe the radio group with the help text', () => {
const onChangeSpy = jest.fn();
render(
Expand Down
6 changes: 6 additions & 0 deletions packages/components/src/radio-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ export type RadioControlProps = Pick<
BaseControlProps,
'label' | 'help' | 'hideLabelFromVision'
> & {
/**
* Whether the radio group should be disabled.
*
* @default false
*/
disabled?: boolean;
/**
* A function that receives the value of the new option that is being
* selected as input.
Expand Down
Loading