wakirin / Litepicker

Date range picker - lightweight, no dependencies
MIT License
899 stars 132 forks source link

Keyboard navigation plugin doesn't allow getting past calendar element #240

Open pocketcolin opened 3 years ago

pocketcolin commented 3 years ago

Describe the bug If you are using the keyboard navigation plugin and select a date, the next tab moves you back to the top of the page. Thus, there is no way to use your keyboard to navigate below the first calendar on a page.

To Reproduce Steps to reproduce the behavior:

  1. Go to https://litepicker.com/docs/plugins/keyboardnav/
  2. Open up the demo and tab into the input element to trigger the calendar
  3. Continue using your keyboard to select a date
  4. Hit tab again after the calendar is closed and you will see that the focus is now in the Litepicker header.

Expected behavior When the calendar closes, focus should move to the next element on the page.

leads commented 3 years ago

Came here to mention this.

If you click on the input to open the gadget, you can tab past it but not into it.

If you tab to it, you cannot tab past it. Calender opens automatically on tab - perhaps it should open when 'return' or 'space' is pressed? Arrow keys can be used once you have tabbed to the dates. But once a date has been selected with 'return', the focussed date is then the first date available, rather than the one just selected.

Once two dates are selected with keyboard tab order is completely lost.

PSalaun commented 3 years ago

Not sure how to make it properly with the library, so I'm not pushing a PR, but here is my dirty workaround. It implies to edit a plugin file directly, so better to fork the repo.

To be able to trigger the modal only when pressing enter, and not just focusing the input, put this in the code where Litepicker is instantiated:

datePickerField.addEventListener('keydown', (e) => {
      if (e.code === 'Enter') {
        datePickerInstance.show();
        const modal = document.querySelector('.litepicker');
        const focusElement = modal.querySelector('[tabindex="1"]');
        focusElement.focus();
      }
});

You will have to remove the onFocus method and the two related event listeners in keyboardnav.js though.

And then to correctly focus another element when closing the modal with ESC:

// pass the next dom element I want to focus after closing the modal
return new Litepicker({
      element: document.getElementById('datepicker');
      plugins: ['keyboardnav'],
      nextFocusElement: document.querySelector('.next-element-in-the-dom'),
 });

in keyboardnav.js, edit onBlur like this


function onBlur(evt) {
      if (this.options.inlineMode) {
        return;
      }

      // get next focusable element
      setTimeout(() => {
        const activeElement = document.activeElement;

        if (!this.ui.contains(activeElement)) {
          this.nextFocusElement = this.options.nextFocusElement; // NEW: here is our next focus element
        }
      });
    }

and in the onKeyDown method:

case 'Escape':
          picker.hide();
          this.options.nextFocusElement.focus(); // NEW: we focus the next element manually
          break;