Repository: wagtail/wagtail

Issue:
Page editing actions dropdown doesn’t support keyboard or screen reader navigation
### Issue Summary

Initially reported by @jessemenn. On the page editing UI, the actions dropdown is very brittle with keyboard and screen reader navigation:

- The dropdown’s toggle button isn’t focusable
- Items within the dropdown are focusable even when hidden.

### Steps to Reproduce

1. Go to the page editing UI on any Wagtail site
2. Reach the "Save draft" button
3. Try to reach any action within the dropdown

---

* I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: yes


### Technical details

Tested in Chrome macOS & Safari VoiceOver. I’d expect this to be an issue for all browsers, all screen readers, in all Wagtail releases over the last few years at least.

Page editing actions dropdown doesn’t support keyboard or screen reader navigation
### Issue Summary

Initially reported by @jessemenn. On the page editing UI, the actions dropdown is very brittle with keyboard and screen reader navigation:

- The dropdown’s toggle button isn’t focusable
- Items within the dropdown are focusable even when hidden.

### Steps to Reproduce

1. Go to the page editing UI on any Wagtail site
2. Reach the "Save draft" button
3. Try to reach any action within the dropdown

---

* I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: yes


### Technical details

Tested in Chrome macOS & Safari VoiceOver. I’d expect this to be an issue for all browsers, all screen readers, in all Wagtail releases over the last few years at least.



Discussion:
Based on reviewing available resources, it seems there are two documented ARIA patterns we could use for this:

- Progressive disclosure, similar to https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-navigation.html
- Or the menubar & menu pattern: https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-1/menubar-1.html

In both cases there are many more keyboard interactions to support than my initial issue description suggests.
As part of refactoring this we should also make sure to consistently use the same element (button or link) for all menu items, so screen reader announcements and high-contrast mode display is more consistent:

![7366](https://user-images.githubusercontent.com/877585/129975685-4b93eb30-903f-409a-8364-fa5c579b3367.png)

I can work on this issue
Some improvement has been made to this, thanks to @Desaiakshata  https://github.com/wagtail/wagtail/commit/997ac738b0bd04db0e737a11fa6304586865eab5

@Desaiakshata are you still on this? Otherwise I would like to give this a shot
I am currently working on this @tbrlpld 
@thibaudcolas to re-test
Hello @thibaudcolas  I'm seeing this issue is to be re-tested. 
I believe I can contribute to this issue and willing to take it on.
I want to work on this issue. kindly give me permission and access to work on this issue
Based on reviewing available resources, it seems there are two documented ARIA patterns we could use for this:

- Progressive disclosure, similar to https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-navigation.html
- Or the menubar & menu pattern: https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-1/menubar-1.html

In both cases there are many more keyboard interactions to support than my initial issue description suggests.
As part of refactoring this we should also make sure to consistently use the same element (button or link) for all menu items, so screen reader announcements and high-contrast mode display is more consistent:

![7366](https://user-images.githubusercontent.com/877585/129975685-4b93eb30-903f-409a-8364-fa5c579b3367.png)

I can work on this issue
Some improvement has been made to this, thanks to @Desaiakshata  https://github.com/wagtail/wagtail/commit/997ac738b0bd04db0e737a11fa6304586865eab5

@Desaiakshata are you still on this? Otherwise I would like to give this a shot
I am currently working on this @tbrlpld 
@thibaudcolas to re-test
Hello @thibaudcolas  I'm seeing this issue is to be re-tested. 
I believe I can contribute to this issue and willing to take it on.
I want to work on this issue. kindly give me permission and access to work on this issue

Resolving patch:
diff --git a/wagtail/admin/templatetags/wagtailadmin_tags.py b/wagtail/admin/templatetags/wagtailadmin_tags.py
--- a/wagtail/admin/templatetags/wagtailadmin_tags.py
+++ b/wagtail/admin/templatetags/wagtailadmin_tags.py
@@ -1114,6 +1114,13 @@ class DropdownNode(BlockInclusionNode):
 register.tag("dropdown", DropdownNode.handle)
 
 
+class DropdownButtonNode(BlockInclusionNode):
+    template = "wagtailadmin/shared/dropdown/dropdown_button.html"
+
+
+register.tag("dropdown_button", DropdownButtonNode.handle)
+
+
 class PanelNode(BlockInclusionNode):
     template = "wagtailadmin/shared/panel.html"
 
diff --git a/wagtail/admin/views/pages/revisions.py b/wagtail/admin/views/pages/revisions.py
--- a/wagtail/admin/views/pages/revisions.py
+++ b/wagtail/admin/views/pages/revisions.py
@@ -77,6 +77,7 @@ def revisions_revert(request, page_id, revision_id):
     action_menu = PageActionMenu(
         request,
         view="revisions_revert",
+        is_revision=True,
         page=page,
         lock=lock,
         locked_for_user=lock is not None and lock.for_user(request.user),


Test patch:
diff --git a/client/src/controllers/DropdownController.test.js b/client/src/controllers/DropdownController.test.js
--- a/client/src/controllers/DropdownController.test.js
+++ b/client/src/controllers/DropdownController.test.js
@@ -8,14 +8,14 @@ describe('DropdownController', () => {
 
   beforeEach(async () => {
     document.body.innerHTML = `
-  <section>
-    <div data-controller="w-dropdown" data-action="custom:show->w-dropdown#show custom:hide->w-dropdown#hide">
-      <button id="toggle" type="button" data-w-dropdown-target="toggle" aria-label="Actions"></button>
-      <div data-w-dropdown-target="content">
-        <a href="/">Option</a>
-      </div>
+<section>
+  <div data-controller="w-dropdown" data-w-dropdown-theme-value="dropdown" data-action="custom:show->w-dropdown#show custom:hide->w-dropdown#hide">
+    <button id="toggle" type="button" data-w-dropdown-target="toggle" aria-label="Actions"></button>
+    <div data-w-dropdown-target="content">
+      <a href="/">Option</a>
     </div>
-  </section>`;
+  </div>
+</section>`;
 
     application = Application.start();
     application.register('w-dropdown', DropdownController);
@@ -57,6 +57,11 @@ describe('DropdownController', () => {
     expect(expandedContent[0].innerHTML).toContain('<a href="/">Option</a>');
   });
 
+  it('supports providing a theme to Tippy.js', () => {
+    const toggle = document.querySelector('[data-w-dropdown-target="toggle"]');
+    expect(toggle._tippy.props.theme).toBe('dropdown');
+  });
+
   it('triggers custom event on activation', async () => {
     const toggle = document.querySelector('[data-w-dropdown-target="toggle"]');
     const dropdownElement = document.querySelector(
diff --git a/wagtail/admin/tests/pages/test_edit_page.py b/wagtail/admin/tests/pages/test_edit_page.py
--- a/wagtail/admin/tests/pages/test_edit_page.py
+++ b/wagtail/admin/tests/pages/test_edit_page.py
@@ -1890,28 +1890,14 @@ def hook_func(menu_items, request, context):
                 reverse("wagtailadmin_pages:edit", args=(self.single_event_page.id,))
             )
 
-        publish_button = """
-            <button type="submit" name="action-publish" value="action-publish" class="button button-longrunning " data-controller="w-progress" data-action="w-progress#activate" data-w-progress-active-value="Publishing…">
-                <svg class="icon icon-upload button-longrunning__icon" aria-hidden="true"><use href="#icon-upload"></use></svg>
-
-                <svg class="icon icon-spinner icon" aria-hidden="true"><use href="#icon-spinner"></use></svg><em data-w-progress-target="label">Publish</em>
-            </button>
-        """
-        save_button = """
-            <button type="submit" class="button action-save button-longrunning " data-controller="w-progress" data-action="w-progress#activate" data-w-progress-active-value="Saving…" >
-                <svg class="icon icon-draft button-longrunning__icon" aria-hidden="true"><use href="#icon-draft"></use></svg>
-
-                <svg class="icon icon-spinner icon" aria-hidden="true"><use href="#icon-spinner"></use></svg>
-                <em data-w-progress-target="label">Save draft</em>
-            </button>
-        """
-
-        # save button should be in a <li>
-        self.assertContains(response, "<li>%s</li>" % save_button, html=True)
+        soup = self.get_soup(response.content)
 
-        # publish button should be present, but not in a <li>
-        self.assertContains(response, publish_button, html=True)
-        self.assertNotContains(response, "<li>%s</li>" % publish_button, html=True)
+        # save button should be inside "More actions" toggle.
+        save_button = soup.select_one(".w-dropdown__content .action-save")
+        self.assertIsNotNone(save_button)
+        # publish button should be directly inside "Dropdown button".
+        publish_button = soup.select_one('.w-dropdown-button > [name="action-publish"]')
+        self.assertIsNotNone(publish_button)
 
     def test_override_publish_action_menu_item_label(self):
         def hook_func(menu_items, request, context):