Skip to content

Commit 70c964a

Browse files
committed
feat(select): add shouldAutofocusOption prop
1 parent bb2736b commit 70c964a

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

docs/props.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ Whether the select should allow multiple selections. If `true`, the `v-model` sh
122122

123123
Whether the select should display a loading state. When `true`, the select will show a loading spinner or custom loading content provided via the `loading` slot.
124124

125+
## shouldAutofocusOption
126+
127+
**Type**: `boolean`
128+
129+
**Default**: `true`
130+
131+
Whether the first option should be focused when the dropdown is opened. If set to `false`, the first option will not be focused, and the user will have to navigate through the options using the keyboard.
132+
125133
## closeOnSelect
126134

127135
**Type**: `boolean`

src/Select.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ describe("single-select option", () => {
286286

287287
expect(wrapper.emitted("update:modelValue")).toBeUndefined();
288288
});
289+
290+
it("should autofocus the first option when opening the menu, by default", async () => {
291+
const wrapper = mount(VueSelect, { props: { modelValue: null, options } });
292+
293+
await openMenu(wrapper);
294+
295+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
296+
});
289297
});
290298

291299
describe("multi-select options", () => {
@@ -324,6 +332,14 @@ describe("multi-select options", () => {
324332
expect(wrapper.findAll(".menu-option").length).toBe(options.length);
325333
expect(wrapper.findAll(".multi-value").length).toBe(0);
326334
});
335+
336+
it("should autofocus the first option when opening the menu, by default", async () => {
337+
const wrapper = mount(VueSelect, { props: { modelValue: [], isMulti: true, options } });
338+
339+
await openMenu(wrapper);
340+
341+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
342+
});
327343
});
328344

329345
describe("clear button", () => {
@@ -399,4 +415,12 @@ describe("component props", () => {
399415

400416
expect(wrapper.findAll("div[role='option']").length).toBe(options.length);
401417
});
418+
419+
it("should not autofocus an option when passing the autofocus prop", async () => {
420+
const wrapper = mount(VueSelect, { props: { modelValue: null, options, shouldAutofocusOption: false } });
421+
422+
await openMenu(wrapper);
423+
424+
expect(wrapper.findAll(".focused[role='option']")).toHaveLength(0);
425+
});
402426
});

src/Select.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ const props = withDefaults(
4242
* when fetching the options asynchronously.
4343
*/
4444
isLoading?: boolean;
45+
/**
46+
* When set to true, focus the first option when the menu is opened.
47+
* When set to false, no option will be focused.
48+
*/
49+
shouldAutofocusOption?: boolean;
4550
/**
4651
* When set to true, clear the search input when an option is selected.
4752
*/
@@ -98,6 +103,7 @@ const props = withDefaults(
98103
isSearchable: true,
99104
isMulti: false,
100105
isLoading: false,
106+
shouldAutofocusOption: true,
101107
closeOnSelect: true,
102108
teleport: undefined,
103109
inputId: undefined,
@@ -171,7 +177,10 @@ const selectedOptions = computed(() => {
171177
172178
const openMenu = (options?: { focusInput?: boolean }) => {
173179
menuOpen.value = true;
174-
focusedOption.value = props.options.findIndex((option) => !option.disabled);
180+
181+
if (props.shouldAutofocusOption) {
182+
focusedOption.value = props.options.findIndex((option) => !option.disabled);
183+
}
175184
176185
if (options?.focusInput && input.value) {
177186
input.value.focus();

0 commit comments

Comments
 (0)