Skip to content

Commit

Permalink
feat(activatable): create new mixin (#7037)
Browse files Browse the repository at this point in the history
* feat: init

* feat(VDialog): integrate activatable mixin

* refactor(activatable): abstract listeners to generator method

* refactor(menuable): minor cleanups

* refactor(menuable): use activatable

* chore(menuable): remove re-defined props

* test(activatable): add tests

* refactor(menuable): remove duplicated code

* refactor(activatable): change hover property name

* refactor(activatable): update listeners type and enforce only scoped slots

BREAKING CHANGE: all activator slots must be bound with v-slot

* refactor(VMenu): remove genActivator method

* docs(Core/SpecBtn): update slot structure

* refactor(activatable): update getActivator logic

use activatorNode of available

* refactor(VTooltip): update to properly use activatable

* refactor(activatable): change ordering of getActivator conditional

favor event over activatorNode

* refactor(activatable): change logic for activator slot bind error

* test(activatable): fix tests

* fix(activatable/VMenu): properly handle dynamic activators

* chore(VMenu): remove deprecated message

* fix(menuable): add support for disabling open-on-click

* style(menuable): remove explicit type declaration

* chore: remove unused props from tests
  • Loading branch information
Dmitry Sharshakov authored and johnleider committed Apr 24, 2019
1 parent 4523d56 commit 553a0d8
Show file tree
Hide file tree
Showing 16 changed files with 388 additions and 311 deletions.
22 changes: 12 additions & 10 deletions packages/docs/src/components/core/FileBtn.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<template>
<v-tooltip left bottom>
<v-btn
slot="activator"
:href="`https://github.com/vuetifyjs/vuetify/tree/master/packages/vuetify/src/${link}`"
icon
target="_blank"
rel="noopener"
aria-label="Source"
>
<v-icon>mdi-source-repository</v-icon>
</v-btn>
<template #activator="{ on }">
<v-btn
:href="`https://github.com/vuetifyjs/vuetify/tree/master/packages/vuetify/src/${link}`"
icon
target="_blank"
rel="noopener"
aria-label="Source"
v-on="on"
>
<v-icon>mdi-source-repository</v-icon>
</v-btn>
</template>
<span>Source</span>
</v-tooltip>
</template>
Expand Down
44 changes: 23 additions & 21 deletions packages/docs/src/components/core/SpecBtn.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
<template>
<v-tooltip left bottom>
<v-btn
slot="activator"
:href="link"
icon
target="_blank"
rel="noopener"
aria-label="Material Design Specification"
>
<v-badge
right
overlap
color="transparent"
<template #activator="{ on }">
<v-btn
:href="link"
icon
target="_blank"
rel="noopener"
aria-label="Material Design Specification"
v-on="on"
>
<span
v-if="version"
slot="badge"
class="caption black--text font-weight-black pl-2"
<v-badge
right
overlap
color="transparent"
>
{{ version }}
</span>
<v-icon>mdi-material-design</v-icon>
</v-badge>
</v-btn>
<span
v-if="version"
slot="badge"
class="caption black--text font-weight-black pl-2"
>
{{ version }}
</span>
<v-icon>mdi-material-design</v-icon>
</v-badge>
</v-btn>
</template>
<span>Material Design Specification</span>
</v-tooltip>
</template>
Expand Down
62 changes: 3 additions & 59 deletions packages/vuetify/src/components/VDialog/VDialog.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import './VDialog.sass'

// Mixins
import Activatable from '../../mixins/activatable'
import Dependent from '../../mixins/dependent'
import Detachable from '../../mixins/detachable'
import Overlayable from '../../mixins/overlayable'
Expand All @@ -12,15 +13,15 @@ import Toggleable from '../../mixins/toggleable'
import ClickOutside from '../../directives/click-outside'

// Helpers
import { convertToUnit, keyCodes, getSlotType } from '../../util/helpers'
import { convertToUnit, keyCodes } from '../../util/helpers'
import ThemeProvider from '../../util/ThemeProvider'
import { consoleError } from '../../util/console'
import mixins from '../../util/mixins'

// Types
import { VNode } from 'vue'

const baseMixins = mixins(
Activatable,
Dependent,
Detachable,
Overlayable,
Expand Down Expand Up @@ -129,12 +130,6 @@ export default baseMixins.extend({
})
},

mounted () {
if (getSlotType(this, 'activator', true) === 'v-slot') {
consoleError(`v-dialog's activator slot must be bound, try '<template #activator="data"><v-btn v-on="data.on>'`, this)
}
},

beforeDestroy () {
if (typeof window !== 'undefined') this.unbind()
},
Expand Down Expand Up @@ -225,57 +220,6 @@ export default baseMixins.extend({
const focusable = this.$refs.content.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
focusable.length && (focusable[0] as HTMLElement).focus()
}
},
getActivator (e?: Event) {
const activator = this.$refs.activator as HTMLElement

if (activator) {
return activator.children.length > 0
? activator.children[0]
: activator
}

if (e) {
this.activatedBy = e.currentTarget || e.target
}

if (this.activatedBy) return this.activatedBy

if (this.activatorNode) {
const activator = Array.isArray(this.activatorNode) ? this.activatorNode[0] : this.activatorNode
const el = activator && activator.elm
if (el) return el
}

return null
},
genActivator () {
if (!this.hasActivator) return null

const listeners = this.disabled ? undefined : {
click: (e: Event) => {
e.stopPropagation()
this.getActivator(e)
if (!this.disabled) this.isActive = !this.isActive
}
}

if (getSlotType(this, 'activator') === 'scoped') {
const activator = this.$scopedSlots.activator!({ on: listeners }) || null

this.activatorNode = activator

return activator
}

return this.$createElement('div', {
staticClass: 'v-dialog__activator',
class: {
'v-dialog__activator--disabled': this.disabled
},
ref: 'activator',
on: listeners
}, this.$slots.activator)
}
},

Expand Down
54 changes: 37 additions & 17 deletions packages/vuetify/src/components/VDialog/__tests__/VDialog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,20 @@ describe('VDialog.ts', () => {
it('should open dialog on activator click', async () => {
const input = jest.fn()
const wrapper = mountFunction({
slots: {
activator: ['<span>activator</span>']
scopedSlots: {
activator ({ on }) {
return this.$createElement('div', {
staticClass: 'activator',
on
})
}
}
})

wrapper.vm.$on('input', input)

expect(wrapper.vm.isActive).toBe(false)
wrapper.find('.v-dialog__activator').trigger('click')
wrapper.find('div.activator').trigger('click')
expect(wrapper.vm.isActive).toBe(true)
await wrapper.vm.$nextTick()
expect(input).toHaveBeenCalledWith(true)
Expand All @@ -153,15 +158,20 @@ describe('VDialog.ts', () => {
propsData: {
disabled: true
},
slots: {
activator: ['<span>activator</span>']
scopedSlots: {
activator ({ on }) {
return this.$createElement('div', {
staticClass: 'activator',
on
})
}
}
})

wrapper.vm.$on('input', input)

expect(wrapper.vm.isActive).toBe(false)
wrapper.find('.v-dialog__activator').trigger('click')
wrapper.find('div.activator').trigger('click')
expect(wrapper.vm.isActive).toBe(false)
await wrapper.vm.$nextTick()
expect(input).not.toHaveBeenCalled()
Expand All @@ -172,8 +182,8 @@ describe('VDialog.ts', () => {
propsData: {
value: false
},
slots: {
activator: ['<span>activator</span>']
scopedSlots: {
activator: '<span>activator</span>'
}
})

Expand Down Expand Up @@ -221,17 +231,22 @@ describe('VDialog.ts', () => {
expect(document.documentElement.className).toContain('overflow-y-hidden')
})

it('should not attach event handlers to the activator container if disabled', async () => {
it('should not respond to events if disabled', async () => {
const wrapper = mountFunction({
propsData: {
disabled: true
},
slots: {
activator: ['<button></button>']
scopedSlots: {
activator ({ on }) {
return this.$createElement('div', {
staticClass: 'activator',
on
})
}
}
})

const activator = wrapper.find('.v-dialog__activator')
const activator = wrapper.find('div.activator')
activator.trigger('click')

expect(wrapper.vm.isActive).toBe(false)
Expand All @@ -244,8 +259,8 @@ describe('VDialog.ts', () => {
expect(wrapper.element.style.display).toBe('block')

const wrapper2 = mountFunction({
slots: {
activator: ['<div></div>']
scopedSlots: {
activator: '<div></div>'
}
})
expect(wrapper2.element.style.display).toBe('inline-block')
Expand All @@ -268,16 +283,21 @@ describe('VDialog.ts', () => {
const input = jest.fn()
const clickOutside = jest.fn()
const wrapper = mountFunction({
slots: {
activator: ['<span>activator</span>']
scopedSlots: {
activator ({ on }) {
return this.$createElement('div', {
staticClass: 'activator',
on
})
}
}
})

wrapper.vm.$on('input', input)
wrapper.vm.$on('click:outside', clickOutside)

expect(wrapper.vm.isActive).toBe(false)
wrapper.find('.v-dialog__activator').trigger('click')
wrapper.find('div.activator').trigger('click')
expect(wrapper.vm.isActive).toBe(true)
await wrapper.vm.$nextTick()
expect(input).toHaveBeenCalledWith(true)
Expand Down
20 changes: 1 addition & 19 deletions packages/vuetify/src/components/VMenu/VMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ import ClickOutside from '../../directives/click-outside'
import Resize from '../../directives/resize'

// Helpers
import { convertToUnit, getSlotType } from '../../util/helpers'
import { convertToUnit } from '../../util/helpers'
import ThemeProvider from '../../util/ThemeProvider'
import { consoleError } from '../../util/console'

/* @vue/component */
export default Vue.extend({
Expand Down Expand Up @@ -152,30 +151,13 @@ export default Vue.extend({
},

watch: {
activator (newActivator, oldActivator) {
this.removeActivatorEvents(oldActivator)
this.addActivatorEvents(newActivator)
},
disabled (disabled) {
if (!this.activator) return

if (disabled) {
this.removeActivatorEvents(this.activator)
} else {
this.addActivatorEvents(this.activator)
}
},
isContentActive (val) {
this.hasJustFocused = val
}
},

mounted () {
this.isActive && this.activate()

if (getSlotType(this, 'activator', true) === 'v-slot') {
consoleError(`v-tooltip's activator slot must be bound, try '<template #activator="data"><v-btn v-on="data.on>'`, this)
}
},

methods: {
Expand Down

0 comments on commit 553a0d8

Please sign in to comment.