Skip to content

Commit 235ea69

Browse files
committed
fix(select): backspace removes last selected option in multi mode
See #296
1 parent ca78c88 commit 235ea69

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

src/Menu.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,20 @@ const handleNavigation = (e: KeyboardEvent) => {
102102
if (e.key === "Backspace" && sharedData.search.value.length === 0 && hasSelectedValue) {
103103
e.preventDefault();
104104
105-
if (sharedProps.isMulti && Array.isArray(selected.value)) {
106-
selected.value = selected.value.slice(0, -1);
105+
if (sharedProps.isMulti) {
106+
const selectedOptions = sharedData.selectedOptions.value;
107+
const lastOption = selectedOptions[selectedOptions.length - 1];
108+
109+
if (lastOption) {
110+
sharedData.removeOption(lastOption);
111+
}
107112
}
108113
else {
109-
selected.value = undefined as OptionValue;
114+
const selectedOption = sharedData.selectedOptions.value[0];
115+
116+
if (selectedOption) {
117+
sharedData.removeOption(selectedOption);
118+
}
110119
}
111120
}
112121
}

src/Select.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,67 @@ describe("multi-select options", () => {
276276
});
277277
});
278278

279+
describe("option-deselected event", () => {
280+
it("should emit option-deselected when removing a tag via click in multi-select", async () => {
281+
const wrapper = mount(VueSelect, { props: { modelValue: [], isMulti: true, options } });
282+
283+
await openMenu(wrapper);
284+
await wrapper.get("div[role='option']").trigger("click");
285+
286+
expect(wrapper.emitted("optionDeselected")).toBeUndefined();
287+
288+
await wrapper.get(".multi-value-remove").trigger("click");
289+
290+
const emittedEvents = wrapper.emitted("optionDeselected");
291+
expect(emittedEvents).toBeDefined();
292+
expect(emittedEvents?.[0]?.[0]).toEqual(options[0]);
293+
});
294+
295+
it("should emit option-deselected when removing a tag via backspace in multi-select", async () => {
296+
const wrapper = mount(VueSelect, { props: { modelValue: [], isMulti: true, options } });
297+
298+
await openMenu(wrapper);
299+
await wrapper.get("div[role='option']").trigger("click");
300+
301+
expect(wrapper.emitted("optionDeselected")).toBeUndefined();
302+
303+
await wrapper.get("input").trigger("mousedown");
304+
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "Backspace" }));
305+
306+
const emittedEvents = wrapper.emitted("optionDeselected");
307+
expect(emittedEvents).toBeDefined();
308+
expect(emittedEvents?.[0]?.[0]).toEqual(options[0]);
309+
});
310+
311+
it("should emit option-deselected when removing via backspace in single-select", async () => {
312+
const wrapper = mount(VueSelect, { props: { modelValue: null, options } });
313+
314+
await openMenu(wrapper);
315+
await wrapper.get("div[role='option']").trigger("click");
316+
317+
expect(wrapper.emitted("optionDeselected")).toBeUndefined();
318+
319+
await wrapper.get("input").trigger("mousedown");
320+
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "Backspace" }));
321+
322+
const emittedEvents = wrapper.emitted("optionDeselected");
323+
expect(emittedEvents).toBeDefined();
324+
expect(emittedEvents?.[0]?.[0]).toEqual(options[0]);
325+
});
326+
327+
it("should emit option-deselected with null when clearing all options in multi-select", async () => {
328+
const wrapper = mount(VueSelect, { props: { modelValue: [], isMulti: true, options, isClearable: true } });
329+
330+
await openMenu(wrapper);
331+
await wrapper.get("div[role='option']").trigger("click");
332+
await wrapper.get(".clear-button").trigger("click");
333+
334+
const emittedEvents = wrapper.emitted("optionDeselected");
335+
expect(emittedEvents).toBeDefined();
336+
expect(emittedEvents?.[0]?.[0]).toBeNull();
337+
});
338+
});
339+
279340
describe("clear button", () => {
280341
it("should display the clear button when an option is selected", async () => {
281342
const wrapper = mount(VueSelect, { props: { modelValue: null, options, isClearable: true } });

src/Select.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,11 @@ const setOption = (option: GenericOption) => {
229229
};
230230
231231
const removeOption = (option: GenericOption) => {
232-
if (props.isMulti && !props.isDisabled) {
232+
if (props.isDisabled) {
233+
return;
234+
}
235+
236+
if (props.isMulti) {
233237
if (Array.isArray(selected.value)) {
234238
selected.value = selected.value.filter((value) => value !== props.getOptionValue(option));
235239
emit("optionDeselected", option);
@@ -238,6 +242,10 @@ const removeOption = (option: GenericOption) => {
238242
console.warn(`[vue3-select-component warn]: The v-model provided should be an array when using \`isMulti\` prop, instead it was: ${selected.value}`);
239243
}
240244
}
245+
else {
246+
selected.value = undefined as OptionValue;
247+
emit("optionDeselected", option);
248+
}
241249
};
242250
243251
const clear = () => {

0 commit comments

Comments
 (0)