Skip to content

Commit

Permalink
fix(VAutocomplete/VCombobox/VSelect): return-object comparison (#16114)
Browse files Browse the repository at this point in the history
fixes #16046

Co-authored-by: Kael <kaelwd@gmail.com>
Co-authored-by: John Leider <john@vuetifyjs.com>
  • Loading branch information
3 people committed Dec 12, 2022
1 parent ee33c68 commit 74f7601
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 4 deletions.
Expand Up @@ -120,7 +120,7 @@ export const VAutocomplete = genericComponent<new <
const { filteredItems } = useFilter(props, items, computed(() => isPristine.value ? undefined : search.value))
const selections = computed(() => {
return model.value.map(v => {
return items.value.find(item => item.value === v.value) || v
return items.value.find(item => props.valueComparator(item.value, v.value)) || v
})
})
const selected = computed(() => selections.value.map(selection => selection.props.value))
Expand Down
@@ -1,5 +1,6 @@
/// <reference types="../../../../types/cypress" />

import { ref } from 'vue'
import { VAutocomplete } from '../VAutocomplete'

describe('VAutocomplete', () => {
Expand All @@ -24,4 +25,75 @@ describe('VAutocomplete', () => {
.get('.v-chip')
.should('have.length', 2)
})

it('should have selected chip with array of strings', () => {
const items = ref(['California', 'Colorado', 'Florida'])

const selectedItems = ref(['California', 'Colorado'])

cy.mount(() => (
<VAutocomplete
v-model={selectedItems.value}
items={items.value}
chips
multiple
closableChips
/>
))

cy.get('.mdi-menu-down').click()

cy.get('.v-list-item--active').should('have.length', 2)
cy.get('.v-list-item--active input').eq(0).click().then(() => {
expect(selectedItems.value).to.deep.equal(['Colorado'])
})

cy.get('.v-list-item--active').should('have.length', 1)

cy
.get('.v-chip__close')
.eq(0)
.click()
.get('.v-chip')
.should('have.length', 0)
.should(() => expect(selectedItems.value).to.be.empty)
})

it('should have selected chip with return-object', () => {
const items = ref([
{
title: 'Item 1',
value: 'item1',
},
{
title: 'Item 2',
value: 'item2',
},
])

const selectedItems = ref([
{
title: 'Item 1',
value: 'item1',
},
])

cy.mount(() => (
<VAutocomplete
v-model={selectedItems.value}
items={items.value}
returnObject
chips
multiple
/>
))

cy.get('.mdi-menu-down').click()

cy.get('.v-list-item--active').should('have.length', 1)
cy.get('.v-list-item--active input').click().then(() => {
expect(selectedItems.value).to.be.empty
})
cy.get('.v-list-item--active').should('have.length', 0)
})
})
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VCombobox/VCombobox.tsx
Expand Up @@ -162,7 +162,7 @@ export const VCombobox = genericComponent<new <

const selections = computed(() => {
return model.value.map(v => {
return items.value.find(item => item.value === v.value) || v
return items.value.find(item => props.valueComparator(item.value, v.value)) || v
})
})
const selected = computed(() => selections.value.map(selection => selection.props.value))
Expand Down
Expand Up @@ -210,4 +210,86 @@ describe('VCombobox', () => {
.should('have.length', 1)
})
})

describe('prefilled data', () => {
it('should work with array of strings when using multiple', () => {
const items = ref(['California', 'Colorado', 'Florida'])

const selectedItems = ref(['California', 'Colorado'])

cy.mount(() => (
<VCombobox v-model={selectedItems.value} items={items.value} multiple chips closableChips />
))

cy.get('.v-combobox input').click()

cy.get('.v-list-item--active').should('have.length', 2)
cy.get('input').get('.v-chip').should('have.length', 2)

cy.get('.v-chip__close')
.eq(0)
.click()
.get('input')
.get('.v-chip')
.should('have.length', 1)
.should(() => expect(selectedItems.value).to.deep.equal(['Colorado']))
})

it('should work with objects when using multiple', () => {
const items = ref([
{
title: 'Item 1',
value: 'item1',
},
{
title: 'Item 2',
value: 'item2',
},
{
title: 'Item 3',
value: 'item3',
},
])

const selectedItems = ref(
[
{
title: 'Item 1',
value: 'item1',
},
{
title: 'Item 2',
value: 'item2',
},
]
)

cy.mount(() => (
<VCombobox
v-model={selectedItems.value}
items={items.value}
multiple
chips
closableChips
returnObject
/>
))

cy.get('.v-combobox input').click()

cy.get('.v-list-item--active').should('have.length', 2)
cy.get('input').get('.v-chip').should('have.length', 2)

cy.get('.v-chip__close')
.eq(0)
.click()
.get('input')
.get('.v-chip')
.should('have.length', 1)
.should(() => expect(selectedItems.value).to.deep.equal([{
title: 'Item 2',
value: 'item2',
}]))
})
})
})
8 changes: 6 additions & 2 deletions packages/vuetify/src/components/VSelect/VSelect.tsx
Expand Up @@ -21,7 +21,7 @@ import { IconValue } from '@/composables/icons'

// Utility
import { computed, mergeProps, ref } from 'vue'
import { genericComponent, omit, propsFactory, useRender, wrapInArray } from '@/util'
import { deepEqual, genericComponent, omit, propsFactory, useRender, wrapInArray } from '@/util'

// Types
import type { VInputSlots } from '@/components/VInput/VInput'
Expand Down Expand Up @@ -50,6 +50,10 @@ export const makeSelectProps = propsFactory({
default: '$vuetify.noDataText',
},
openOnClear: Boolean,
valueComparator: {
type: Function as PropType<typeof deepEqual>,
default: deepEqual,
},

...makeItemsProps({ itemChildren: false }),
}, 'v-select')
Expand Down Expand Up @@ -120,7 +124,7 @@ export const VSelect = genericComponent<new <
)
const selections = computed(() => {
return model.value.map(v => {
return items.value.find(item => item.value === v.value) || v
return items.value.find(item => props.valueComparator(item.value, v.value)) || v
})
})
const selected = computed(() => selections.value.map(selection => selection.props.value))
Expand Down
106 changes: 106 additions & 0 deletions packages/vuetify/src/components/VSelect/__tests__/VSelect.spec.cy.tsx
@@ -1,6 +1,7 @@
/// <reference types="../../../../types/cypress" />

import { VListItem } from '@/components/VList'
import { ref } from 'vue'
import { VSelect } from '../VSelect'

describe('VSelect', () => {
Expand Down Expand Up @@ -77,4 +78,109 @@ describe('VSelect', () => {
.get('.v-chip')
.should('have.length', 2)
})

describe('prefilled data', () => {
it('should work with array of strings when using multiple', () => {
const items = ref(['California', 'Colorado', 'Florida'])

const selectedItems = ref(['California', 'Colorado'])

cy.mount(() => (
<VSelect v-model={selectedItems.value} items={items.value} multiple chips closableChips />
))

cy.get('.v-select').click()

cy.get('.v-list-item--active').should('have.length', 2)
cy.get('.v-list-item input').eq(2).click().should(() => {
expect(selectedItems.value).to.deep.equal(['California', 'Colorado', 'Florida'])
})

cy
.get('.v-chip__close')
.eq(0)
.click()
.get('.v-chip')
.should('have.length', 2)
.should(() => expect(selectedItems.value).to.deep.equal(['Colorado', 'Florida']))
})

it('should work with objects when using multiple', () => {
const items = ref([
{
title: 'Item 1',
value: 'item1',
},
{
title: 'Item 2',
value: 'item2',
},
{
title: 'Item 3',
value: 'item3',
},
])

const selectedItems = ref(
[
{
title: 'Item 1',
value: 'item1',
},
{
title: 'Item 2',
value: 'item2',
},
]
)

cy.mount(() => (
<VSelect
v-model={selectedItems.value}
items={items.value}
multiple
chips
closableChips
returnObject
/>
))

cy.get('.v-select').click()

cy.get('.v-list-item--active').should('have.length', 2)
cy.get('.v-list-item input').eq(2).click().should(() => {
expect(selectedItems.value).to.deep.equal([
{
title: 'Item 1',
value: 'item1',
},
{
title: 'Item 2',
value: 'item2',
},
{
title: 'Item 3',
value: 'item3',
},
])
})

cy
.get('.v-chip__close')
.eq(0)
.click()
.get('.v-chip')
.should('have.length', 2)
.should(() => expect(selectedItems.value).to.deep.equal([
{
title: 'Item 2',
value: 'item2',
},
{
title: 'Item 3',
value: 'item3',
},
]))
})
})
})

0 comments on commit 74f7601

Please sign in to comment.