Skip to content

Commit

Permalink
fix(VDialog): focus on internal content when shown (#14584)
Browse files Browse the repository at this point in the history
fixes #14581

Co-authored-by: Kael <kaelwd@gmail.com>
  • Loading branch information
TreVld and KaelWD committed Jun 29, 2022
1 parent 621f273 commit ffbaae1
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 34 deletions.
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VDialog/VDialog.sass
Expand Up @@ -9,6 +9,7 @@
transition: .3s map-get($transition, 'fast-in-fast-out')
width: 100%
z-index: inherit
outline: none
+elevation($dialog-elevation)

&:not(.v-dialog--fullscreen)
Expand Down Expand Up @@ -46,7 +47,6 @@
transition: .2s map-get($transition, 'fast-in-fast-out'), z-index 1ms
width: 100%
z-index: 6
outline: none

.v-dialog__container
display: none
Expand Down
15 changes: 9 additions & 6 deletions packages/vuetify/src/components/VDialog/VDialog.ts
Expand Up @@ -178,9 +178,9 @@ export default baseMixins.extend({
// Double nextTick to wait for lazy content to be generated
this.$nextTick(() => {
this.$nextTick(() => {
if (!this.$refs.content.contains(document.activeElement)) {
if (!this.$refs.dialog?.contains(document.activeElement)) {
this.previousActiveElement = document.activeElement as HTMLElement
this.$refs.content.focus()
this.$refs.dialog?.focus()
}
this.bind()
})
Expand Down Expand Up @@ -222,18 +222,19 @@ export default baseMixins.extend({

if (
!!target &&
this.$refs.dialog &&
// It isn't the document or the dialog body
![document, this.$refs.content].includes(target) &&
![document, this.$refs.dialog].includes(target) &&
// It isn't inside the dialog body
!this.$refs.content.contains(target) &&
!this.$refs.dialog.contains(target) &&
// We're the topmost dialog
this.activeZIndex >= this.getMaxZIndex() &&
// It isn't inside a dependent element (like a menu)
!this.getOpenDependentElements().some(el => el.contains(target))
// So we must have focused something outside the dialog and its children
) {
// Find and focus the first available element inside the dialog
const focusable = this.$refs.content.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
const focusable = this.$refs.dialog.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
const el = [...focusable].find(el => !el.hasAttribute('disabled')) as HTMLElement | undefined
el && el.focus()
}
Expand All @@ -251,7 +252,6 @@ export default baseMixins.extend({
class: this.contentClasses,
attrs: {
role: 'dialog',
tabindex: this.isActive ? 0 : undefined,
'aria-modal': this.hideOverlay ? undefined : 'true',
...this.getScopeIdAttrs(),
},
Expand All @@ -278,6 +278,9 @@ export default baseMixins.extend({
genInnerContent () {
const data: VNodeData = {
class: this.classes,
attrs: {
tabindex: this.isActive ? 0 : undefined,
},
ref: 'dialog',
directives: [
{
Expand Down
Expand Up @@ -308,15 +308,15 @@ describe('VDialog.ts', () => {
propsData: { eager: true },
})

const content = wrapper.find('.v-dialog__content')
const dialog = wrapper.find('.v-dialog')

expect(content.html()).toMatchSnapshot()
expect(content.element.tabIndex).toBe(-1)
expect(dialog.html()).toMatchSnapshot()
expect(dialog.element.tabIndex).toBe(-1)

wrapper.setData({ isActive: true })

expect(content.element.tabIndex).toBe(0)
expect(content.html()).toMatchSnapshot()
expect(dialog.element.tabIndex).toBe(0)
expect(dialog.html()).toMatchSnapshot()
})

// https://github.com/vuetifyjs/vuetify/issues/8697
Expand Down
@@ -1,29 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VDialog.ts should only set tabindex if active 1`] = `
<div role="dialog"
aria-modal="true"
class="v-dialog__content"
style="z-index: 0;"
<div class="v-dialog"
style="transform-origin: center center; display: none;"
>
<div class="v-dialog"
style="transform-origin: center center; display: none;"
>
</div>
</div>
`;

exports[`VDialog.ts should only set tabindex if active 2`] = `
<div role="dialog"
aria-modal="true"
class="v-dialog__content v-dialog__content--active"
style="z-index: 202; z-index: 202;"
<div class="v-dialog v-dialog--active"
style="transform-origin: center center;"
tabindex="0"
>
<div class="v-dialog v-dialog--active"
style="transform-origin: center center;"
>
</div>
</div>
`;

Expand Down
6 changes: 3 additions & 3 deletions packages/vuetify/src/mixins/dependent/index.ts
Expand Up @@ -3,10 +3,10 @@ import Vue from 'vue'
import mixins from '../../util/mixins'
import { VOverlay } from '../../components/VOverlay'

interface options extends Vue {
interface options {
$el: HTMLElement
$refs: {
content: HTMLElement
content?: HTMLElement
}
overlay?: InstanceType<typeof VOverlay>
}
Expand All @@ -31,7 +31,7 @@ function searchChildren (children: Vue[]): DependentInstance[] {
}

/* @vue/component */
export default mixins<options>().extend({
export default mixins<Vue & options>().extend({
name: 'dependent',

data () {
Expand Down
6 changes: 3 additions & 3 deletions packages/vuetify/src/mixins/detachable/index.ts
Expand Up @@ -7,13 +7,13 @@ import mixins, { ExtractVue } from '../../util/mixins'
import { consoleWarn } from '../../util/console'

// Types
import Vue, { PropOptions } from 'vue'
import { PropOptions } from 'vue'
import { VNode } from 'vue/types'

interface options extends Vue {
interface options {
$el: HTMLElement
$refs: {
content: HTMLElement
content?: HTMLElement
}
}

Expand Down

0 comments on commit ffbaae1

Please sign in to comment.