Skip to content

Commit

Permalink
fix(NcEmojiPicker): skip emoji on Tab navigation
Browse files Browse the repository at this point in the history
Signed-off-by: Grigorii K. Shartsev <[email protected]>
  • Loading branch information
ShGKme committed Jan 30, 2025
1 parent cd4bdf6 commit 16c596f
Showing 1 changed file with 21 additions and 35 deletions.
56 changes: 21 additions & 35 deletions src/components/NcEmojiPicker/NcEmojiPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,12 @@ This component allows the user to pick an emoji.
</docs>

<template>
<NcPopover :shown.sync="open"
<NcPopover ref="popover"
:shown.sync="open"
:container="container"
popup-role="dialog"
v-bind="$attrs"
:focus-trap="false /* Handled manually to remove emoji buttons from TAB sequence */"
v-on="$listeners"
@after-show="afterShow"
@after-hide="afterHide">
Expand All @@ -135,6 +137,7 @@ This component allows the user to pick an emoji.
aria-modal="true"
:aria-label="t('Emoji picker')"
v-bind="$attrs"
@keydown.native.tab.prevent="handleTabNavigationSkippingEmojis"
@select="select">
<template #searchTemplate="slotProps">
<div class="search__wrapper">
Expand Down Expand Up @@ -396,45 +399,28 @@ export default {
},

afterShow() {
// add focus trap in modal
const picker = this.$refs.picker
picker.$el.addEventListener('keydown', this.checkKeyEvent)

// set focus on input search field
const input = this.$refs.search?.$refs.inputField?.$refs.input
if (input) {
input.focus()
}
this.$refs.search.focus()
},

afterHide() {
// remove keydown listner if popover is hidden
const picker = this.$refs.picker
picker.$el.removeEventListener('keydown', this.checkKeyEvent)
// Manually return focus to the trigger button, as we disabled focus-trap
this.$refs.popover.$el.querySelector('button, [role="button"]')?.focus()
},

checkKeyEvent(event) {
if (event.key !== 'Tab') {
return
}
const picker = this.$refs.picker
const focusableList = picker.$el.querySelectorAll(
'button, input',
)
const last = focusableList.length - 1
// escape early if only 1 or no elements to focus
if (focusableList.length <= 1) {
event.preventDefault()
return
}
if (event.shiftKey === false && event.target === focusableList[last]) {
// Jump to first item when pressing tab on the latest item
event.preventDefault()
focusableList[0].focus()
} else if (event.shiftKey === true && event.target === focusableList[0]) {
// Jump to the last item if pressing shift+tab on the first item
event.preventDefault()
focusableList[last].focus()
/**
* Manually handle Tab navigation skipping emoji buttons.
* Navigation over emojis is handled by Arrow keys.
* @param {KeyboardEvent} event - Keyboard event
*/
handleTabNavigationSkippingEmojis(event) {
const current = event.target
const focusable = Array.from(this.$refs.picker.$el.querySelectorAll('button:not(.emoji-mart-emoji), input'))
if (!event.shiftKey) {
const nextNode = focusable.find((node) => current.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING) || focusable[0]
nextNode.focus()
} else {
const prevNode = focusable.findLast((node) => current.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_PRECEDING) || focusable.at(-1)
prevNode.focus()
}
},

Expand Down

0 comments on commit 16c596f

Please sign in to comment.