) {
// ARCHITECTURE: Script Tag Pattern
// USWDS is loaded globally via script tag in .storybook/preview-head.html
// Components just render HTML - USWDS enhances automatically via window.USWDS
// ARCHITECTURE: USWDS-Mirrored Behavior Pattern
// Uses dedicated behavior file (usa-language-selector-behavior.ts) that replicates USWDS source exactly
super.firstUpdated(changedProperties);
// Wait for DOM to be fully rendered
await this.updateComplete;
await new Promise((resolve) => requestAnimationFrame(() => resolve(undefined)));
// Initialize using mirrored USWDS behavior
// Pass document instead of 'this' to enable click-outside-to-close functionality
this.cleanup = initializeLanguageSelector(document);
}
private handleLanguageSelect(language: LanguageOption, e?: Event) {
console.log('🌐 Language Selector: Language selected using USWDS pattern:', language.code);
if (e) {
e.preventDefault();
}
const previousCode = this.currentLanguage;
this.currentLanguage = language.code;
// Dispatch language-select event
this.dispatchEvent(
new CustomEvent('language-select', {
detail: {
language,
code: language.code,
previousCode,
},
bubbles: true,
composed: true,
})
);
// Close dropdown if open
if (this._isOpen) {
this._isOpen = false;
this.requestUpdate();
}
// If href is provided, navigate to it
if (language.href && !e?.defaultPrevented) {
window.location.href = language.href;
}
}
private toggleDropdown(e: Event) {
e.preventDefault();
this._isOpen = !this._isOpen;
this.dispatchEvent(
new CustomEvent('menu-toggle', {
detail: {
isOpen: this._isOpen,
},
bubbles: true,
composed: true,
})
);
// Update aria-expanded on the button
const button = e.currentTarget as HTMLButtonElement;
button.setAttribute('aria-expanded', String(this._isOpen));
}
private renderTwoLanguages() {
// For two languages, show a simple toggle button
const otherLanguage =
this.languages.find((lang) => lang.code !== this.currentLanguage) || this.languages[0];
// Handle empty language array
if (!otherLanguage) {
return html``;
}
return html`
`;
}
private renderDropdown() {
const containerClasses = [
'usa-language-container',
'usa-language',
this.small ? 'usa-language--small' : '',
]
.filter(Boolean)
.join(' ');
return html`
`;
}
private renderUnstyled() {
// Unstyled list of language links
return html`
${this.languages.map((language) => this.renderUnstyledLanguageItem(language))}
`;
}
private renderDropdownLanguageItem(language: LanguageOption) {
return html`
`;
}
private renderLanguageNameWithNative(language: LanguageOption) {
return html`${language.nativeName} ${language.name}`;
}
private renderUnstyledLanguageItem(language: LanguageOption) {
return html`
this.handleLanguageSelect(language, e)}"
class="${language.code === this.currentLanguage ? 'usa-current' : ''}"
>
${language.nativeName}
`;
}
// Use light DOM for USWDS compatibility
protected override createRenderRoot(): HTMLElement {
return this as any;
}
override render() {
// Automatically choose variant based on number of languages if not specified
let effectiveVariant = this.variant;
if (this.variant === 'two-languages' && this.languages.length > 2) {
effectiveVariant = 'dropdown';
}
switch (effectiveVariant) {
case 'two-languages':
return this.renderTwoLanguages();
case 'dropdown':
return this.renderDropdown();
case 'unstyled':
return this.renderUnstyled();
default:
return this.renderTwoLanguages();
}
}
// Public API methods
setCurrentLanguage(code: string) {
const language = this.languages.find((lang) => lang.code === code);
if (language) {
this.handleLanguageSelect(language);
} else {
// If language not found, still update currentLanguage property for tests
this.currentLanguage = code;
}
}
getCurrentLanguage(): LanguageOption | undefined {
return this.languages.find((lang) => lang.code === this.currentLanguage);
}
addLanguage(language: LanguageOption) {
if (!this.languages.find((lang) => lang.code === language.code)) {
this.languages = [...this.languages, language];
this.requestUpdate();
}
}
removeLanguage(code: string) {
this.languages = this.languages.filter((lang) => lang.code !== code);
if (this.currentLanguage === code && this.languages.length > 0) {
this.currentLanguage = this.languages[0].code;
}
this.requestUpdate();
}
openDropdown() {
if (this.variant === 'dropdown') {
this._isOpen = true;
this.requestUpdate();
}
}
closeDropdown() {
this._isOpen = false;
this.requestUpdate();
}
toggleDropdownState() {
this._isOpen = !this._isOpen;
this.requestUpdate();
}
}