) {
// Update the radio element if it exists
if (this.radioElement) {
this.updateRadioElement();
}
}
private updateRadioElement() {
if (!this.radioElement) return;
// Update radio properties
this.radioElement.name = this.name;
this.radioElement.value = this.value;
this.radioElement.checked = this.checked;
this.radioElement.disabled = this.disabled;
this.radioElement.required = this.required;
// Update classes
// Remove existing USWDS classes
const classesToRemove = Array.from(this.radioElement.classList).filter((className) =>
className.startsWith('usa-radio__input')
);
classesToRemove.forEach((className) => this.radioElement?.classList.remove(className));
// Always add base usa-radio__input class
this.radioElement.classList.add('usa-radio__input');
// Add tile class if needed
if (this.tile) {
this.radioElement.classList.add('usa-radio__input--tile');
}
// Add error class if error exists
if (this.error) {
this.radioElement.classList.add('usa-input--error');
}
// Update ARIA attributes
if (this.error) {
this.radioElement.setAttribute('aria-invalid', 'true');
} else {
this.radioElement.removeAttribute('aria-invalid');
}
}
private handleChange = (e: Event) => {
const radio = e.target as HTMLInputElement;
this.checked = radio.checked;
// Dispatch both change and input events for consistency with other form elements
this.dispatchEvent(
new CustomEvent('change', {
detail: {
checked: this.checked,
value: this.value,
name: this.name,
},
bubbles: true,
composed: true,
})
);
this.dispatchEvent(
new CustomEvent('input', {
detail: {
checked: this.checked,
value: this.value,
name: this.name,
},
bubbles: true,
composed: true,
})
);
};
private get radioId() {
// Always check for element id first, then use cached generated id
if (this.id) {
return this.id;
}
if (!this._radioId) {
// Generate exactly 9 random characters to ensure consistent ID length
// Math.random().toString(36) can produce varying lengths, so we pad if needed
let randomPart = Math.random().toString(36).substring(2, 11);
while (randomPart.length < 9) {
randomPart += Math.random().toString(36).substring(2, 3);
}
this._radioId = `radio-${randomPart.substring(0, 9)}`;
}
return this._radioId;
}
private async initializeUSWDSRadio() {
// Prevent multiple initializations
if (this.usingUSWDSEnhancement) {
return;
}
try {
// Check if global USWDS is available for potential future enhancements
if (typeof window !== 'undefined' && typeof (window as any).USWDS !== 'undefined') {
const USWDS = (window as any).USWDS;
if (USWDS.radio && typeof USWDS.radio.on === 'function') {
USWDS.radio.on(this);
return;
}
}
// Radio is a presentational component (USWDS Radio is CSS-only)
} catch {
// Initialization completed with basic behavior
}
}
override disconnectedCallback() {
// Clean up event listeners
if (this.radioElement) {
this.radioElement.removeEventListener('change', this.handleChange);
}
super.disconnectedCallback();
this.cleanupUSWDS();
}
/**
* Clean up USWDS module on component destruction
*/
private cleanupUSWDS() {
// Try cleanup with global USWDS (radio components are presentational)
if (typeof window !== 'undefined' && typeof (window as any).USWDS !== 'undefined') {
const USWDS = (window as any).USWDS;
if (USWDS.radio?.off) {
try {
USWDS.radio.off(this);
} catch {
// Cleanup failed silently
}
}
}
}
private renderError(radioId: string) {
if (!this.error) return '';
return html`
Error: ${this.error}
`;
}
private renderLabelDescription(radioId: string) {
if (!this.description) return '';
return html`
${this.description}
`;
}
override render() {
const radioId = this.radioId;
const wrapperClasses = ['usa-radio', this.tile ? 'usa-radio--tile' : '']
.filter(Boolean)
.join(' ');
const describedByIds: string[] = [];
if (this.description) {
describedByIds.push(`${radioId}-description`);
}
if (this.error) {
describedByIds.push(`${radioId}-error`);
}
return html`
${this.renderError(radioId)}
`;
}
}