diff --git a/django/contrib/admin/static/admin/css/nav_sidebar.css b/django/contrib/admin/static/admin/css/nav_sidebar.css index 6fcfb26131..7d4c75fe12 100644 --- a/django/contrib/admin/static/admin/css/nav_sidebar.css +++ b/django/contrib/admin/static/admin/css/nav_sidebar.css @@ -17,6 +17,7 @@ cursor: pointer; font-size: 20px; color: #447e9b; + padding: 0; } [dir="rtl"] .toggle-nav-sidebar { diff --git a/django/contrib/admin/static/admin/js/nav_sidebar.js b/django/contrib/admin/static/admin/js/nav_sidebar.js index 70685f2c06..efaa7214b8 100644 --- a/django/contrib/admin/static/admin/js/nav_sidebar.js +++ b/django/contrib/admin/static/admin/js/nav_sidebar.js @@ -2,18 +2,35 @@ { const toggleNavSidebar = document.getElementById('toggle-nav-sidebar'); if (toggleNavSidebar !== null) { + const navLinks = document.querySelectorAll('#nav-sidebar a'); + function disableNavLinkTabbing() { + for (const navLink of navLinks) { + navLink.tabIndex = -1; + } + } + function enableNavLinkTabbing() { + for (const navLink of navLinks) { + navLink.tabIndex = 0; + } + } + const main = document.getElementById('main'); let navSidebarIsOpen = localStorage.getItem('django.admin.navSidebarIsOpen'); if (navSidebarIsOpen === null) { navSidebarIsOpen = 'true'; } + if (navSidebarIsOpen === 'false') { + disableNavLinkTabbing(); + } main.classList.toggle('shifted', navSidebarIsOpen === 'true'); toggleNavSidebar.addEventListener('click', function() { if (navSidebarIsOpen === 'true') { navSidebarIsOpen = 'false'; + disableNavLinkTabbing(); } else { navSidebarIsOpen = 'true'; + enableNavLinkTabbing(); } localStorage.setItem('django.admin.navSidebarIsOpen', navSidebarIsOpen); main.classList.toggle('shifted'); diff --git a/django/contrib/admin/templates/admin/nav_sidebar.html b/django/contrib/admin/templates/admin/nav_sidebar.html index 84956c1bd1..9fde4c28e1 100644 --- a/django/contrib/admin/templates/admin/nav_sidebar.html +++ b/django/contrib/admin/templates/admin/nav_sidebar.html @@ -1,4 +1,5 @@ -
+{% load i18n %} + diff --git a/tests/admin_views/test_nav_sidebar.py b/tests/admin_views/test_nav_sidebar.py index 9d52c541c0..01cd4cfee7 100644 --- a/tests/admin_views/test_nav_sidebar.py +++ b/tests/admin_views/test_nav_sidebar.py @@ -97,7 +97,14 @@ class SeleniumTests(AdminSeleniumTestCase): def test_sidebar_can_be_closed(self): self.selenium.get(self.live_server_url + reverse('test_with_sidebar:auth_user_changelist')) toggle_button = self.selenium.find_element_by_css_selector('#toggle-nav-sidebar') + self.assertEqual(toggle_button.tag_name, 'button') + self.assertEqual(toggle_button.get_attribute('aria-label'), 'Toggle navigation') + for link in self.selenium.find_elements_by_css_selector('#nav-sidebar a'): + self.assertEqual(link.get_attribute('tabIndex'), '0') toggle_button.click() + # Hidden sidebar is not reachable via keyboard navigation. + for link in self.selenium.find_elements_by_css_selector('#nav-sidebar a'): + self.assertEqual(link.get_attribute('tabIndex'), '-1') main_element = self.selenium.find_element_by_css_selector('#main') self.assertNotIn('shifted', main_element.get_attribute('class').split()) @@ -115,7 +122,12 @@ class SeleniumTests(AdminSeleniumTestCase): self.assertNotIn('shifted', main_element.get_attribute('class').split()) toggle_button = self.selenium.find_element_by_css_selector('#toggle-nav-sidebar') + # Hidden sidebar is not reachable via keyboard navigation. + for link in self.selenium.find_elements_by_css_selector('#nav-sidebar a'): + self.assertEqual(link.get_attribute('tabIndex'), '-1') toggle_button.click() + for link in self.selenium.find_elements_by_css_selector('#nav-sidebar a'): + self.assertEqual(link.get_attribute('tabIndex'), '0') self.assertEqual( self.selenium.execute_script("return localStorage.getItem('django.admin.navSidebarIsOpen')"), 'true',