Skip to content

Commit 1ddf7e1

Browse files
committed
fix(select): handle optional chaining in clear method
1 parent 54dca27 commit 1ddf7e1

File tree

2 files changed

+37
-34
lines changed

2 files changed

+37
-34
lines changed

src/Select.spec.ts

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe("input + menu interactions behavior", () => {
6262
});
6363

6464
it("should not open the menu when is-disabled and an option is selected", async () => {
65-
const wrapper = mount(VueSelect, { props: { modelValue: options[0].value, options, isDisabled: true } });
65+
const wrapper = mount(VueSelect, { props: { modelValue: options[0]?.value, options, isDisabled: true } });
6666

6767
await openMenu(wrapper, "single-value");
6868

@@ -93,11 +93,11 @@ describe("menu keyboard navigation", () => {
9393
await openMenu(wrapper);
9494
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "ArrowDown" }));
9595

96-
expect(wrapper.get(".focused[role='option']").text()).toBe(options[1].label);
96+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[1]?.label);
9797

9898
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "ArrowUp" }));
9999

100-
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
100+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0]?.label);
101101
});
102102

103103
it("should navigate through the options with the arrow keys and skip disabled options", async () => {
@@ -112,11 +112,11 @@ describe("menu keyboard navigation", () => {
112112
await openMenu(wrapper);
113113
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "ArrowDown" }));
114114

115-
expect(wrapper.get(".focused[role='option']").text()).toBe(options[2].label);
115+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[2]?.label);
116116

117117
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "ArrowUp" }));
118118

119-
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
119+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0]?.label);
120120
});
121121
});
122122

@@ -142,8 +142,8 @@ describe("single-select option", () => {
142142
await openMenu(wrapper);
143143
await wrapper.get("div[role='option']").trigger("click");
144144

145-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value]]);
146-
expect(wrapper.get(".single-value").element.textContent).toBe(options[0].label);
145+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value]]);
146+
expect(wrapper.get(".single-value").element.textContent).toBe(options[0]?.label);
147147
});
148148

149149
it("should select an option when focusing and pressing enter", async () => {
@@ -152,8 +152,8 @@ describe("single-select option", () => {
152152
await openMenu(wrapper);
153153
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "Enter" }));
154154

155-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value]]);
156-
expect(wrapper.get(".single-value").element.textContent).toBe(options[0].label);
155+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value]]);
156+
expect(wrapper.get(".single-value").element.textContent).toBe(options[0]?.label);
157157
});
158158

159159
it("should select an option when pressing space", async () => {
@@ -169,8 +169,8 @@ describe("single-select option", () => {
169169

170170
await wrapper.vm.$nextTick();
171171

172-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value]]);
173-
expect(wrapper.get(".single-value").element.textContent).toBe(options[0].label);
172+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value]]);
173+
expect(wrapper.get(".single-value").element.textContent).toBe(options[0]?.label);
174174
});
175175

176176
it("should remove the selected option when pressing backspace without typing", async () => {
@@ -179,13 +179,13 @@ describe("single-select option", () => {
179179
await openMenu(wrapper);
180180
await wrapper.get("div[role='option']").trigger("click");
181181

182-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value]]);
183-
expect(wrapper.get(".single-value").element.textContent).toBe(options[0].label);
182+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value]]);
183+
expect(wrapper.get(".single-value").element.textContent).toBe(options[0]?.label);
184184

185185
await wrapper.get("input").trigger("mousedown");
186186
await dispatchEvent(wrapper, new KeyboardEvent("keydown", { key: "Backspace" }));
187187

188-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value], [undefined]]);
188+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value], [undefined]]);
189189
expect(wrapper.find(".single-value").exists()).toBe(false);
190190
});
191191

@@ -195,14 +195,14 @@ describe("single-select option", () => {
195195
await openMenu(wrapper);
196196
await wrapper.get("div[role='option']").trigger("click");
197197

198-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value]]);
199-
expect(wrapper.get(".single-value").element.textContent).toBe(options[0].label);
198+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value]]);
199+
expect(wrapper.get(".single-value").element.textContent).toBe(options[0]?.label);
200200

201201
await inputSearch(wrapper, "F");
202202
await wrapper.get("input").trigger("keydown", { key: "Backspace" });
203203

204-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value]]);
205-
expect(wrapper.get(".single-value").element.textContent).toBe(options[0].label);
204+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value]]);
205+
expect(wrapper.get(".single-value").element.textContent).toBe(options[0]?.label);
206206
});
207207

208208
it("cannot select an option when there are no matching options", async () => {
@@ -236,8 +236,8 @@ describe("multi-select options", () => {
236236
await openMenu(wrapper);
237237
await wrapper.get("div[role='option']").trigger("click");
238238

239-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[[options[0].value]]]);
240-
expect(wrapper.get(".multi-value").element.textContent).toBe(options[0].label);
239+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[[options[0]?.value]]]);
240+
expect(wrapper.get(".multi-value").element.textContent).toBe(options[0]?.label);
241241
});
242242

243243
it("should display non-selected remaining options on the list", async () => {
@@ -284,7 +284,7 @@ describe("clear button", () => {
284284
await wrapper.get("div[role='option']").trigger("click");
285285
await wrapper.get(".clear-button").trigger("click");
286286

287-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0].value], [undefined]]);
287+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[options[0]?.value], [undefined]]);
288288
expect(wrapper.find(".clear-button").exists()).toBe(false);
289289
});
290290

@@ -295,7 +295,7 @@ describe("clear button", () => {
295295
await wrapper.get("div[role='option']").trigger("click");
296296
await wrapper.get(".clear-button").trigger("click");
297297

298-
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[[options[0].value]], [[]]]);
298+
expect(wrapper.emitted("update:modelValue")).toStrictEqual([[[options[0]?.value]], [[]]]);
299299
expect(wrapper.find(".clear-button").exists()).toBe(false);
300300
});
301301
});
@@ -357,8 +357,8 @@ describe("component props", () => {
357357
await openMenu(wrapper);
358358

359359
const optionElements = wrapper.findAll("div[role='option']");
360-
expect(optionElements[0].text()).toBe("Admin");
361-
expect(optionElements[1].text()).toBe("User");
360+
expect(optionElements[0]?.text()).toBe("Admin");
361+
expect(optionElements[1]?.text()).toBe("User");
362362

363363
await wrapper.get("div[role='option']").trigger("click");
364364

@@ -561,7 +561,7 @@ describe("menu autofocus behavior", () => {
561561
// @ts-expect-error -- ignore type error
562562
const wrapper = mount(VueSelect, { props: testCase.props });
563563
await openMenu(wrapper);
564-
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
564+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0]?.label);
565565
}
566566
});
567567

@@ -623,7 +623,7 @@ describe("hideSelectedOptions prop", () => {
623623
await openMenu(wrapper);
624624

625625
expect(wrapper.findAll("div[role='option']").length).toBe(options.length - 1);
626-
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).not.toContain(options[0].label);
626+
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).not.toContain(options[0]?.label);
627627
});
628628

629629
it("should show selected options in menu when hideSelectedOptions is false", async () => {
@@ -634,7 +634,7 @@ describe("hideSelectedOptions prop", () => {
634634
await openMenu(wrapper);
635635

636636
expect(wrapper.findAll("div[role='option']").length).toBe(options.length);
637-
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).toContain(options[0].label);
637+
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).toContain(options[0]?.label);
638638
});
639639

640640
it("should show all options when in single-select mode regardless of hideSelectedOptions", async () => {
@@ -645,7 +645,7 @@ describe("hideSelectedOptions prop", () => {
645645
await openMenu(wrapper);
646646

647647
expect(wrapper.findAll("div[role='option']").length).toBe(options.length);
648-
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).toContain(options[0].label);
648+
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).toContain(options[0]?.label);
649649
});
650650

651651
it("should correctly restore hidden options when they are deselected", async () => {
@@ -658,15 +658,15 @@ describe("hideSelectedOptions prop", () => {
658658

659659
// Verify it's hidden from dropdown
660660
expect(wrapper.findAll("div[role='option']").length).toBe(options.length - 1);
661-
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).not.toContain(options[0].label);
661+
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).not.toContain(options[0]?.label);
662662

663663
// Remove the option
664664
await wrapper.get(".multi-value-remove").trigger("click");
665665
await openMenu(wrapper);
666666

667667
// Verify it's back in the dropdown
668668
expect(wrapper.findAll("div[role='option']").length).toBe(options.length);
669-
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).toContain(options[0].label);
669+
expect(wrapper.findAll("div[role='option']").map((option) => option.text())).toContain(options[0]?.label);
670670
});
671671

672672
it("should correctly filter options when searching with hideSelectedOptions enabled", async () => {
@@ -744,10 +744,10 @@ describe("exposed component methods and refs", () => {
744744
});
745745

746746
it("should expose clear method for single select", async () => {
747-
const wrapper = mount(VueSelect, { props: { modelValue: options[0].value, options } });
747+
const wrapper = mount(VueSelect, { props: { modelValue: options[0]?.value, options } });
748748

749749
expect(typeof wrapper.vm.clear).toBe("function");
750-
expect(wrapper.get(".single-value").text()).toBe(options[0].label);
750+
expect(wrapper.get(".single-value").text()).toBe(options[0]?.label);
751751

752752
wrapper.vm.clear();
753753
await wrapper.vm.$nextTick();
@@ -756,7 +756,7 @@ describe("exposed component methods and refs", () => {
756756
});
757757

758758
it("should expose clear method for multi select", async () => {
759-
const wrapper = mount(VueSelect, { props: { modelValue: [options[0].value], isMulti: true, options } });
759+
const wrapper = mount(VueSelect, { props: { modelValue: [options[0]?.value], isMulti: true, options } });
760760

761761
expect(typeof wrapper.vm.clear).toBe("function");
762762
expect(wrapper.find(".multi-value").exists()).toBe(true);

src/Select.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,10 @@ const clear = () => {
227227
}
228228
else {
229229
selected.value = undefined as OptionValue;
230-
emit("optionDeselected", selectedOptions.value[0]);
230+
231+
if (selectedOptions.value[0]) {
232+
emit("optionDeselected", selectedOptions.value[0]);
233+
}
231234
}
232235
233236
if (menuOpen.value) {

0 commit comments

Comments
 (0)