Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to disable options with selectable function #921

Merged
merged 4 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions src/components/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
v-for="(option, index) in filteredOptions"
:key="index"
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) }}
Expand Down Expand Up @@ -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.
Expand Down
33 changes: 22 additions & 11 deletions src/mixins/typeAheadPointer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
},
Expand Down
9 changes: 9 additions & 0 deletions src/scss/modules/_dropdown-option.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
53 changes: 53 additions & 0 deletions tests/unit/Selectable.spec.js
Original file line number Diff line number Diff line change
@@ -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);
})
})
8 changes: 4 additions & 4 deletions tests/unit/TypeAhead.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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");

Expand All @@ -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");

Expand Down