[LON-CAPA-cvs] cvs: loncom /interface loncommon.pm londocs.pm

raeburn raeburn at source.lon-capa.org
Tue Jun 9 01:57:42 EDT 2026


raeburn		Tue Jun  9 05:57:42 2026 EDT

  Modified files:              
    /loncom/interface	londocs.pm loncommon.pm 
  Log:
  - WCAG 2 compliance - accessibility for keyboard-only interaction.
    - Course Editor links to add new content can be reached using Tab key.
    - Order of Course Editor menu items (New Folder, Upload, External, Import 
      Assessment, Collaboration, and Other) when reached by tabbing matches
      visual order.
    - Sub-menu items in Upload, External etc. reachable using Tab key.
    - Course Editor menu item with current focus is outlined when reached 
      using Tab key.
  
  
Index: loncom/interface/londocs.pm
diff -u loncom/interface/londocs.pm:1.741 loncom/interface/londocs.pm:1.742
--- loncom/interface/londocs.pm:1.741	Tue Jun  9 03:59:32 2026
+++ loncom/interface/londocs.pm	Tue Jun  9 05:57:42 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Documents
 #
-# $Id: londocs.pm,v 1.741 2026/06/09 03:59:32 raeburn Exp $
+# $Id: londocs.pm,v 1.742 2026/06/09 05:57:42 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -8856,8 +8856,9 @@
     }
     my $backicon = $iconpath.'clickhere.gif';
     my $backtext = &mt('Exit Editor');
-    $form = '<div class="LC_Box" style="margin:0;">'.
-            '<ul id="navigation'.$tid.'" class="LC_TabContent">'."\n".
+    $form = '<div class="LC_DocsOuter">'.
+            '<div class="LC_DocsInner">'.
+            '<ul class="LC_TabContent LC_floatleft">'."\n".
             '<li class="goback">'.
             '<a href="javascript:toContents('."'$jumpto'".');">'.
             '<img src="'.$backicon.'" class="LC_icon" style="border: none; vertical-align: top;"'.
@@ -8876,22 +8877,34 @@
         $form .= '<li><a href="javascript:toggleHistoryDisp(0);">'.
                  &mt('Edit').'</a></li>'."\n";
     }
-    foreach my $name (reverse(sort(keys(%orderhash)))) {
-        if($name ne '00'){
-            if($activetab eq '' || $activetab ne $name){
-               $active = '';
-            }elsif($activetab eq $name){
-               $active = 'class="active"';
-            }
-            $form .= '<li style="float:right" '.$active
-                .' onclick="javascript:showPage(this, \''.$name.$tid.'\', \'navigation'.$tid.'\',\'content'.$tid.'\');"><a href="javascript:;"><b>'.&mt(${$orderhash{$name}}[0]).'</b></a></li>'."\n";
-        } else {
-	    $form .= '<li style="float:right">'.${$orderhash{$name}}[1].'</li>'."\n";
-
-	}
+    $form .= '</ul>';
+    if (keys(%orderhash)) {
+        my @ordered = sort(keys(%orderhash));
+        if ($ordered[-1] eq '00') {
+            unshift(@ordered, pop(@ordered));
+        }
+        $form .= '<ul id="navigation'.$tid.'" class="LC_TabContent LC_floatright">';
+        foreach my $name (@ordered) {
+            if ($name ne '00') {
+                if ($activetab eq '' || $activetab ne $name) {
+                    $active = '';
+                } elsif($activetab eq $name){
+                    $active = 'class="active"';
+                }
+                $form .= '<li style="float:left" '.$active
+                        .' onclick="javascript:showPage(this, \''.$name.$tid.'\', \'navigation'.$tid.'\',\'content'.$tid.'\');"><a href="javascript:;"><b>'.&mt(${$orderhash{$name}}[0]).'</b></a></li>'."\n";
+            } else {
+	        $form .= '<li style="float:left">'.${$orderhash{$name}}[1].'</li>'."\n";
+	    }
+        }
+        $form .= '</ul>'."\n";
+    } else {
+        $form .= '<ul class="LC_TabContent LC_floatright">'."\n".
+                 '<li class="LC_empty"> </li>'."\n".
+                 '</ul>'."\n";
     }
-    $form .= '</ul>'."\n";
-    $form .= '<div id="content'.$tid.'" style="padding: 0 0; margin: 0 0; overflow: hidden; clear:right">'."\n";
+    $form .= '</div>';
+    $form .= '<div id="content'.$tid.'" style="padding: 0 10px; margin: 0 0; overflow: hidden; clear:both">'."\n";
 
     if ($to_show ne '') {
         my $saveform;
@@ -10096,25 +10109,62 @@
 	hideAll(current, nav, data);
 	openTabs(pageId);
 	unselectInactive(nav);
+        currentData = document.getElementById(pageId);
         if ((currstate == 'active') || (currstate == 'right active')) {
             if (currstate == 'active') {
 	        current.className = '';
             } else {
                 current.className = 'right';
             }
-            activeTab = ''; 
+            activeTab = '';
             toggleExternal();
             toggleUpload();
             toggleMap();
             toggleCrsRes();
             toggleImportCrsres();
+            currentData.removeAttribute('tabindex');
             resize_scrollbox('contentscroll','1','0');
             return;
         } else {
             current.className = 'active';
         }
-	currentData = document.getElementById(pageId);
 	currentData.style.display = 'block';
+        currentData.style.outline = 'none';
+        currentData.setAttribute('tabindex',0);
+        currentData.focus();
+        const rightTabFocusTrap = (e) => {
+    if (e.key === 'Escape') {
+        e.preventDefault();
+        current.querySelector(':scope > a').focus();
+        showPage(current, pageId, nav, data);
+        return;
+    }
+    if (e.key !== 'Tab') return;
+    const focusableElements = \$(currentData).find(':focusable');
+    const focusableNum = focusableElements.length;
+    if (focusableNum === 0) {
+        e.preventDefault();
+        current.querySelector(':scope > a').focus();
+        showPage(current, pageId, nav, data);
+        return;
+    }
+    if (e.shiftKey) {
+        if ((document.activeElement === currentData) ||
+            (document.activeElement === focusableElements[0])) {
+            e.preventDefault();
+            current.querySelector(':scope > a').focus();
+            showPage(current, pageId, nav, data);
+        }
+    } else {
+        if (document.activeElement === focusableElements[focusableNum-1]) {
+            e.preventDefault();
+            current.querySelector(':scope > a').focus();
+            showPage(current, pageId, nav, data);
+        }
+    }
+    return;
+        };
+        currentData.addEventListener('keydown', rightTabFocusTrap);
         activeTab = pageId;
         toggleExternal();
         toggleUpload();
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1527 loncom/interface/loncommon.pm:1.1528
--- loncom/interface/loncommon.pm:1.1527	Tue Jun  9 03:03:00 2026
+++ loncom/interface/loncommon.pm	Tue Jun  9 05:57:42 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1527 2026/06/09 03:03:00 raeburn Exp $
+# $Id: loncommon.pm,v 1.1528 2026/06/09 05:57:42 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -9343,11 +9343,11 @@
 }
 
 ul.LC_TabContent {
-  display:block;
+  display: inline-block;
   background: $sidebg;
   border-bottom: solid 1px $lg_border_color;
   list-style:none;
-  margin: -1px -10px 0 -10px;
+  margin: 0;
   padding: 0;
 }
 
@@ -9390,7 +9390,6 @@
 ul.LC_TabContent li a:focus {
   color: $button_hover;
   background:none;
-  outline:none;
 }
 
 ul.LC_TabContent li:hover {
@@ -9408,7 +9407,7 @@
 ul.LC_TabContent li.active a {
   color:$font;
   background:#FFFFFF;
-  outline: none;
+  outline: reset;
 }
 
 ul.LC_TabContent li.goback {
@@ -9416,6 +9415,12 @@
   border-left: none;
 }
 
+ul.LC_TabContent li.LC_empty {
+  margin-bottom: 1px;
+  border: 0;
+  background-color: $sidebg;
+}
+
 #maincoursedoc {
   clear:both;
 }
@@ -9446,7 +9451,7 @@
   text-align: center;
   display: block;
   text-decoration: none;
-  outline: none;  
+  outline: none;
 }
 
 ul.LC_TabContentBigger li.active a {
@@ -9525,6 +9530,19 @@
   padding: 0 0 10px 10px;
 }
 
+.LC_DocsOuter {
+  border: solid 1px $lg_border_color;
+  padding: 0 0 10px 0;
+  margin: 0;
+}
+
+.LC_DocsInner {
+  background-color: $sidebg;
+  display: inline-block;
+  margin: -1px 0 0 0;
+  width: 100%;
+}
+
 .LC_MainMenu_Box {
   border: solid 1px $lg_border_color;
   padding: 0 10px 0 10px;
@@ -10941,7 +10959,7 @@
 		modal += "<div class=\"LCmodal-overlay\"></div>";
 		modal += "<div id=\"" + this.windowId + "\" class=\"LCmodal-window\" style=\"width:" + this.width + "px; height:" + this.height + "px; margin-top:-" + (this.height / 2) + "px; margin-left:-" + (this.width / 2) + "px;\">";
 		modal += this.content;
-		modal += "</div>";	
+		modal += "</div>";
 
 		$(this.parent).append(modal);
 




More information about the LON-CAPA-cvs mailing list