Skip to content

Commit

Permalink
refactor(VTreeview): avoid re-render by opened & respect openAll (#20032
Browse files Browse the repository at this point in the history
)
  • Loading branch information
yuwu9145 committed Jun 20, 2024
1 parent d7fbcc5 commit 0a32df5
Show file tree
Hide file tree
Showing 2 changed files with 314 additions and 12 deletions.
17 changes: 5 additions & 12 deletions packages/vuetify/src/labs/VTreeview/VTreeview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ import { makeFilterProps, useFilter } from '@/composables/filter'
import { useProxiedModel } from '@/composables/proxiedModel'

// Utilities
import { computed, provide, ref, toRef, watch } from 'vue'
import { genericComponent, getCurrentInstance, omit, propsFactory, useRender } from '@/util'
import { computed, provide, ref, toRef } from 'vue'
import { genericComponent, omit, propsFactory, useRender } from '@/util'

// Types
import type { ExtractPublicPropTypes } from 'vue'
import { VTreeviewSymbol } from './shared'
import type { VListChildrenSlots } from '@/components/VList/VListChildren'
import type { ListItem } from '@/composables/list-items'
Expand All @@ -35,8 +34,6 @@ export const makeVTreeviewProps = propsFactory({
...omit(makeVListProps({
collapseIcon: '$treeviewCollapse',
expandIcon: '$treeviewExpand',
selectStrategy: 'classic' as const,
openStrategy: 'multiple' as const,
slim: true,
}), ['nav']),
}, 'VTreeview')
Expand All @@ -60,17 +57,16 @@ export const VTreeview = genericComponent<new <T>(
},

setup (props, { slots }) {
const vm = getCurrentInstance('VTreeview')
const { items } = useListItems(props)
const activeColor = toRef(props, 'activeColor')
const baseColor = toRef(props, 'baseColor')
const color = toRef(props, 'color')
const opened = useProxiedModel(props, 'opened')
const activated = useProxiedModel(props, 'activated')
const selected = useProxiedModel(props, 'selected')

const vListRef = ref<VList>()

const opened = computed(() => props.openAll ? openAll(items.value) : props.opened)
const flatItems = computed(() => flatten(items.value))
const search = toRef(props, 'search')
const { filteredItems } = useFilter(props, flatItems, search)
Expand Down Expand Up @@ -105,10 +101,6 @@ export const VTreeview = genericComponent<new <T>(
return arr
}

watch(() => props.openAll, val => {
opened.value = val ? openAll(items.value) : []
}, { immediate: true })

function openAll (item: any) {
let ids: number[] = []

Expand Down Expand Up @@ -148,7 +140,7 @@ export const VTreeview = genericComponent<new <T>(
})

useRender(() => {
const listProps = VList.filterProps(vm.vnode.props! as ExtractPublicPropTypes<typeof makeVTreeviewProps>)
const listProps = VList.filterProps(props)

const treeviewChildrenProps = VTreeviewChildren.filterProps(props)

Expand All @@ -161,6 +153,7 @@ export const VTreeview = genericComponent<new <T>(
props.class,
]}
style={ props.style }
opened={ opened.value }
v-model:activated={ activated.value }
v-model:selected={ selected.value }
>
Expand Down
309 changes: 309 additions & 0 deletions packages/vuetify/src/labs/VTreeview/__tests__/VTreeview.spec.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
/// <reference types="../../../../types/cypress" />

// Components
import { VTreeview } from '../VTreeview'

// Utilities
import { ref } from 'vue'

describe('VTreeview', () => {
const items = ref([
{
id: 1,
title: 'Videos :',
children: [
{
id: 2,
title: 'Tutorials :',
children: [
{ id: 3, title: 'Basic layouts : mp4' },
{ id: 4, title: 'Advanced techniques : mp4' },
{ id: 5, title: 'All about app : dir' },
],
},
{ id: 6, title: 'Intro : mov' },
{ id: 7, title: 'Conference introduction : avi' },
],
},
])
describe('activate', () => {
it('single-leaf strategy', () => {
const activated = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:activated={ activated.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
activatable
active-strategy="single-leaf"
/>
</>
))

cy.get('.v-treeview-item').should('have.length', 7)
.get('.v-treeview-item').eq(0).click()
.then(_ => {
expect(activated.value).to.deep.equal([])
})
.get('.v-treeview-item').eq(2).click()
.get('.v-treeview-item').eq(3).click()
.get('.v-treeview-item').eq(4).click()
.then(_ => {
expect(activated.value).to.deep.equal([5])
})
})
it('leaf strategy', () => {
const activated = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:activated={ activated.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
activatable
active-strategy="leaf"
/>
</>
))

cy.get('.v-treeview-item').should('have.length', 7)
.get('.v-treeview-item').eq(0).click()
.then(_ => {
expect(activated.value).to.deep.equal([])
})
.get('.v-treeview-item').eq(2).click()
.get('.v-treeview-item').eq(3).click()
.get('.v-treeview-item').eq(4).click()
.then(_ => {
expect(activated.value.sort()).to.deep.equal([3, 4, 5].sort())
})
})
it('independent strategy', () => {
const activated = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:activated={ activated.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
activatable
active-strategy="independent"
/>
</>
))

cy.get('.v-treeview-item').should('have.length', 7)
.get('.v-treeview-item').eq(0).click()
.then(_ => {
expect(activated.value).to.deep.equal([1])
})
.get('.v-treeview-item').eq(1).click()
.then(_ => {
expect(activated.value).to.deep.equal([1, 2])
})
.get('.v-treeview-item').eq(2).click()
.get('.v-treeview-item').eq(3).click()
.get('.v-treeview-item').eq(4).click()
.then(_ => {
expect(activated.value.sort()).to.deep.equal([1, 2, 3, 4, 5].sort())
})
})
it('single-independent strategy', () => {
const activated = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:activated={ activated.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
activatable
active-strategy="single-independent"
/>
</>
))

cy.get('.v-treeview-item').should('have.length', 7)
.get('.v-treeview-item').eq(0).click()
.then(_ => {
expect(activated.value).to.deep.equal([1])
})
.get('.v-treeview-item').eq(1).click()
.then(_ => {
expect(activated.value).to.deep.equal([2])
})
.get('.v-treeview-item').eq(2).click()
.get('.v-treeview-item').eq(3).click()
.get('.v-treeview-item').eq(4).click()
.then(_ => {
expect(activated.value).to.deep.equal([5])
})
})
})
describe('select', () => {
it('single-leaf strategy', () => {
const selected = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:selected={ selected.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
selectable
select-strategy="single-leaf"
/>
</>
))

cy.get('.v-checkbox-btn').should('have.length', 5)
.get('.v-checkbox-btn').eq(0).click(20, 20)
.get('.v-checkbox-btn').eq(1).click(20, 20)
.then(_ => {
expect(selected.value).to.deep.equal([4])
})
.get('.v-checkbox-btn').eq(3).click(20, 20)
.then(_ => {
expect(selected.value).to.deep.equal([6])
})
})
it('leaf strategy', () => {
const selected = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:selected={ selected.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
selectable
select-strategy="leaf"
/>
</>
))

cy.get('.v-checkbox-btn').should('have.length', 5)
.get('.v-checkbox-btn').eq(0).click(20, 20)
.get('.v-checkbox-btn').eq(1).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([3, 4].sort())
})
.get('.v-checkbox-btn').eq(3).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([3, 4, 6].sort())
})
})
it.only('independent strategy', () => {
const selected = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:selected={ selected.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
selectable
select-strategy="independent"
/>
</>
))

cy.get('.v-checkbox-btn').should('have.length', 7)
.get('.v-checkbox-btn').eq(2).click(20, 20)
.get('.v-checkbox-btn').eq(3).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([3, 4].sort())
})
.get('.v-checkbox-btn').eq(1).click(20, 20)
.get('.v-checkbox-btn').eq(0).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([1, 2, 3, 4].sort())
})
})
it('single-independent strategy', () => {
const selected = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:selected={ selected.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
selectable
select-strategy="single-independent"
/>
</>
))

cy.get('.v-checkbox-btn').should('have.length', 7)
.get('.v-checkbox-btn').eq(2).click(20, 20)
.get('.v-checkbox-btn').eq(3).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([4].sort())
})
.get('.v-checkbox-btn').eq(1).click(20, 20)
.get('.v-checkbox-btn').eq(0).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([1].sort())
})
})
it('classic strategy', () => {
const selected = ref([])
cy.mount(() => (
<>
<VTreeview
v-model:selected={ selected.value }
open-all
items={ items.value }
item-title="title"
item-value="id"
selectable
select-strategy="classic"
/>
</>
))

cy.get('.v-checkbox-btn').eq(2).click(20, 20)
.get('.v-checkbox-btn').eq(3).click(20, 20)
.get('.v-checkbox-btn').eq(4).click(20, 20)
.then(_ => {
expect(selected.value).to.deep.equal([3, 4, 5])
})
.get('.v-checkbox-btn').eq(1).click(20, 20)
.then(_ => {
expect(selected.value).to.deep.equal([])
})
.get('.v-checkbox-btn').eq(0).click(20, 20)
.then(_ => {
expect(selected.value.sort()).to.deep.equal([3, 4, 5, 6, 7].sort())
})
})
})
it('should have all items visible when open-all is applied', () => {
cy.mount(() => (
<>
<VTreeview
open-all
items={ items.value }
item-title="title"
item-value="id"
/>
</>
))
.get('.v-treeview-item')
.filter(':visible')
.should('have.length', 7)
})
})

0 comments on commit 0a32df5

Please sign in to comment.