Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues with keyboard navigation on KDropdownMenu #818

Conversation

Sahil-Sinha-11
Copy link
Contributor

…p down position

Description

Issue addressed

Addresses #588

After Video

Screen.Recording.2024-11-08.at.12.16.34.AM.mov

Changelog

Steps to test

  1. Step 1
  2. Step 2
  3. ...

(optional) Implementation notes

At a high level, how did you implement this?

Does this introduce any tech-debt items?

Testing checklist

  • Contributor has fully tested the PR manually
  • If there are any front-end changes, before/after screenshots are included
  • Critical and brittle code paths are covered by unit tests
  • The change is described in the changelog section above

Reviewer guidance

  • Is the code clean and well-commented?
  • Are there tests for this change?
  • Are all UI components LTR and RTL compliant (if applicable)?
  • Add other things to check for here

Comments


//function to check if the sibling is divider
const isDivider = element => {
return element && element.classList.contains('is-divider');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good way to determine if the element is a divider, but is a little specific. If anything changes and in the future we get rid of this specific class. This would break this condition.

A more general solution could be to ask if the element has a tabIndex >= 0, so this means it is focusable and with this, we can skip the dividers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, that seems more logical to skip any other element [like divider] in a more generalized way. Thanks @AlexVelezLl .


//Chekcing if next sibling is divider and skipping it
let sibling = focusedElement.nextElementSibling;
while (sibling && isDivider(sibling)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could write two separate methods here, to breakdown a little bit this method. so we can just do something like

const sibling = this.getNextFocusableSibling(focusedElement);
const prevSibling = this.getPrevFocusableSibling(focusedElement);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @AlexVelezLl , I've defined prevSibling Already here
Screenshot 2024-11-11 at 12 21 48 AM
and the variable sibling Is for next focused element.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, my suggestion was more to avoid writing more logic to query the next/previouss element in the same handleOpenMenuNavigation method. So lets define two new methods in this component getNextFocusableSibling and getPrevFocusableSibling where we should do the focusable check and the while sentence.

e.g.

methods: {
  ...
  getNextFocusableSibling(element) {
    let sibling = focusedElement.nextElementSibling;
    while (sibling && !isFocusable(sibling)) {
      sibling = sibling.nextElementSibling;
    }
    return sibling;
  }
}

and lets refactor the definition of these variables prevSibling and sibling to use the result of these methods.

@Sahil-Sinha-11
Copy link
Contributor Author

Sahil-Sinha-11 commented Nov 10, 2024

Hey @Sahil-Sinha-11, I made the suggested changes. 🤗
PS: sorry for the late reply, actually my university exams are next week so quite loaded with tests. 😅

@Sahil-Sinha-11
Copy link
Contributor Author

Hey @AlexVelezLl , the focus works like this, should I return it to the last focused button?

Screen.Recording.2024-11-11.at.1.27.50.AM.mov

@AlexVelezLl
Copy link
Member

Hey @Sahil-Sinha-11. Yes!! It should be working like that, when we close the dropdown, the last focused element is the element that should be focused. Thank you!! Loking forward to see this change pushed in the PR 💯.

PS: sorry for the late reply, actually my university exams are next week so quite loaded with tests. 😅

No worriees! 😅 There is no rush, please take your time 🤗.

@Sahil-Sinha-11
Copy link
Contributor Author

Hey @AlexVelezLl, I made the changes, please review it, thanks.

Copy link
Member

@AlexVelezLl AlexVelezLl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woo 🎉. This is working soo smothly @Sahil-Sinha-11. Thank you!! I have left a couple of comments just for some code clean up, thanks!

@@ -32,6 +32,7 @@
<script>

import { computed } from '@vue/composition-api';
// import { get } from 'lodash';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can get rid of this line

@@ -147,6 +149,7 @@
handleOpen() {
this.$nextTick(() => this.$nextTick(() => this.setFocus()));
window.addEventListener('keydown', this.handleOpenMenuNavigation, true);
this.lastFocusElement = document.activeElement;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although there is no problem with having this line as the last line of the handleOpen method, we can move it to be the first line of the method, so the intention of storing the last focused element before we focus anything else is clearer :).

@@ -162,8 +165,31 @@
) {
this.focusOnButton();
}

if (this.lastFocusElement) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above this, you can see there is a call to the this.focusOnButton() method, which apparently was an old behaviour we had in KDropdownMenu, but its no longer useful as now KDropdownMenu doesnt render any button anymore.

But if in the past, we were making this call in those conditions, it was for some reason (perhaps because of an edge case im not aware of), but to play safe, lets just replace that focusOnButton() call with this lastFocusElement call, for this focus to occur on those conditions too, so we would have something like:

        if (
          popover.contains(focusedElement) &&
          (focusedElement.classList.contains('ui-popover') ||
            focusedElement.classList.contains('ui-popover-focus-redirector') ||
            focusedElement.classList.contains('ui-menu-option'))
        ) {
          if (this.lastFocusElement) {
            this.lastFocusElement.focus();
          }
        }

And then we can remove the focusOnButton method :).

@@ -175,19 +201,27 @@
const popoverIsOpen = popover.clientWidth > 0 && popover.clientHeight > 0;
// set current element and its siblings
let focusedElement = document.activeElement;
let sibling = focusedElement.nextElementSibling;
let prevSibling = focusedElement.previousElementSibling;
// let sibling = focusedElement.nextElementSibling;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! We dont need to make these calls always. We can remove these lines too.


// manage rotating through the options using arrow keys
// UP arrow: .keyCode is depricated and should used only as a fallback
if ((event.key == 'ArrowUp' || event.keyCode == 38) && popoverIsOpen) {
event.preventDefault();

// Checking if previous sibling is divider and if yes then skip it
let prevSibling = this.getPreviousFocusableSibling(focusedElement);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets define these prevSibling and sibling as const, as we wont need to reassign them anymore.

@Sahil-Sinha-11
Copy link
Contributor Author

Hey @AlexVelezLl, I made the suggested changes, please review them, thanks.

@AlexVelezLl
Copy link
Member

Thank youu @Sahil-Sinha-11!! Code changes look good to me! :) . I have just opened a PR in Kolibri with your changes for QA to test this in Kolibri and confirming that this is the expected behaviour, after that we can merge your PR. Thanks @Sahil-Sinha-11! Good job 🤗

Copy link
Member

@AlexVelezLl AlexVelezLl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Sahil-Sinha-11!

@AlexVelezLl AlexVelezLl merged commit ece696d into learningequality:develop Nov 19, 2024
8 checks passed
learning-equality-bot bot pushed a commit that referenced this pull request Nov 19, 2024
@MisRob
Copy link
Member

MisRob commented Nov 20, 2024

Thanks @Sahil-Sinha-11 and @AlexVelezLl, good chunk of work here :)!

@AlexVelezLl AlexVelezLl linked an issue Jan 7, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Issues with keyboard navigation on KDropdownMenu
3 participants