Skip to content

Commit 4b5c7ff

Browse files
committed
feat(ui): refine translation controls and menu behavior
- Reposition 'Translate' section to header controls - Remove redundant top-bar elements - improve 'Chapters' dropdown menu visibility and styling - Update disclaimer text - Ensure menus are translated immediately - Fix translation bugs - Docs: Move changelog to docs directory
1 parent d56ca88 commit 4b5c7ff

6 files changed

Lines changed: 223 additions & 368 deletions

File tree

File renamed without changes.

CompendiumUI/index.html

Lines changed: 48 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,7 @@
3030
<header class="header">
3131
<div class="usa-overlay"></div>
3232
<header class="usa-header usa-header--basic">
33-
<!-- Translation Info Link (moved to top bar) -->
34-
<div id="translation-info-link-wrapper" class="translation-info-wrapper translation-top-bar">
35-
<button type="button" id="translation-info-link" class="translation-info-link"
36-
aria-label="Translation information" aria-expanded="false">
37-
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
38-
<use xlink:href="/assets/img/sprite.svg#language"></use>
39-
</svg>
40-
<span>Translation not available</span>
41-
</button>
42-
<!-- Tooltip/Popover -->
43-
<div id="translation-info-tooltip" class="translation-info-tooltip" role="tooltip" style="display: none;">
44-
<h3 class="translation-info-title">Translation Feature</h3>
45-
<p>This application supports in-browser translation using the experimental Translation API.</p>
46-
<p><strong>Requirements:</strong></p>
47-
<ul>
48-
<li>Chrome 120+ or Edge 120+</li>
49-
<li>Translation API enabled in browser settings</li>
50-
</ul>
51-
<p><strong>Alternative:</strong> You can use your browser's built-in translation feature (right-click
52-
→ "Translate to...").</p>
53-
</div>
54-
</div>
33+
5534
<div class="usa-nav-container">
5635
<div class="usa-navbar">
5736
<div class="usa-logo">
@@ -65,11 +44,52 @@ <h3 class="translation-info-title">Translation Feature</h3>
6544

6645
<!-- Chapters Dropdown (moved from mobile menu) -->
6746
<div class="navbar-controls">
68-
<button type="button" class="usa-accordion__button usa-nav__link navbar-chapters-btn" aria-expanded="false"
69-
aria-controls="basic-nav-section-one">
47+
<!-- Translation Controls (Moved here from mobile menu) -->
48+
<div id="translation-controls-wrapper" style="display: none;">
49+
<div class="translation-controls">
50+
<!-- Label removed per plan, icon moved to button -->
51+
<div class="translation-controls-row">
52+
<select id="language-select" class="usa-select translation-select"
53+
aria-label="Select translation language">
54+
<option value="">English (Original)</option>
55+
<option value="es">Español (Spanish)</option>
56+
<option value="zh">中文 (Chinese)</option>
57+
<option value="fr">Français (French)</option>
58+
<option value="de">Deutsch (German)</option>
59+
<option value="ja">日本語 (Japanese)</option>
60+
<option value="ko">한국어 (Korean)</option>
61+
<option value="ru">Русский (Russian)</option>
62+
<option value="ar">العربية (Arabic)</option>
63+
<option value="pt">Português (Portuguese)</option>
64+
<option value="it">Italiano (Italian)</option>
65+
<option value="hi">हिन्दी (Hindi)</option>
66+
<option value="vi">Tiếng Việt (Vietnamese)</option>
67+
</select>
68+
<button type="button" id="translate-button"
69+
class="usa-button usa-button--outline translate-button hidden"
70+
aria-label="Translate current chapter">
71+
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img"
72+
style="margin-right: 0.25rem;">
73+
<use xlink:href="/assets/img/sprite.svg#language"></use>
74+
</svg>
75+
Translate
76+
</button>
77+
</div>
78+
<!-- Clear button might need better placement or styling in header context, keeping it inside for now -->
79+
<button type="button" id="clear-translations-button"
80+
class="usa-button usa-button--unstyled clear-translations-button"
81+
aria-label="Clear all cached translations" style="display: none;">
82+
<!-- Hidden by default, script shows it -->
83+
Clear cached translations
84+
</button>
85+
</div>
86+
</div>
87+
<button type="button" class="usa-accordion__button usa-nav__link navbar-chapters-btn"
88+
aria-expanded="false" aria-controls="basic-nav-section-one">
7089
<span>Chapters</span>
7190
</button>
72-
<ul id="basic-nav-section-one" class="usa-nav__submenu navbar-chapters-submenu" hidden aria-label="Available chapters">
91+
<ul id="basic-nav-section-one" class="usa-nav__submenu navbar-chapters-submenu" hidden
92+
aria-label="Available chapters">
7393
<!-- Chapter list will be populated here -->
7494
</ul>
7595

@@ -106,47 +126,7 @@ <h3 class="version-tooltip__title">About the Copyright Compendium Viewer</h3>
106126
<div id="autocomplete-search"></div>
107127
</section>
108128
</li>
109-
<!-- Translation Controls -->
110-
<li class="usa-nav__primary-item" id="translation-controls-list-item" style="display: none;">
111-
<section aria-label="Translation controls" id="translation-controls-wrapper">
112-
<div class="translation-controls">
113-
<label for="language-select" class="usa-label translation-label">
114-
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
115-
<use xlink:href="/assets/img/sprite.svg#language"></use>
116-
</svg>
117-
Translate
118-
</label>
119-
<div class="translation-controls-row">
120-
<select id="language-select" class="usa-select translation-select"
121-
aria-label="Select translation language">
122-
<option value="">English (Original)</option>
123-
<option value="es">Español (Spanish)</option>
124-
<option value="zh">中文 (Chinese)</option>
125-
<option value="fr">Français (French)</option>
126-
<option value="de">Deutsch (German)</option>
127-
<option value="ja">日本語 (Japanese)</option>
128-
<option value="ko">한국어 (Korean)</option>
129-
<option value="ru">Русский (Russian)</option>
130-
<option value="ar">العربية (Arabic)</option>
131-
<option value="pt">Português (Portuguese)</option>
132-
<option value="it">Italiano (Italian)</option>
133-
<option value="hi">हिन्दी (Hindi)</option>
134-
<option value="vi">Tiếng Việt (Vietnamese)</option>
135-
</select>
136-
<button type="button" id="translate-button"
137-
class="usa-button usa-button--outline translate-button hidden"
138-
aria-label="Translate current chapter">
139-
Translate
140-
</button>
141-
</div>
142-
<button type="button" id="clear-translations-button"
143-
class="usa-button usa-button--unstyled clear-translations-button"
144-
aria-label="Clear all cached translations">
145-
Clear cached translations
146-
</button>
147-
</div>
148-
</section>
149-
</li>
129+
150130
</nav>
151131
</div>
152132
</header>
@@ -157,8 +137,8 @@ <h3 class="version-tooltip__title">About the Copyright Compendium Viewer</h3>
157137
style="display: none;">
158138
<div class="usa-alert__body">
159139
<p class="usa-alert__text">
160-
<strong>⚠️ Experimental Translation:</strong> This is an automatically translated version and is not
161-
official.
140+
<strong>⚠️ Experimental Translation:</strong> Any language other than English represents automatically
141+
translated text and is not official.
162142
The translation may contain inaccuracies or errors. For authoritative information, please refer to the
163143
<a href="#" id="view-original-link" class="usa-link">original English version</a>.
164144
</p>

CompendiumUI/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@
3232
"commitlint": {
3333
"extends": [
3434
"@commitlint/config-conventional"
35-
]
35+
],
36+
"rules": {
37+
"body-max-line-length": [0, "always", 10000]
38+
}
3639
},
3740
"standard-version": {
41+
"infile": "docs/CHANGELOG.md",
3842
"scripts": {
39-
"postbump": "git add package.json package-lock.json"
43+
"postbump": "git add package.json package-lock.json docs/CHANGELOG.md"
4044
}
4145
}
4246
}

CompendiumUI/script.ts

Lines changed: 56 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,7 @@ document.addEventListener('DOMContentLoaded', () => {
500500
const languageSelect = document.getElementById('language-select');
501501
const translationDisclaimer = document.getElementById('translation-disclaimer');
502502
const translationControlsWrapper = document.getElementById('translation-controls-wrapper');
503-
const translationInfoLinkWrapper = document.getElementById('translation-info-link-wrapper');
504-
const translationInfoLink = document.getElementById('translation-info-link');
505-
const translationInfoTooltip = document.getElementById('translation-info-tooltip');
503+
// Removed: translationInfoLinkWrapper, translationInfoLink, translationInfoTooltip
506504
const viewOriginalLink = document.getElementById('view-original-link');
507505
const translateButton = document.getElementById('translate-button');
508506
const clearTranslationsButton = document.getElementById('clear-translations-button');
@@ -1778,10 +1776,7 @@ document.addEventListener('DOMContentLoaded', () => {
17781776
translationControlsWrapper.style.display = 'none';
17791777
}
17801778

1781-
// Show translation info link
1782-
if (translationInfoLinkWrapper) {
1783-
translationInfoLinkWrapper.style.display = 'flex';
1784-
}
1779+
17851780
} else {
17861781
// Show translation controls in menu
17871782
console.log('Translation supported. Showing controls.');
@@ -1796,14 +1791,13 @@ document.addEventListener('DOMContentLoaded', () => {
17961791
translationControlsWrapper.style.display = 'block';
17971792
}
17981793

1799-
// Hide translation info link
1800-
if (translationInfoLinkWrapper) {
1801-
translationInfoLinkWrapper.style.display = 'none';
1794+
if (translationControlsWrapper) {
1795+
translationControlsWrapper.style.display = 'block';
18021796
}
18031797
}
18041798
}
18051799

1806-
// Handle language selection change - now just updates the dropdown, doesn't auto-translate
1800+
// Handle language selection change - now just updates the dropdown and resets if empty
18071801
if (languageSelect) {
18081802
languageSelect.addEventListener('change', async (event) => {
18091803
const selectedLanguage = (event.target as HTMLSelectElement).value;
@@ -1824,9 +1818,43 @@ document.addEventListener('DOMContentLoaded', () => {
18241818
}
18251819
// Restore original content if saved
18261820
if (originalContent && chapterContent) {
1827-
// Reload the current page to get original content
1821+
// Reload the current page to get original content - this resets main content
1822+
// and triggers side nav regeneration in English
18281823
const currentFile = currentFilename || 'introduction.html';
18291824
loadContent(currentFile, { updateHistory: false, forceReload: true });
1825+
1826+
// Re-populate chapters dropdown to restore English
1827+
if (chapterListDropdown) {
1828+
chapterListDropdown.innerHTML = ''; // Clear current
1829+
chapters.forEach((chapter) => {
1830+
if (!chapter.filename || !chapter.title) return;
1831+
const listItem = document.createElement('li');
1832+
listItem.classList.add('usa-nav__submenu-item');
1833+
const link = document.createElement('a');
1834+
// Reconstruct logic from initialization
1835+
link.href = `/${chapter.filename}`;
1836+
link.textContent = `${chapter.number}${chapter.number ? ': ' : ''}${chapter.title}`;
1837+
link.dataset.filename = chapter.filename;
1838+
link.addEventListener('click', (e) => {
1839+
e.preventDefault();
1840+
const filename = link.dataset.filename;
1841+
if (filename !== currentFilename) {
1842+
if (filename) loadContent(filename, { updateHistory: true });
1843+
} else {
1844+
chapterContent?.scrollTo({ top: 0, behavior: 'smooth' });
1845+
if (filename !== 'glossary.html') updateSideNavCurrent(null);
1846+
history.replaceState({ filename: filename, hash: null }, document.title, `/${filename}`);
1847+
}
1848+
if (uswdsNav && uswdsNav.classList.contains('is-visible')) {
1849+
uswdsOverlay?.classList.remove('is-visible');
1850+
uswdsNav.classList.remove('is-visible');
1851+
if (uswdsMenuButton) uswdsMenuButton.setAttribute('aria-expanded', 'false');
1852+
}
1853+
});
1854+
listItem.appendChild(link);
1855+
chapterListDropdown.appendChild(listItem);
1856+
});
1857+
}
18301858
}
18311859
} else {
18321860
// A language was selected - show and enable the translate button
@@ -1872,6 +1900,22 @@ document.addEventListener('DOMContentLoaded', () => {
18721900
console.warn('Translation failed or was cancelled');
18731901
}
18741902
}
1903+
1904+
// Translate Chapters Menu
1905+
if (chapterListDropdown) {
1906+
// We use specific filename for caching menu translation to avoid collisions or re-translation
1907+
const menuCacheKey = 'chapters_menu_dropdown';
1908+
await translationService.translateContent(chapterListDropdown as HTMLElement, selectedLanguage, menuCacheKey);
1909+
}
1910+
1911+
// Translate Sidenav
1912+
if (sectionListContainer) {
1913+
// Sidenav is dynamic based on content, but we can translate the current state
1914+
// Cache key based on current filename + 'sidenav'
1915+
const currentFile = currentFilename || 'introduction.html';
1916+
const sidenavCacheKey = `${currentFile}_sidenav`;
1917+
await translationService.translateContent(sectionListContainer as HTMLElement, selectedLanguage, sidenavCacheKey);
1918+
}
18751919
});
18761920
}
18771921

@@ -1906,70 +1950,13 @@ document.addEventListener('DOMContentLoaded', () => {
19061950
});
19071951
}
19081952

1909-
// Handle translation info link tooltip
1910-
if (translationInfoLink && translationInfoTooltip) {
1911-
// Media query for responsive behavior
1912-
const mobileBreakpoint = window.matchMedia('(max-width: 40em)');
1913-
1914-
// Toggle tooltip on click
1915-
translationInfoLink.addEventListener('click', (event) => {
1916-
event.stopPropagation();
1917-
const isVisible = translationInfoTooltip.style.display !== 'none';
1918-
const newVisibility = isVisible ? 'none' : 'block';
1919-
translationInfoTooltip.style.display = newVisibility;
1920-
// Update ARIA attribute for accessibility
1921-
translationInfoLink.setAttribute('aria-expanded', newVisibility === 'block' ? 'true' : 'false');
1922-
});
1923-
1924-
// Show tooltip on hover (desktop only) - use media query
1925-
const handleMouseEnter = () => {
1926-
if (!mobileBreakpoint.matches) {
1927-
translationInfoTooltip.style.display = 'block';
1928-
translationInfoLink.setAttribute('aria-expanded', 'true');
1929-
}
1930-
};
1931-
1932-
const handleMouseLeave = () => {
1933-
if (!mobileBreakpoint.matches) {
1934-
translationInfoTooltip.style.display = 'none';
1935-
translationInfoLink.setAttribute('aria-expanded', 'false');
1936-
}
1937-
};
1938-
1939-
translationInfoLink.addEventListener('mouseenter', handleMouseEnter);
1940-
translationInfoLink.addEventListener('mouseleave', handleMouseLeave);
1941-
1942-
// Close tooltip when clicking outside
1943-
document.addEventListener('click', (event) => {
1944-
if (translationInfoTooltip.style.display === 'block' &&
1945-
!translationInfoLink.contains(event.target as Node) &&
1946-
!translationInfoTooltip.contains(event.target as Node)) {
1947-
translationInfoTooltip.style.display = 'none';
1948-
translationInfoLink.setAttribute('aria-expanded', 'false');
1949-
}
1950-
});
1951-
1952-
// Accessibility: Close tooltip with Escape key
1953-
document.addEventListener('keydown', (event) => {
1954-
if (event.key === 'Escape' && translationInfoTooltip.style.display === 'block') {
1955-
translationInfoTooltip.style.display = 'none';
1956-
translationInfoLink.setAttribute('aria-expanded', 'false');
1957-
// Return focus to the button that opened the tooltip
1958-
translationInfoLink.focus();
1959-
}
1960-
});
1961-
}
1962-
19631953
// Initialize translation on page load - set default visibility until check completes
19641954
const translationListItem = document.getElementById('translation-controls-list-item');
19651955
if (translationListItem) {
19661956
translationListItem.style.display = 'none';
19671957
} else if (translationControlsWrapper) {
19681958
translationControlsWrapper.style.display = 'none';
19691959
}
1970-
if (translationInfoLinkWrapper) {
1971-
translationInfoLinkWrapper.style.display = 'flex';
1972-
}
19731960

19741961
// Then run the async check to update based on actual browser support
19751962
initializeTranslation();

0 commit comments

Comments
 (0)