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

Dock: enable proper keyboard navigation #2503

Open
Tracked by #2486
Kiarokh opened this issue Sep 26, 2023 · 0 comments
Open
Tracked by #2486

Dock: enable proper keyboard navigation #2503

Kiarokh opened this issue Sep 26, 2023 · 0 comments

Comments

@Kiarokh
Copy link
Contributor

Kiarokh commented Sep 26, 2023

New feature motivation

The new feature should improve accessibility and keyboard navigation.

New feature description

Note that limel-dock-buttons contain a button element inside them, which is already focusable, using the Tab key on the keyboard.

But we don't want the user to be using the Tab key to set focus on each individual item. This is the behavior I expect:

  1. Once the component is loaded, the button inside the limel-dock-button should receive focus, as soon as the user tabs into the component. This means the first button should have tabindex="0" and all other buttons in the dock should have tabindex="-1".
  2. Now if the user presses the tab key again, the focus will leave the limel-dock to the next focusable component in the DOM.
  3. However, while one of the buttons in limel-dock is focused, user should be able to navigate with arrow keys, to set the focus on the next or previous button, and toggle the tabindex.
  4. The last focused button in the Dock should be remembered. This means the tabindexes should be remembered. So next time that the user sets the focus inside the limel-dock component, the last item that had tabindex="0"would receive the focus.
  5. When useMobileLayout= true the left and right arrow keys can be used to navigate. Otherwise the top and bottom arrow keys should be used to navigate and set the focus on next and previous items.

New feature implementation

For inspiration

import {
    // ... existing imports ...
} from '@stencil/core';

@Component({
    // ... existing component configuration ...
})
export class Dock {
    // ... existing properties and methods ...

    private focusedButtonIndex: number = 0; // To remember the last focused button

    private handleKeyDown = (event: KeyboardEvent) => {
        if (event.key === 'ArrowLeft' && this.useMobileLayout) {
            this.navigateLeft();
        } else if (event.key === 'ArrowRight' && this.useMobileLayout) {
            this.navigateRight();
        } else if (event.key === 'ArrowUp' && !this.useMobileLayout) {
            this.navigateUp();
        } else if (event.key === 'ArrowDown' && !this.useMobileLayout) {
            this.navigateDown();
        }
    };

    private navigateLeft() {
        // Implement logic for left arrow key navigation (if needed)
    }

    private navigateRight() {
        // Implement logic for right arrow key navigation (if needed)
    }

    private navigateUp() {
        // Implement logic for up arrow key navigation (if needed)
    }

    private navigateDown() {
        // Implement logic for down arrow key navigation (if needed)
    }

    componentDidLoad() {
        // Set focus to the first button when the component loads
        const firstButton = this.getFirstFocusableButton();
        if (firstButton) {
            firstButton.focus();
        }

        // Add event listener for arrow key navigation
        document.addEventListener('keydown', this.handleKeyDown);
    }

    disconnectedCallback() {
        // Remove event listener to avoid memory leaks
        document.removeEventListener('keydown', this.handleKeyDown);
    }

    private getFirstFocusableButton(): HTMLButtonElement | null {
        const buttons = this.getDockButtons();
        return buttons[0] || null;
    }

    private getDockButtons(): NodeListOf<HTMLButtonElement> {
        return this.shadowRoot.querySelectorAll('limel-dock-button button');
    }
}

Inspiration for navigateRight()

private navigateRight() {
    const buttons = this.getDockButtons();
    const currentButton = buttons[this.focusedButtonIndex];

    if (currentButton) {
        // Remove focus from the current button
        currentButton.tabIndex = -1;
        currentButton.blur();
    }

    // Increment the index, cycling back to 0 if at the end
    this.focusedButtonIndex = (this.focusedButtonIndex + 1) % buttons.length;

    const nextButton = buttons[this.focusedButtonIndex];

    if (nextButton) {
        // Set focus to the next button
        nextButton.tabIndex = 0;
        nextButton.focus();
    }
}

⚠️ Important note

We want the same kind of logic in many components (see the umbrella issue). It's better to make a reusable script that can be imported into any component. Some components –depending on their layout of clickable elements–, may need all 4 different directions of the keyboard navigations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: All Open Issues
Development

No branches or pull requests

1 participant