@dimailn/vuetify 2.7.2-alpha52 → 2.7.2-alpha54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vuetify.js +13 -8
- package/dist/vuetify.js.map +1 -1
- package/dist/vuetify.min.css +1 -1
- package/dist/vuetify.min.js +2 -2
- package/es5/components/VTreeview/VTreeview.js +1 -1
- package/es5/components/VTreeview/VTreeview.js.map +1 -1
- package/es5/framework.js +1 -1
- package/es5/mixins/routable/index.js +12 -6
- package/es5/mixins/routable/index.js.map +1 -1
- package/lib/components/VTreeview/VTreeview.js +2 -2
- package/lib/components/VTreeview/VTreeview.js.map +1 -1
- package/lib/framework.js +1 -1
- package/lib/mixins/routable/index.js +11 -5
- package/lib/mixins/routable/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/VList/__tests__/VListItem.spec.ts +117 -0
- package/src/components/VTabs/__tests__/VTab.spec.ts +3 -5
- package/src/components/VTreeview/VTreeview.ts +2 -2
- package/src/mixins/routable/__tests__/routable.spec.ts +75 -1
- package/src/mixins/routable/index.ts +7 -5
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// Components
|
|
2
2
|
import VListItem from '../VListItem'
|
|
3
3
|
|
|
4
|
+
// Libraries
|
|
5
|
+
import { defineComponent, h, nextTick } from 'vue'
|
|
6
|
+
|
|
4
7
|
// Utilities
|
|
5
8
|
import {
|
|
6
9
|
mount,
|
|
7
10
|
VueWrapper,
|
|
8
11
|
enableAutoUnmount
|
|
9
12
|
} from '@vue/test-utils'
|
|
13
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
10
14
|
import { Vue3RouterLinkStub } from '../../../../test/util/stubs'
|
|
11
15
|
|
|
12
16
|
describe('VListItem.ts', () => {
|
|
@@ -325,4 +329,117 @@ describe('VListItem.ts', () => {
|
|
|
325
329
|
|
|
326
330
|
expect(wrapper.vm.isClickable).toBe(true)
|
|
327
331
|
})
|
|
332
|
+
|
|
333
|
+
it('passes active and toggle in default slot scope', async () => {
|
|
334
|
+
const wrapper = mountFunction({
|
|
335
|
+
props: { modelValue: true },
|
|
336
|
+
slots: {
|
|
337
|
+
default: ({ active, toggle }: { active: boolean, toggle: Function }) => h('div', [
|
|
338
|
+
h('span', { class: { 'link--text': active } }, String(active)),
|
|
339
|
+
h('button', { onClick: toggle }, 'toggle')
|
|
340
|
+
])
|
|
341
|
+
}
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
expect(wrapper.find('.link--text').exists()).toBe(true)
|
|
345
|
+
expect(wrapper.find('span').text()).toBe('true')
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
it('does not pass undefined to activeClass when to is used without activeClass', () => {
|
|
349
|
+
const RouterLinkCapture = defineComponent({
|
|
350
|
+
name: 'RouterLinkCapture',
|
|
351
|
+
props: {
|
|
352
|
+
to: { type: [String, Object], required: true },
|
|
353
|
+
activeClass: String,
|
|
354
|
+
exactActiveClass: String
|
|
355
|
+
},
|
|
356
|
+
setup (props, { slots }) {
|
|
357
|
+
return () => h('a', { class: props.activeClass }, slots.default?.())
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
const wrapper = mountFunction({
|
|
362
|
+
props: { to: '/foo' },
|
|
363
|
+
global: {
|
|
364
|
+
stubs: {
|
|
365
|
+
'router-link': RouterLinkCapture
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
const routerLink = wrapper.findComponent({ name: 'RouterLinkCapture' })
|
|
371
|
+
|
|
372
|
+
expect(routerLink.props('activeClass')).toBe('v-list-item--active')
|
|
373
|
+
expect(routerLink.props('activeClass')).not.toContain('undefined')
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('syncs slot active with router-link when route matches', async () => {
|
|
377
|
+
const router = createRouter({
|
|
378
|
+
history: createWebHistory(),
|
|
379
|
+
routes: [
|
|
380
|
+
{ path: '/products/:id', component: { template: '<div />' } }
|
|
381
|
+
]
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
await router.push('/products/131830946')
|
|
385
|
+
await router.isReady()
|
|
386
|
+
|
|
387
|
+
const Host = defineComponent({
|
|
388
|
+
components: { VListItem },
|
|
389
|
+
props: { to: String },
|
|
390
|
+
template: `
|
|
391
|
+
<v-list-item :to="to">
|
|
392
|
+
<template #default="{ active }">
|
|
393
|
+
<span :class="{ 'link--text': active }">{{ active }}</span>
|
|
394
|
+
</template>
|
|
395
|
+
</v-list-item>
|
|
396
|
+
`
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
const wrapper = mount(Host, {
|
|
400
|
+
props: { to: '/products/131830946' },
|
|
401
|
+
global: {
|
|
402
|
+
plugins: [router],
|
|
403
|
+
provide: { isInGroup: true },
|
|
404
|
+
stubs: {
|
|
405
|
+
'router-link': Vue3RouterLinkStub
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
const listItem = wrapper.findComponent(VListItem)
|
|
411
|
+
|
|
412
|
+
const linkRef = listItem.vm.$refs.link as any
|
|
413
|
+
const linkEl = linkRef.$el || linkRef
|
|
414
|
+
linkEl.classList.add('v-list-item--active')
|
|
415
|
+
|
|
416
|
+
listItem.vm.onRouteChange()
|
|
417
|
+
await nextTick()
|
|
418
|
+
await nextTick()
|
|
419
|
+
|
|
420
|
+
expect(listItem.vm.isActive).toBe(true)
|
|
421
|
+
expect(wrapper.find('.link--text').exists()).toBe(true)
|
|
422
|
+
expect(listItem.element.getAttribute('aria-selected')).toBe('true')
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('syncs aria-selected with isActive in list item group', async () => {
|
|
426
|
+
const wrapper = mountFunction({
|
|
427
|
+
props: { modelValue: 'item-1' },
|
|
428
|
+
global: {
|
|
429
|
+
provide: {
|
|
430
|
+
isInGroup: true,
|
|
431
|
+
listItemGroup: {
|
|
432
|
+
activeClass: 'v-item--active',
|
|
433
|
+
register: () => {},
|
|
434
|
+
unregister: () => {}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
wrapper.vm.isActive = true
|
|
441
|
+
await wrapper.vm.$nextTick()
|
|
442
|
+
|
|
443
|
+
expect(wrapper.element.getAttribute('aria-selected')).toBe('true')
|
|
444
|
+
})
|
|
328
445
|
})
|
|
@@ -80,11 +80,9 @@ describe('VTab.ts', () => {
|
|
|
80
80
|
// explicitly mock class added
|
|
81
81
|
// by vue router
|
|
82
82
|
if (wrapper.vm.$refs.link) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
}
|
|
83
|
+
const linkRef = wrapper.vm.$refs.link as any
|
|
84
|
+
const linkEl = linkRef.$el || linkRef
|
|
85
|
+
linkEl.classList.add('bar', 'v-tab--active')
|
|
88
86
|
}
|
|
89
87
|
(wrapper.vm as any).$route.path = '/foo'
|
|
90
88
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PropType, VNode } from 'vue'
|
|
2
|
-
import { h } from 'vue'
|
|
2
|
+
import { h, markRaw } from 'vue'
|
|
3
3
|
import type { VNodeChildrenArrayContents } from '../../types/vue-internal'
|
|
4
4
|
// Styles
|
|
5
5
|
import './VTreeview.sass'
|
|
@@ -320,7 +320,7 @@ export default mixins(
|
|
|
320
320
|
register (node: VTreeviewNodeInstance) {
|
|
321
321
|
const key = getObjectValueByPath(node.item, this.itemKey)
|
|
322
322
|
|
|
323
|
-
this.nodes[key].vnode = node
|
|
323
|
+
this.nodes[key].vnode = markRaw(node)
|
|
324
324
|
|
|
325
325
|
this.updateVnodeState(key)
|
|
326
326
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Routable from '../'
|
|
2
2
|
import { mount, Wrapper } from '@vue/test-utils'
|
|
3
3
|
import { createRouter, createWebHistory } from 'vue-router'
|
|
4
|
-
import { nextTick } from 'vue'
|
|
4
|
+
import { defineComponent, h, nextTick } from 'vue'
|
|
5
5
|
|
|
6
6
|
describe('routable.ts', () => {
|
|
7
7
|
let mountFunction: (options?: object) => Wrapper<any>
|
|
@@ -102,4 +102,78 @@ describe('routable.ts', () => {
|
|
|
102
102
|
expect(wrapper.vm.isLink).toBe(true)
|
|
103
103
|
expect(wrapper.vm.isClickable).toBe(true)
|
|
104
104
|
})
|
|
105
|
+
|
|
106
|
+
it('should not include undefined in activeClass passed to router-link', async () => {
|
|
107
|
+
const RouterLinkCapture = defineComponent({
|
|
108
|
+
name: 'RouterLinkCapture',
|
|
109
|
+
props: {
|
|
110
|
+
to: { type: [String, Object], required: true },
|
|
111
|
+
activeClass: String,
|
|
112
|
+
exactActiveClass: String
|
|
113
|
+
},
|
|
114
|
+
template: '<a><slot /></a>'
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const wrapper = mount({
|
|
118
|
+
mixins: [Routable],
|
|
119
|
+
data: () => ({
|
|
120
|
+
proxyClass: 'v-list-item--active'
|
|
121
|
+
}),
|
|
122
|
+
render () {
|
|
123
|
+
const { tag, data } = this.generateRouteLink()
|
|
124
|
+
return h(tag, data, { default: () => 'link' })
|
|
125
|
+
}
|
|
126
|
+
}, {
|
|
127
|
+
global: {
|
|
128
|
+
plugins: [router],
|
|
129
|
+
stubs: {
|
|
130
|
+
'router-link': RouterLinkCapture
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
props: {
|
|
134
|
+
to: '/'
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const routerLink = wrapper.findComponent({ name: 'RouterLinkCapture' })
|
|
139
|
+
|
|
140
|
+
expect(routerLink.props('activeClass')).toBe('v-list-item--active')
|
|
141
|
+
expect(routerLink.props('activeClass')).not.toContain('undefined')
|
|
142
|
+
expect(routerLink.props('exactActiveClass')).toBe('v-list-item--active')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should sync isActive with router-link on route change', async () => {
|
|
146
|
+
const toggle = jest.fn()
|
|
147
|
+
const wrapper = mount({
|
|
148
|
+
mixins: [Routable],
|
|
149
|
+
data: () => ({
|
|
150
|
+
proxyClass: 'v-tab--active'
|
|
151
|
+
}),
|
|
152
|
+
methods: {
|
|
153
|
+
toggle
|
|
154
|
+
},
|
|
155
|
+
template: '<div ref="link" />'
|
|
156
|
+
}, {
|
|
157
|
+
global: {
|
|
158
|
+
plugins: [router]
|
|
159
|
+
},
|
|
160
|
+
props: {
|
|
161
|
+
to: '/foo',
|
|
162
|
+
activeClass: 'bar'
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
wrapper.vm.onRouteChange()
|
|
167
|
+
await nextTick()
|
|
168
|
+
|
|
169
|
+
expect(toggle).not.toHaveBeenCalled()
|
|
170
|
+
|
|
171
|
+
const linkEl = wrapper.vm.$refs.link as HTMLElement
|
|
172
|
+
linkEl.classList.add('bar', 'v-tab--active')
|
|
173
|
+
|
|
174
|
+
wrapper.vm.onRouteChange()
|
|
175
|
+
await nextTick()
|
|
176
|
+
|
|
177
|
+
expect(toggle).toHaveBeenCalledTimes(1)
|
|
178
|
+
})
|
|
105
179
|
})
|
|
@@ -6,7 +6,6 @@ import { defineComponent, resolveComponent } from 'vue'
|
|
|
6
6
|
import Ripple, { RippleOptions } from '../../directives/ripple'
|
|
7
7
|
|
|
8
8
|
// Utilities
|
|
9
|
-
import { getObjectValueByPath } from '../../util/helpers'
|
|
10
9
|
|
|
11
10
|
export default defineComponent({
|
|
12
11
|
name: 'routable',
|
|
@@ -119,8 +118,8 @@ export default defineComponent({
|
|
|
119
118
|
let exactActiveClass = this.exactActiveClass || activeClass
|
|
120
119
|
|
|
121
120
|
if (this.proxyClass) {
|
|
122
|
-
activeClass =
|
|
123
|
-
exactActiveClass =
|
|
121
|
+
activeClass = [activeClass, this.proxyClass].filter(Boolean).join(' ')
|
|
122
|
+
exactActiveClass = [exactActiveClass, this.proxyClass].filter(Boolean).join(' ')
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
tag = resolveComponent(this.nuxt ? 'nuxt-link' : 'router-link')
|
|
@@ -148,11 +147,14 @@ export default defineComponent({
|
|
|
148
147
|
const activeClass = `${this.activeClass || ''} ${this.proxyClass || ''}`.trim()
|
|
149
148
|
const exactActiveClass = `${this.exactActiveClass || ''} ${this.proxyClass || ''}`.trim() || activeClass
|
|
150
149
|
|
|
151
|
-
const
|
|
150
|
+
const activeClasses = (this.exact ? exactActiveClass : activeClass).split(' ')
|
|
152
151
|
|
|
153
152
|
this.$nextTick(() => {
|
|
154
153
|
/* istanbul ignore else */
|
|
155
|
-
|
|
154
|
+
const el = (this.$refs.link as any).$el || this.$refs.link
|
|
155
|
+
const isLinkActive = activeClasses.every(c => el?.classList?.contains(c))
|
|
156
|
+
|
|
157
|
+
if (isLinkActive !== this.isActive) {
|
|
156
158
|
this.toggle()
|
|
157
159
|
}
|
|
158
160
|
})
|