2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-22 10:30:34 +03:00

create dropdown component, inject context

This commit is contained in:
Jeff Sagal
2022-11-30 11:15:38 -08:00
parent 4942cb20ac
commit 0965d602c4
7 changed files with 95 additions and 59 deletions
+31
View File
@@ -0,0 +1,31 @@
<script setup lang="ts">
import { inject } from 'vue'
import vAppendToBody from '@/directives/appendToBody.js'
import { VueSelectInjectionKey } from '@/symbols'
import type { VueSelectInjectedProps } from '@/components/Select.vue'
const context = inject<VueSelectInjectedProps>(VueSelectInjectionKey)
</script>
<template>
<ul
v-if="context.dropdownOpen"
:id="`vs${context.uid}__listbox`"
ref="dropdownMenu"
:key="`vs${context.uid}__listbox`"
v-append-to-body
class="vs__dropdown-menu"
role="listbox"
tabindex="-1"
@mousedown.prevent="context.onMousedown"
@mouseup="context.onMouseUp"
>
<slot></slot>
</ul>
<ul
v-else
:id="`vs${context.uid}__listbox`"
role="listbox"
style="display: none; visibility: hidden"
></ul>
</template>
+28 -28
View File
@@ -83,18 +83,7 @@
</div> </div>
</div> </div>
<transition :name="transition"> <transition :name="transition">
<ul <DropdownMenu>
v-if="dropdownOpen"
:id="`vs${uid}__listbox`"
ref="dropdownMenu"
:key="`vs${uid}__listbox`"
v-append-to-body
class="vs__dropdown-menu"
role="listbox"
tabindex="-1"
@mousedown.prevent="onMousedown"
@mouseup="onMouseUp"
>
<slot name="list-header" v-bind="scope.listHeader" /> <slot name="list-header" v-bind="scope.listHeader" />
<li <li
v-for="(option, index) in filteredOptions" v-for="(option, index) in filteredOptions"
@@ -123,34 +112,32 @@
</slot> </slot>
</li> </li>
<slot name="list-footer" v-bind="scope.listFooter" /> <slot name="list-footer" v-bind="scope.listFooter" />
</ul> </DropdownMenu>
<ul
v-else
:id="`vs${uid}__listbox`"
role="listbox"
style="display: none; visibility: hidden"
></ul>
</transition> </transition>
<slot name="footer" v-bind="scope.footer" /> <slot name="footer" v-bind="scope.footer" />
</div> </div>
</template> </template>
<script> <script lang="ts">
import pointerScroll from '@/mixins/pointerScroll.js' import pointerScroll from '@/mixins/pointerScroll.js'
import typeAheadPointer from '@/mixins/typeAheadPointer.js' import typeAheadPointer from '@/mixins/typeAheadPointer.js'
import ajax from '@/mixins/ajax.js' import ajax from '@/mixins/ajax.js'
import childComponents from '@/components/childComponents.js' import childComponents from '@/components/childComponents.js'
import appendToBody from '@/directives/appendToBody.js'
import sortAndStringify from '@/utility/sortAndStringify.js' import sortAndStringify from '@/utility/sortAndStringify.js'
import uniqueId from '@/utility/uniqueId.js' import uniqueId from '@/utility/uniqueId.js'
import { computed, ComputedRef, defineComponent } from 'vue'
import { VueSelectInjectionKey } from '@/symbols.js'
import DropdownMenu from '@/components/DropdownMenu.vue'
/** export interface VueSelectContext {
* @name VueSelect uid: ComputedRef<string>
*/ dropdownOpen: ComputedRef<boolean>
export default { onMousedown: (e: MouseEvent) => void
components: { ...childComponents }, onMouseup: (e: MouseEvent) => void
}
directives: { appendToBody }, export default defineComponent({
components: { DropdownMenu, ...childComponents },
mixins: [pointerScroll, typeAheadPointer, ajax], mixins: [pointerScroll, typeAheadPointer, ajax],
@@ -689,6 +676,19 @@ export default {
}, },
}, },
provide() {
return {
[VueSelectInjectionKey]: computed(() => {
return {
uid: this.uid,
dropdownOpen: this.dropdownOpen,
onMousedown: this.onMousedown,
onMouseup: this.onMouseUp,
}
}),
}
},
data() { data() {
return { return {
search: '', search: '',
@@ -1362,5 +1362,5 @@ export default {
} }
}, },
}, },
} })
</script> </script>
+5
View File
@@ -0,0 +1,5 @@
import type { InjectionKey } from 'vue'
export const VueSelectInjectionKey = Symbol(
'VueSelectInjectionKey'
) as InjectionKey<string>
+3 -3
View File
@@ -1,4 +1,4 @@
import { shallowMount } from '@vue/test-utils' import { mount, shallowMount } from '@vue/test-utils'
import VueSelect from '@/components/Select.vue' import VueSelect from '@/components/Select.vue'
/** /**
@@ -50,7 +50,7 @@ export const selectTag = async (Wrapper, searchText) => {
* @returns {Wrapper<Vue>} * @returns {Wrapper<Vue>}
*/ */
export const selectWithProps = (props = {}) => { export const selectWithProps = (props = {}) => {
return shallowMount(VueSelect, { props }) return mount(VueSelect, { props })
} }
/** /**
@@ -60,7 +60,7 @@ export const selectWithProps = (props = {}) => {
* @return {Wrapper<Vue>} * @return {Wrapper<Vue>}
*/ */
export const mountDefault = (props = {}, options = {}) => { export const mountDefault = (props = {}, options = {}) => {
return shallowMount(VueSelect, { return mount(VueSelect, {
props: { props: {
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
...props, ...props,
+3 -3
View File
@@ -1,5 +1,5 @@
import { it, describe, expect, vi } from 'vitest' import { it, describe, expect, vi } from 'vitest'
import { shallowMount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import VueSelect from '@/components/Select.vue' import VueSelect from '@/components/Select.vue'
import { selectWithProps } from '@tests/helpers.js' import { selectWithProps } from '@tests/helpers.js'
@@ -29,7 +29,7 @@ describe('Labels', () => {
}) })
it('should display a placeholder if the value is empty', () => { it('should display a placeholder if the value is empty', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
options: ['one'], options: ['one'],
}, },
@@ -66,7 +66,7 @@ describe('Labels', () => {
*/ */
// it('will not call getOptionLabel if both scoped option slots are used and a filter is provided', () => { // it('will not call getOptionLabel if both scoped option slots are used and a filter is provided', () => {
// const spy = spyOn(VueSelect.props.getOptionLabel, 'default') // const spy = spyOn(VueSelect.props.getOptionLabel, 'default')
// const Select = shallowMount(VueSelect, { // const Select = mount(VueSelect, {
// props: { // props: {
// options: [{ name: 'one' }], // options: [{ name: 'one' }],
// filter: () => {}, // filter: () => {},
+24 -24
View File
@@ -1,5 +1,5 @@
import { it, describe, expect, vi, beforeEach, afterEach } from 'vitest' import { it, describe, expect, vi, beforeEach, afterEach } from 'vitest'
import { mount, shallowMount } from '@vue/test-utils' import { mount, mount } from '@vue/test-utils'
import VueSelect from '@/components/Select.vue' import VueSelect from '@/components/Select.vue'
import typeAheadPointer from '@/mixins/typeAheadPointer.js' import typeAheadPointer from '@/mixins/typeAheadPointer.js'
import { mountDefault } from '@tests/helpers.js' import { mountDefault } from '@tests/helpers.js'
@@ -20,14 +20,14 @@ describe('VS - Selecting Values', () => {
}) })
it('can accept an array with pre-selected values', () => { it('can accept an array with pre-selected values', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: defaultProps, props: defaultProps,
}) })
expect(Select.vm.selectedValue[0]).toEqual(Select.vm.modelValue) expect(Select.vm.selectedValue[0]).toEqual(Select.vm.modelValue)
}) })
it('can accept an array of objects and pre-selected value (single)', () => { it('can accept an array of objects and pre-selected value (single)', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
modelValue: { label: 'This is Foo', value: 'foo' }, modelValue: { label: 'This is Foo', value: 'foo' },
options: [ options: [
@@ -40,7 +40,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can accept an array of objects and pre-selected values (multiple)', () => { it('can accept an array of objects and pre-selected values (multiple)', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
modelValue: [ modelValue: [
{ label: 'This is Foo', value: 'foo' }, { label: 'This is Foo', value: 'foo' },
@@ -59,7 +59,7 @@ describe('VS - Selecting Values', () => {
it('can select an option on tab', () => { it('can select an option on tab', () => {
spy = vi.spyOn(typeAheadPointer.methods, 'typeAheadSelect') spy = vi.spyOn(typeAheadPointer.methods, 'typeAheadSelect')
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
selectOnTab: true, selectOnTab: true,
}, },
@@ -71,7 +71,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can deselect a pre-selected object', () => { it('can deselect a pre-selected object', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
multiple: true, multiple: true,
options: [ options: [
@@ -93,7 +93,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can deselect a pre-selected string', () => { it('can deselect a pre-selected string', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
multiple: true, multiple: true,
options: ['foo', 'bar'], options: ['foo', 'bar'],
@@ -107,7 +107,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can deselect an option when multiple is false', () => { it('can deselect an option when multiple is false', () => {
const Select = shallowMount(VueSelect) const Select = mount(VueSelect)
Select.vm.$data._value = 'foo' Select.vm.$data._value = 'foo'
@@ -116,7 +116,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can determine if the value prop is empty', () => { it('can determine if the value prop is empty', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
}, },
@@ -142,7 +142,7 @@ describe('VS - Selecting Values', () => {
}) })
it('should reset the selected values when the multiple property changes', () => { it('should reset the selected values when the multiple property changes', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
multiple: true, multiple: true,
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
@@ -157,7 +157,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can retain values present in a new array of options', () => { it('can retain values present in a new array of options', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
modelValue: ['one'], modelValue: ['one'],
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
@@ -169,7 +169,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can determine if an object is already selected', () => { it('can determine if an object is already selected', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
modelValue: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
@@ -195,7 +195,7 @@ describe('VS - Selecting Values', () => {
}) })
it('can check if a string value is selected when the value is an object and multiple is true', () => { it('can check if a string value is selected when the value is an object and multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { props: {
multiple: true, multiple: true,
modelValue: [{ label: 'foo', value: 'bar' }], modelValue: [{ label: 'foo', value: 'bar' }],
@@ -238,13 +238,13 @@ describe('VS - Selecting Values', () => {
describe('input Event', () => { describe('input Event', () => {
it('will trigger the input event when the selection changes', () => { it('will trigger the input event when the selection changes', () => {
const Select = shallowMount(VueSelect) const Select = mount(VueSelect)
Select.vm.select('bar') Select.vm.select('bar')
expect(Select.emitted('update:modelValue')[0]).toEqual(['bar']) expect(Select.emitted('update:modelValue')[0]).toEqual(['bar'])
}) })
it('will trigger the input event when the selection changes and multiple is true', () => { it('will trigger the input event when the selection changes and multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
props: { multiple: true, modelValue: ['foo'], options: ['foo', 'bar'] }, props: { multiple: true, modelValue: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.select('bar') Select.vm.select('bar')
@@ -252,7 +252,7 @@ describe('VS - Selecting Values', () => {
}) })
it('will not trigger the input event when multiple is true and selection is repeated', () => { it('will not trigger the input event when multiple is true and selection is repeated', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
value: ['foo ', 'bar'], value: ['foo ', 'bar'],
@@ -267,13 +267,13 @@ describe('VS - Selecting Values', () => {
describe('option:selecting Event', () => { describe('option:selecting Event', () => {
it('will trigger the option:selecting event when an option is selected', () => { it('will trigger the option:selecting event when an option is selected', () => {
const Select = shallowMount(VueSelect) const Select = mount(VueSelect)
Select.vm.select('bar') Select.vm.select('bar')
expect(Select.emitted('option:selecting')[0]).toEqual(['bar']) expect(Select.emitted('option:selecting')[0]).toEqual(['bar'])
}) })
it('will trigger the option:selecting event regardless of current value', () => { it('will trigger the option:selecting event regardless of current value', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { value: ['foo'], options: ['foo', 'bar'] }, propsData: { value: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.select('foo') Select.vm.select('foo')
@@ -282,7 +282,7 @@ describe('VS - Selecting Values', () => {
}) })
it('will trigger the option:selecting event with current selected item when multiple is true', () => { it('will trigger the option:selecting event with current selected item when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] }, propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.select('bar') Select.vm.select('bar')
@@ -290,7 +290,7 @@ describe('VS - Selecting Values', () => {
}) })
it('will trigger the option:selecting event regardless of current value when multiple is true', () => { it('will trigger the option:selecting event regardless of current value when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
value: ['foo', 'bar'], value: ['foo', 'bar'],
@@ -305,7 +305,7 @@ describe('VS - Selecting Values', () => {
describe('option:deselected Event', () => { describe('option:deselected Event', () => {
it('will trigger the option:deselected event when an option is deselected', () => { it('will trigger the option:deselected event when an option is deselected', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { value: ['foo'], options: ['foo', 'bar'] }, propsData: { value: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.deselect('foo') Select.vm.deselect('foo')
@@ -313,7 +313,7 @@ describe('VS - Selecting Values', () => {
}) })
it('will trigger the option:deselected event regardless of current value', () => { it('will trigger the option:deselected event regardless of current value', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { value: ['foo'], options: ['foo', 'bar'] }, propsData: { value: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.deselect('foo') Select.vm.deselect('foo')
@@ -322,7 +322,7 @@ describe('VS - Selecting Values', () => {
}) })
it('will trigger the option:selected event with current selected item when multiple is true', () => { it('will trigger the option:selected event with current selected item when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] }, propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.deselect('bar') Select.vm.deselect('bar')
@@ -330,7 +330,7 @@ describe('VS - Selecting Values', () => {
}) })
it('will trigger the option:selected event regardless of current value when multiple is true', () => { it('will trigger the option:selected event regardless of current value when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = mount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
value: ['foo', 'bar'], value: ['foo', 'bar'],