mirror of
https://github.com/tenrok/vue-select.git
synced 2026-05-17 02:29:37 +03:00
Allow to disable options with selectable function (#921)
* allow to disable options with selectable function * add simple spec for new selectable option * Prevent non-selectable options from being keyboard navigatable
This commit is contained in:
@@ -57,9 +57,9 @@
|
||||
v-for="(option, index) in filteredOptions"
|
||||
:key="getOptionKey(option)"
|
||||
class="vs__dropdown-option"
|
||||
:class="{ 'vs__dropdown-option--selected': isOptionSelected(option), 'vs__dropdown-option--highlight': index === typeAheadPointer }"
|
||||
@mouseover="typeAheadPointer = index"
|
||||
@mousedown.prevent.stop="select(option)"
|
||||
:class="{ 'vs__dropdown-option--selected': isOptionSelected(option), 'vs__dropdown-option--highlight': index === typeAheadPointer, 'vs__dropdown-option--disabled': !selectable(option) }"
|
||||
@mouseover="selectable(option) ? typeAheadPointer = index : null"
|
||||
@mousedown.prevent.stop="selectable(option) ? select(option) : null"
|
||||
>
|
||||
<slot name="option" v-bind="normalizeOptionForSlot(option)">
|
||||
{{ getOptionLabel(option) }}
|
||||
@@ -224,6 +224,19 @@
|
||||
default: option => option,
|
||||
},
|
||||
|
||||
/**
|
||||
* Decides wether an option is selectable or not. Not selectable options
|
||||
* are displayed but disabled and cannot be selected.
|
||||
*
|
||||
* @type {Function}
|
||||
* @param {Object|String} option
|
||||
* @return {Boolean}
|
||||
*/
|
||||
selectable: {
|
||||
type: Function,
|
||||
default: option => true,
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback to generate the label text. If {option}
|
||||
* is an object, returns option[this.label] by default.
|
||||
|
||||
@@ -7,35 +7,46 @@ export default {
|
||||
|
||||
watch: {
|
||||
filteredOptions() {
|
||||
this.typeAheadPointer = 0
|
||||
for (let i = 0; i < this.filteredOptions.length; i++) {
|
||||
if (this.selectable(this.filteredOptions[i])) {
|
||||
this.typeAheadPointer = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Move the typeAheadPointer visually up the list by
|
||||
* subtracting the current index by one.
|
||||
* setting it to the previous selectable option.
|
||||
* @return {void}
|
||||
*/
|
||||
typeAheadUp() {
|
||||
if (this.typeAheadPointer > 0) {
|
||||
this.typeAheadPointer--
|
||||
if( this.maybeAdjustScroll ) {
|
||||
this.maybeAdjustScroll()
|
||||
for (let i = this.typeAheadPointer - 1; i >= 0; i--) {
|
||||
if (this.selectable(this.filteredOptions[i])) {
|
||||
this.typeAheadPointer = i;
|
||||
if( this.maybeAdjustScroll ) {
|
||||
this.maybeAdjustScroll()
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Move the typeAheadPointer visually down the list by
|
||||
* adding the current index by one.
|
||||
* setting it to the next selectable option.
|
||||
* @return {void}
|
||||
*/
|
||||
typeAheadDown() {
|
||||
if (this.typeAheadPointer < this.filteredOptions.length - 1) {
|
||||
this.typeAheadPointer++
|
||||
if( this.maybeAdjustScroll ) {
|
||||
this.maybeAdjustScroll()
|
||||
for (let i = this.typeAheadPointer + 1; i < this.filteredOptions.length; i++) {
|
||||
if (this.selectable(this.filteredOptions[i])) {
|
||||
this.typeAheadPointer = i;
|
||||
if( this.maybeAdjustScroll ) {
|
||||
this.maybeAdjustScroll()
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -16,3 +16,12 @@
|
||||
background: $vs-state-active-bg;
|
||||
color: $vs-state-active-color;
|
||||
}
|
||||
|
||||
.vs__dropdown-option--disabled {
|
||||
background: inherit;
|
||||
color: $vs-state-disabled-color;
|
||||
|
||||
&:hover {
|
||||
cursor: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { selectWithProps } from "../helpers";
|
||||
|
||||
describe("Selectable prop", () => {
|
||||
it("should select selectable option if clicked", () => {
|
||||
const Select = selectWithProps({
|
||||
options: ["one", "two", "three"],
|
||||
selectable: (option) => option == "one"
|
||||
});
|
||||
|
||||
Select.vm.$data.open = true;
|
||||
|
||||
Select.find(".vs__dropdown-menu li:first-child").trigger("mousedown");
|
||||
expect(Select.vm.selectedValue).toEqual(["one"]);
|
||||
})
|
||||
|
||||
it("should not select not selectable option if clicked", () => {
|
||||
const Select = selectWithProps({
|
||||
options: ["one", "two", "three"],
|
||||
selectable: (option) => option == "one"
|
||||
});
|
||||
|
||||
Select.vm.$data.open = true;
|
||||
|
||||
Select.find(".vs__dropdown-menu li:last-child").trigger("mousedown");
|
||||
expect(Select.vm.selectedValue).toEqual([]);
|
||||
});
|
||||
|
||||
it("should skip non-selectable option on down arrow keyUp", () => {
|
||||
const Select = selectWithProps({
|
||||
options: ["one", "two", "three"],
|
||||
selectable: (option) => option !== "two"
|
||||
});
|
||||
|
||||
Select.vm.typeAheadPointer = 1;
|
||||
|
||||
Select.find({ ref: "search" }).trigger("keyup.down");
|
||||
|
||||
expect(Select.vm.typeAheadPointer).toEqual(2);
|
||||
})
|
||||
|
||||
it("should skip non-selectable option on up arrow keyUp", () => {
|
||||
const Select = selectWithProps({
|
||||
options: ["one", "two", "three"],
|
||||
selectable: (option) => option !== "two"
|
||||
});
|
||||
|
||||
Select.vm.typeAheadPointer = 2;
|
||||
|
||||
Select.find({ ref: "search" }).trigger("keyup.up");
|
||||
|
||||
expect(Select.vm.typeAheadPointer).toEqual(0);
|
||||
})
|
||||
})
|
||||
@@ -18,7 +18,7 @@ describe("Moving the Typeahead Pointer", () => {
|
||||
expect(Select.vm.typeAheadPointer).toEqual(0);
|
||||
});
|
||||
|
||||
it("should move the pointer visually up the list on up arrow keyDown", () => {
|
||||
it("should move the pointer visually up the list on up arrow keyUp", () => {
|
||||
const Select = mountDefault();
|
||||
|
||||
Select.vm.typeAheadPointer = 1;
|
||||
@@ -28,7 +28,7 @@ describe("Moving the Typeahead Pointer", () => {
|
||||
expect(Select.vm.typeAheadPointer).toEqual(0);
|
||||
});
|
||||
|
||||
it("should move the pointer visually down the list on down arrow keyDown", () => {
|
||||
it("should move the pointer visually down the list on down arrow keyUp", () => {
|
||||
const Select = mountDefault();
|
||||
|
||||
Select.vm.typeAheadPointer = 1;
|
||||
@@ -47,7 +47,7 @@ describe("Moving the Typeahead Pointer", () => {
|
||||
});
|
||||
|
||||
describe("Automatic Scrolling", () => {
|
||||
it("should check if the scroll position needs to be adjusted on up arrow keyDown", () => {
|
||||
it("should check if the scroll position needs to be adjusted on up arrow keyUp", () => {
|
||||
const Select = mountDefault();
|
||||
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
|
||||
|
||||
@@ -57,7 +57,7 @@ describe("Moving the Typeahead Pointer", () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should check if the scroll position needs to be adjusted on down arrow keyDown", () => {
|
||||
it("should check if the scroll position needs to be adjusted on down arrow keyUp", () => {
|
||||
const Select = mountDefault();
|
||||
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user