Skip to content

Commit

Permalink
fix(NcActions): intercept into current focus trap stack
Browse files Browse the repository at this point in the history
Signed-off-by: Grigorii K. Shartsev <me@shgk.me>
  • Loading branch information
ShGKme committed Jan 25, 2024
1 parent 20577bc commit 7f8fa2d
Showing 1 changed file with 42 additions and 3 deletions.
45 changes: 42 additions & 3 deletions src/components/NcActions/NcActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,7 @@ p {
import NcButton from '../NcButton/index.js'
import NcPopover from '../NcPopover/index.js'
import GenRandomId from '../../utils/GenRandomId.js'
import { getTrapStack } from '../../utils/focusTrap.js'
import { t } from '../../l10n.js'
import Vue, { computed } from 'vue'
Expand Down Expand Up @@ -994,6 +995,7 @@ export default {
isSemanticMenu: false,
isSemanticNavigation: false,
isSemanticPopoverLike: false,
externalFocusTrapStack: [],
}
},
Expand All @@ -1005,6 +1007,13 @@ export default {
// If it has a name, we use a secondary button
: this.menuName ? 'secondary' : 'tertiary')
},
/**
* Menu and navigation should not have focus trap
*/
shouldHaveFocusTrap() {
return this.isSemanticPopoverLike
},
},
watch: {
Expand All @@ -1016,9 +1025,40 @@ export default {
this.opened = state
},
opened() {
this.intersectIntoCurrentFocusTrapStack()
},
},
methods: {
/**
* When the component has its own focus trap, then it is managed by global trap stack by focus-trap.
*
* However if the component has no focus trap and is used inside another focus trap - there is an issue.
* By default popover content is rendered in body or other container, which is likely outside the current focus trap containers.
* It results in broken behavior from focus-trap.
*
* We need to pause all the focus traps for opening popover and then unpause them back after closing.
*/
intersectIntoCurrentFocusTrapStack() {
if (this.shouldHaveFocusTrap) {
return
}
if (this.opened) {
this.externalFocusTrapStack = [...getTrapStack()]
for (const trap of this.externalFocusTrapStack) {
trap.pause()
}
} else {
for (const trap of this.externalFocusTrapStack) {
trap.unpause()
}
this.externalFocusTrapStack = []
}
},
/**
* Do we have exactly one Action and
* is it allowed as a standalone element?
Expand Down Expand Up @@ -1381,10 +1421,9 @@ export default {
container: this.container,
popoverBaseClass: 'action-item__popper',
popupRole,
// Menu and navigation should not have focus trap
// Tab should close the menu and move focus to the next UI element
setReturnFocus: !this.isSemanticPopoverLike ? null : this.$refs.menuButton?.$el,
focusTrap: this.isSemanticPopoverLike,
setReturnFocus: this.shouldHaveFocusTrap ? this.$refs.menuButton?.$el : null,
focusTrap: this.shouldHaveFocusTrap,
},
// For some reason the popover component
// does not react to props given under the 'props' key,
Expand Down

0 comments on commit 7f8fa2d

Please sign in to comment.