diff --git a/packages/webawesome/docs/docs/components/radio-group.md b/packages/webawesome/docs/docs/components/radio-group.md
index 9fa82ee04..b628a393b 100644
--- a/packages/webawesome/docs/docs/components/radio-group.md
+++ b/packages/webawesome/docs/docs/components/radio-group.md
@@ -44,7 +44,7 @@ Set the `appearance` attribute to `button` on all radios to render a radio butto
Option 3
-
+
```
-### Disabling Options
+### Disabling
-Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
+To disable the entire radio group, add the `disabled` attribute to the radio group.
```html {.example}
-
+
+ Option 1
+ Option 2
+ Option 3
+
+```
+
+To disable individual options, add the `disabled` attribute to the respective options.
+
+```html {.example}
+
Option 1
Option 2
Option 3
diff --git a/packages/webawesome/src/components/radio-group/radio-group.ts b/packages/webawesome/src/components/radio-group/radio-group.ts
index b99ccb53f..0a2cba15f 100644
--- a/packages/webawesome/src/components/radio-group/radio-group.ts
+++ b/packages/webawesome/src/components/radio-group/radio-group.ts
@@ -1,10 +1,10 @@
+import type { PropertyValues } from 'lit';
import { html, isServer } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { uniqueId } from '../../internal/math.js';
import { HasSlotController } from '../../internal/slot.js';
import { RequiredValidator } from '../../internal/validators/required-validator.js';
-import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
import formControlStyles from '../../styles/component/form-control.css';
import sizeStyles from '../../styles/utilities/size.css';
@@ -73,6 +73,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
/** The name of the radio group, submitted as a name/value pair with form data. */
@property({ reflect: true }) name: string | null = null;
+ /** Disables the radio group and all child radios. */
+ @property({ type: Boolean, reflect: true }) disabled = false;
+
/** The orientation in which to show radio items. */
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'vertical';
@@ -141,6 +144,12 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
return radio;
}
+ updated(changedProperties: PropertyValues) {
+ if (changedProperties.has('disabled') || changedProperties.has('value')) {
+ this.syncRadioElements();
+ }
+ }
+
formResetCallback(...args: Parameters) {
this.value = this.defaultValue;
@@ -152,7 +161,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
private handleRadioClick = (e: Event) => {
const clickedRadio = (e.target as HTMLElement).closest('wa-radio');
- if (!clickedRadio || clickedRadio.disabled) {
+ if (!clickedRadio || clickedRadio.disabled || (clickedRadio as any).forceDisabled || this.disabled) {
return;
}
@@ -199,6 +208,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
radio.toggleAttribute('data-wa-radio-first', index === 0);
radio.toggleAttribute('data-wa-radio-inner', index !== 0 && index !== radios.length - 1);
radio.toggleAttribute('data-wa-radio-last', index === radios.length - 1);
+
+ // Set forceDisabled state based on radio group's disabled state
+ (radio as WaRadio).forceDisabled = this.disabled;
});
// If at least one radio button exists, we assume it's a radio button group
@@ -216,18 +228,42 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}),
);
- if (radios.length > 0 && !radios.some(radio => radio.checked)) {
- radios[0].setAttribute('tabindex', '0');
+ // Manage tabIndex based on disabled state and checked status
+ if (this.disabled) {
+ // If radio group is disabled, all radios should not be tabbable
+ radios.forEach(radio => {
+ radio.tabIndex = -1;
+ });
+ } else {
+ // Normal tabbing behavior
+ const enabledRadios = radios.filter(radio => !radio.disabled);
+ const checkedRadio = enabledRadios.find(radio => radio.checked);
+
+ if (enabledRadios.length > 0) {
+ if (checkedRadio) {
+ // If there's a checked radio, it should be tabbable
+ enabledRadios.forEach(radio => {
+ radio.tabIndex = radio.checked ? 0 : -1;
+ });
+ } else {
+ // If no radio is checked, first enabled radio should be tabbable
+ enabledRadios.forEach((radio, index) => {
+ radio.tabIndex = index === 0 ? 0 : -1;
+ });
+ }
+ }
+
+ // Disabled radios should never be tabbable
+ radios
+ .filter(radio => radio.disabled)
+ .forEach(radio => {
+ radio.tabIndex = -1;
+ });
}
}
- @watch('value')
- handleValueChange() {
- this.syncRadioElements();
- }
-
private handleKeyDown(event: KeyboardEvent) {
- if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(event.key)) {
+ if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(event.key) || this.disabled) {
return;
}
@@ -287,6 +323,8 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
/** Sets focus on the radio group. */
public focus(options?: FocusOptions) {
+ if (this.disabled) return;
+
const radios = this.getAllRadios();
const checked = radios.find(radio => radio.checked);
const firstEnabledRadio = radios.find(radio => !radio.disabled);
diff --git a/packages/webawesome/src/components/radio/radio.css b/packages/webawesome/src/components/radio/radio.css
index 827c18195..12ab57deb 100644
--- a/packages/webawesome/src/components/radio/radio.css
+++ b/packages/webawesome/src/components/radio/radio.css
@@ -89,7 +89,7 @@
}
/* Disabled */
-:host([disabled]) {
+:host(:state(disabled)) {
opacity: 0.5;
cursor: not-allowed;
}
@@ -142,7 +142,7 @@
}
@media (hover: hover) {
- :host([appearance='button']:hover:not([disabled], :state(checked))) {
+ :host([appearance='button']:hover:not(:state(disabled), :state(checked))) {
background-color: color-mix(in srgb, var(--wa-color-surface-default) 95%, var(--wa-color-mix-hover));
}
}
diff --git a/packages/webawesome/src/components/radio/radio.ts b/packages/webawesome/src/components/radio/radio.ts
index cdec1799a..a6dca2ea5 100644
--- a/packages/webawesome/src/components/radio/radio.ts
+++ b/packages/webawesome/src/components/radio/radio.ts
@@ -44,6 +44,9 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
@state() checked = false;
+ /** @internal Used by radio group to force disable radios while preserving their original disabled state. */
+ @state() forceDisabled = false;
+
/**
* The string pointing to a form's id.
*/
@@ -79,7 +82,7 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
private setInitialAttributes() {
this.setAttribute('role', 'radio');
this.tabIndex = 0;
- this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
+ this.setAttribute('aria-disabled', this.disabled || this.forceDisabled ? 'true' : 'false');
}
updated(changedProperties: PropertyValues) {
@@ -88,12 +91,24 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
if (changedProperties.has('checked')) {
this.customStates.set('checked', this.checked);
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
- this.tabIndex = this.checked ? 0 : -1;
+ // Only set tabIndex if not disabled
+ if (!this.disabled && !this.forceDisabled) {
+ this.tabIndex = this.checked ? 0 : -1;
+ }
}
- if (changedProperties.has('disabled')) {
- this.customStates.set('disabled', this.disabled);
- this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
+ if (changedProperties.has('disabled') || changedProperties.has('forceDisabled')) {
+ const effectivelyDisabled = this.disabled || this.forceDisabled;
+ this.customStates.set('disabled', effectivelyDisabled);
+ this.setAttribute('aria-disabled', effectivelyDisabled ? 'true' : 'false');
+
+ // Set tabIndex based on disabled state
+ if (effectivelyDisabled) {
+ this.tabIndex = -1;
+ } else {
+ // Restore proper tabIndex - this will be managed by the radio group
+ this.tabIndex = this.checked ? 0 : -1;
+ }
}
}
@@ -104,8 +119,9 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
// We override `setValue` because we don't want to set form values from here. We want to do that in "RadioGroup" itself.
}
+ // Update the handleClick method (around line 75)
private handleClick = () => {
- if (!this.disabled) {
+ if (!this.disabled && !this.forceDisabled) {
this.checked = true;
}
};