Skip to content

Commit 7e1a8a5

Browse files
authored
feat(select): add shouldAutofocusOption prop (#134)
1 parent 5bfb097 commit 7e1a8a5

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
@@ -298,6 +298,14 @@ describe("single-select option", () => {
298298

299299
expect(wrapper.emitted("update:modelValue")).toBeUndefined();
300300
});
301+
302+
it("should autofocus the first option when opening the menu, by default", async () => {
303+
const wrapper = mount(VueSelect, { props: { modelValue: null, options } });
304+
305+
await openMenu(wrapper);
306+
307+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
308+
});
301309
});
302310

303311
describe("multi-select options", () => {
@@ -336,6 +344,14 @@ describe("multi-select options", () => {
336344
expect(wrapper.findAll(".menu-option").length).toBe(options.length);
337345
expect(wrapper.findAll(".multi-value").length).toBe(0);
338346
});
347+
348+
it("should autofocus the first option when opening the menu, by default", async () => {
349+
const wrapper = mount(VueSelect, { props: { modelValue: [], isMulti: true, options } });
350+
351+
await openMenu(wrapper);
352+
353+
expect(wrapper.get(".focused[role='option']").text()).toBe(options[0].label);
354+
});
339355
});
340356

341357
describe("clear button", () => {
@@ -411,4 +427,12 @@ describe("component props", () => {
411427

412428
expect(wrapper.findAll("div[role='option']").length).toBe(options.length);
413429
});
430+
431+
it("should not autofocus an option when passing the autofocus prop", async () => {
432+
const wrapper = mount(VueSelect, { props: { modelValue: null, options, shouldAutofocusOption: false } });
433+
434+
await openMenu(wrapper);
435+
436+
expect(wrapper.findAll(".focused[role='option']")).toHaveLength(0);
437+
});
414438
});

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)