Akryum / floating-vue

💬 Easy tooltips, popovers, dropdown, menus... for Vue
https://floating-vue.starpad.dev/
MIT License
3.26k stars 333 forks source link

Accessibility: the active tooltip loses the ability to dismiss it with "escape" key when autoHide: true and noAutoFocus: true #1027

Open nnoack-bh opened 5 months ago

nnoack-bh commented 5 months ago

After some searching, I found that v-tooltip is able to be dismissed when autoHide: true (reference: https://github.com/Akryum/floating-vue/issues/159)

I also found that when tab-navigating to links or non-semantic HTML elements that use tabindex="0", we're now able to prevent the focus from shifting to the tooltip itself by setting noAutoFocus: true, which is great for accessibility because it doesn't lose meaningful sequence when tabbing through the DOM elements. (reference: https://github.com/Akryum/floating-vue/issues/872)

The problem is when these two settings are combined, escaping to hide the tooltip no longer works.

Just wondering if there was a way around this natively that I might've missed.

Thanks for the great library :)

nnoack-bh commented 4 months ago

There is no alternative for noAutofocus: true, so I'd definitely need that to be able to tab and show tooltips between different triggering elements.

I did try a suboptimal workaround for escaping the tooltip via keyboard, but it doesn't work 100% of the time, and it's not pretty.

Updating shown or hide did not work, so I had to toggle with disabled

Component.vue

<template>
...
  <span
    v-tooltip="{
      content: 'Here is the tooltip text',
      disabled: !isTooltipEnabled,
    }"
    tabindex="0"
  >
    Text of the triggering element
  </span>
...
</template>
...

mixin.js

export default {
  data() {
    return {
      isTooltipEnabled: true, // this is for the 'disabled' property of v-tooltip
      escapeKeyListener: null, // needed to ensure each component has its own listener
    };
  },
  mounted() {
    this.escapeKeyListener = (event) => {
      if (event.key === 'Escape') {
        this.isTooltipEnabled = false;

        // Nasty workaround to ensure v-tooltip is re-enabled after "escape"-ing to dismiss
        setTimeout(() => {
          this.isTooltipEnabled = true;
        }, 100);
      }
    };
    document.addEventListener('keydown', this.escapeKeyListener);
  },
  beforeUnmount() {
    document.removeEventListener('keydown', this.escapeKeyListener);
  }
};