diff --git a/src/components/Select.vue b/src/components/Select.vue index 281ec7c..adcd6a4 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -409,12 +409,17 @@ }, /** - * When false, updating the options will not reset the select value - * @type {Boolean} + * When false, updating the options will not reset the selected value. Accepts + * a `boolean` or `function` that returns a `boolean`. If defined as a function, + * it will receive the params listed below. + * @type {Boolean|Function} + * @param {Array} newOptions + * @param {Array} oldOptions + * @param {Array} selectedValue */ resetOnOptionsChange: { - type: Boolean, - default: false + default: false, + validator: (value) => ['function', 'boolean'].includes(typeof value) }, /** @@ -513,13 +518,17 @@ * is correct. * @return {[type]} [description] */ - options(val) { - if (!this.taggable && this.resetOnOptionsChange) { - this.clearSelection() + options (newOptions, oldOptions) { + let shouldReset = () => typeof this.resetOnOptionsChange === 'function' + ? this.resetOnOptionsChange(newOptions, oldOptions, this.selectedValue) + : this.resetOnOptionsChange; + + if (!this.taggable && shouldReset()) { + this.clearSelection(); } if (this.value && this.isTrackingValues) { - this.setInternalValueFromOptions(this.value) + this.setInternalValueFromOptions(this.value); } }, diff --git a/tests/unit/ReactiveOptions.spec.js b/tests/unit/ReactiveOptions.spec.js index 04b6a91..efefd37 100755 --- a/tests/unit/ReactiveOptions.spec.js +++ b/tests/unit/ReactiveOptions.spec.js @@ -1,5 +1,6 @@ -import { shallowMount } from "@vue/test-utils"; +import { mount, shallowMount } from '@vue/test-utils'; import VueSelect from "../../src/components/Select"; +import { mountDefault } from '../helpers'; describe("Reset on options change", () => { it("should not reset the selected value by default when the options property changes", () => { @@ -13,6 +14,73 @@ describe("Reset on options change", () => { expect(Select.vm.selectedValue).toEqual(["one"]); }); + describe('resetOnOptionsChange as a function', () => { + it('will yell at you if resetOnOptionsChange is not a function or boolean', () => { + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + mountDefault({resetOnOptionsChange: 1}); + expect(spy.mock.calls[0][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') + + mountDefault({resetOnOptionsChange: 'one'}); + expect(spy.mock.calls[1][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') + + mountDefault({resetOnOptionsChange: []}); + expect(spy.mock.calls[2][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') + + mountDefault({resetOnOptionsChange: {}}); + expect(spy.mock.calls[3][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') + }); + + it('should receive the new options, old options, and current value', () => { + let resetOnOptionsChange = jest.fn(option => option); + const Select = mountDefault( + {resetOnOptionsChange, options: ['bear'], value: 'selected'}, + ); + + Select.setProps({options: ['lake', 'kite']}); + + expect(resetOnOptionsChange).toHaveBeenCalledTimes(1); + expect(resetOnOptionsChange) + .toHaveBeenCalledWith(['lake', 'kite'], ['bear'], ['selected']); + }); + + it('should allow resetOnOptionsChange to be a function that returns true', () => { + let resetOnOptionsChange = () => true; + const Select = shallowMount(VueSelect, { + propsData: {resetOnOptionsChange, options: ['one'], value: 'one'}, + }); + const spy = jest.spyOn(Select.vm, 'clearSelection'); + + Select.setProps({options: ['one', 'two']}); + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should allow resetOnOptionsChange to be a function that returns false', () => { + let resetOnOptionsChange = () => false; + const Select = shallowMount(VueSelect, { + propsData: {resetOnOptionsChange, options: ['one'], value: 'one'}, + }); + const spy = jest.spyOn(Select.vm, 'clearSelection'); + + Select.setProps({options: ['one', 'two']}); + expect(spy).not.toHaveBeenCalled(); + }); + + it('should reset the options if the selectedValue does not exist in the new options', async () => { + let resetOnOptionsChange = (options, old, val) => val.some(val => options.includes(val)); + const Select = shallowMount(VueSelect, { + propsData: {resetOnOptionsChange, options: ['one'], value: 'one'}, + }); + const spy = jest.spyOn(Select.vm, 'clearSelection'); + + Select.setProps({options: ['one', 'two']}); + expect(Select.vm.selectedValue).toEqual(['one']); + + Select.setProps({options: ['two']}); + expect(spy).toHaveBeenCalledTimes(1); + }); + }); + it("should reset the selected value when the options property changes", () => { const Select = shallowMount(VueSelect, { propsData: { resetOnOptionsChange: true, options: ["one"] }