Keep closed workspace dropdown out of SR navigation
This commit is contained in:
1 parent
06e0df4656
commit
67f8414656
3 files changed
+85
-6
No files matched your search
+1
-1
@@ -751,7 +751,7 @@
|
||||
<button class="ctx-compress-btn composer-mobile-context-compress" id="composerMobileCtxCompressBtn" type="button" style="display:none"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ws-dropdown ws-dropdown-footer" id="composerWsDropdown"></div>
|
||||
<div class="ws-dropdown ws-dropdown-footer" id="composerWsDropdown" hidden aria-hidden="true" inert></div>
|
||||
<div class="composer-reasoning-dropdown" id="composerReasoningDropdown">
|
||||
<div class="reasoning-option" data-effort="none">None</div>
|
||||
<div class="reasoning-option" data-effort="minimal">Minimal</div>
|
||||
|
||||
+50
-5
@@ -5648,7 +5648,7 @@ function syncWorkspaceDisplays(){
|
||||
const mobileAction=$('composerMobileWorkspaceAction');
|
||||
const mobileLabel=$('composerMobileWorkspaceLabel');
|
||||
const composerDropdown=$('composerWsDropdown');
|
||||
if(!hasWorkspace && composerDropdown) composerDropdown.classList.remove('open');
|
||||
if(!hasWorkspace && composerDropdown) _setWorkspaceDropdownOpenState(composerDropdown,false);
|
||||
// Only show workspace label once boot has finished to prevent
|
||||
// flash of "No workspace" before the saved session finishes loading.
|
||||
if(composerLabel) composerLabel.textContent=S._bootReady?label:'';
|
||||
@@ -5680,6 +5680,41 @@ async function loadWorkspaceList(){
|
||||
}catch(e){ return {workspaces:[], last:''}; }
|
||||
}
|
||||
|
||||
function _setWorkspaceDropdownOpenState(dd,open){
|
||||
if(!dd)return;
|
||||
dd.classList.toggle('open',!!open);
|
||||
dd.hidden=!open;
|
||||
dd.setAttribute('aria-hidden',open?'false':'true');
|
||||
if(open){
|
||||
try{dd.inert=false;}catch(_){}
|
||||
dd.removeAttribute('inert');
|
||||
}else{
|
||||
try{dd.inert=true;}catch(_){}
|
||||
dd.setAttribute('inert','');
|
||||
}
|
||||
}
|
||||
|
||||
function _getComposerWorkspaceFocusTarget(){
|
||||
const panel=(typeof $==='function')?$('composerMobileConfigPanel'):null;
|
||||
const mobileAction=(typeof $==='function')?$('composerMobileWorkspaceAction'):null;
|
||||
if(panel&&panel.classList.contains('open')&&mobileAction&&!mobileAction.disabled) return mobileAction;
|
||||
return (typeof $==='function')?$('composerWorkspaceChip'):null;
|
||||
}
|
||||
|
||||
function _focusComposerWorkspaceTarget(target){
|
||||
if(target&&!target.disabled&&typeof target.focus==='function'){
|
||||
try{target.focus({preventScroll:true});}
|
||||
catch(_){target.focus();}
|
||||
}
|
||||
}
|
||||
|
||||
function _shouldRestoreComposerWorkspaceFocus(dd){
|
||||
if(typeof document==='undefined') return true;
|
||||
const active=document.activeElement;
|
||||
if(!active||active===document.body) return true;
|
||||
return !!(dd&&dd.contains(active));
|
||||
}
|
||||
|
||||
function _renderWorkspaceAction(label, meta, iconSvg, onClick){
|
||||
const opt=document.createElement('div');
|
||||
opt.className='ws-opt ws-opt-action';
|
||||
@@ -5833,7 +5868,7 @@ function toggleWsDropdown(){
|
||||
closeProfileDropdown(); // close profile dropdown if open
|
||||
loadWorkspaceList().then(data=>{
|
||||
renderWorkspaceDropdownInto(dd, data.workspaces, S.session?.workspace||S._profileDefaultWorkspace||data.last||'');
|
||||
dd.classList.add('open');
|
||||
_setWorkspaceDropdownOpenState(dd,true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5853,7 +5888,7 @@ function toggleComposerWsDropdown(){
|
||||
if(typeof closeReasoningDropdown==='function') closeReasoningDropdown();
|
||||
loadWorkspaceList().then(data=>{
|
||||
renderWorkspaceDropdownInto(dd, data.workspaces, S.session?.workspace||S._profileDefaultWorkspace||data.last||'');
|
||||
dd.classList.add('open');
|
||||
_setWorkspaceDropdownOpenState(dd,true);
|
||||
_positionComposerWsDropdown();
|
||||
if(chip){
|
||||
chip.classList.add('active');
|
||||
@@ -5872,8 +5907,8 @@ function closeWsDropdown(){
|
||||
const composerDd=$('composerWsDropdown');
|
||||
const composerChip=$('composerWorkspaceChip');
|
||||
const mobileAction=$('composerMobileWorkspaceAction');
|
||||
if(dd)dd.classList.remove('open');
|
||||
if(composerDd)composerDd.classList.remove('open');
|
||||
if(dd)_setWorkspaceDropdownOpenState(dd,false);
|
||||
if(composerDd)_setWorkspaceDropdownOpenState(composerDd,false);
|
||||
if(composerChip){
|
||||
composerChip.classList.remove('active');
|
||||
composerChip.setAttribute('aria-expanded','false');
|
||||
@@ -6325,6 +6360,10 @@ async function switchToWorkspace(path,name){
|
||||
if(typeof cancelEditMode==='function')cancelEditMode();
|
||||
if(typeof clearPreview==='function')clearPreview();
|
||||
}
|
||||
const composerDd=(typeof $==='function')?$('composerWsDropdown'):null;
|
||||
const restoreComposerFocusTarget=(composerDd&&composerDd.classList.contains('open')&&typeof _getComposerWorkspaceFocusTarget==='function')
|
||||
? _getComposerWorkspaceFocusTarget()
|
||||
: null;
|
||||
try{
|
||||
closeWsDropdown();
|
||||
await api('/api/session/update',{method:'POST',body:JSON.stringify({
|
||||
@@ -6336,6 +6375,12 @@ async function switchToWorkspace(path,name){
|
||||
S._profileSwitchWorkspace=null;
|
||||
S._pendingSessionToolsets=null;
|
||||
syncTopbar();
|
||||
if(
|
||||
restoreComposerFocusTarget&&
|
||||
typeof _shouldRestoreComposerWorkspaceFocus==='function'&&
|
||||
_shouldRestoreComposerWorkspaceFocus(composerDd)&&
|
||||
typeof _focusComposerWorkspaceTarget==='function'
|
||||
) _focusComposerWorkspaceTarget(restoreComposerFocusTarget);
|
||||
await loadDir('.');
|
||||
if (_currentPanel === 'memory') await loadMemory(true);
|
||||
showToast(t('workspace_switched_to',name||getWorkspaceFriendlyName(path)));
|
||||
|
||||
@@ -47,6 +47,21 @@ def test_composer_workspace_dropdown_keeps_aria_expanded_in_sync():
|
||||
assert "mobileAction.setAttribute('aria-expanded','false')" in close
|
||||
|
||||
|
||||
def test_closed_composer_workspace_dropdown_is_removed_from_screen_reader_navigation():
|
||||
assert 'id="composerWsDropdown" hidden aria-hidden="true" inert' in INDEX_HTML
|
||||
|
||||
helper = _block(PANELS_JS, "function _setWorkspaceDropdownOpenState", "function _getComposerWorkspaceFocusTarget")
|
||||
assert "dd.hidden=!open" in helper
|
||||
assert "dd.setAttribute('aria-hidden',open?'false':'true')" in helper
|
||||
assert "dd.removeAttribute('inert')" in helper
|
||||
assert "dd.setAttribute('inert','')" in helper
|
||||
|
||||
toggle = _block(PANELS_JS, "function toggleComposerWsDropdown", "function closeWsDropdown")
|
||||
close = _block(PANELS_JS, "function closeWsDropdown", "document.addEventListener('click'")
|
||||
assert "_setWorkspaceDropdownOpenState(dd,true)" in toggle
|
||||
assert "_setWorkspaceDropdownOpenState(composerDd,false)" in close
|
||||
|
||||
|
||||
def test_composer_workspace_cue_is_transient_not_a_persistent_editor_description():
|
||||
assert 'id="msg"' in INDEX_HTML
|
||||
assert 'aria-describedby="composerWorkspaceContext"' not in INDEX_HTML
|
||||
@@ -59,6 +74,25 @@ def test_composer_workspace_cue_is_transient_not_a_persistent_editor_description
|
||||
assert "workspace_context_none" not in sync
|
||||
|
||||
|
||||
def test_workspace_selection_from_composer_dropdown_restores_focus_to_opening_trigger_once():
|
||||
focus_helpers = _block(PANELS_JS, "function _getComposerWorkspaceFocusTarget", "function _renderWorkspaceAction")
|
||||
switch = _block(PANELS_JS, "async function switchToWorkspace", "// ── Profile panel")
|
||||
|
||||
assert "const mobileAction=(typeof $==='function')?$('composerMobileWorkspaceAction'):null" in focus_helpers
|
||||
assert "if(panel&&panel.classList.contains('open')&&mobileAction&&!mobileAction.disabled) return mobileAction" in focus_helpers
|
||||
assert "return (typeof $==='function')?$('composerWorkspaceChip'):null" in focus_helpers
|
||||
assert "function _shouldRestoreComposerWorkspaceFocus(dd)" in focus_helpers
|
||||
assert "return !!(dd&&dd.contains(active))" in focus_helpers
|
||||
|
||||
assert "const restoreComposerFocusTarget=(composerDd&&composerDd.classList.contains('open')&&typeof _getComposerWorkspaceFocusTarget==='function')" in switch
|
||||
assert "_shouldRestoreComposerWorkspaceFocus(composerDd)" in switch
|
||||
assert "_focusComposerWorkspaceTarget(restoreComposerFocusTarget)" in switch
|
||||
assert switch.count("_focusComposerWorkspaceTarget(restoreComposerFocusTarget)") == 1
|
||||
assert switch.index("syncTopbar();") < switch.index("_focusComposerWorkspaceTarget(restoreComposerFocusTarget)") < switch.index("await loadDir('.')")
|
||||
assert "workspace_switched_to" in switch
|
||||
assert switch.index("_focusComposerWorkspaceTarget(restoreComposerFocusTarget)") < switch.index("workspace_switched_to")
|
||||
|
||||
|
||||
def test_new_chat_has_screen_reader_only_workspace_announcer():
|
||||
assert 'id="a11yAnnouncer"' in INDEX_HTML
|
||||
assert 'class="sr-only"' in INDEX_HTML
|
||||
|
||||
Reference in new issue
Block a user