Skip to content

[Bug Report][4.1.10] focus() is not exposed on VCheckbox, VRadioGroup, VSwitch or VColorInput #23147

Description

@johnleider

Environment

Vuetify Version: 4.1.10
Vue Version: 3.x
OS: Not specified

Steps to reproduce

  1. Put a template ref on a v-checkbox, v-radio-group, v-switch or v-color-input
  2. Call ref.value.focus()
<template>
  <v-checkbox ref="checkbox" label="VCheckbox" />
</template>

<script setup>
  import { useTemplateRef, onMounted } from 'vue'

  const checkbox = useTemplateRef('checkbox')

  onMounted(() => checkbox.value.focus())
</script>

Expected Behavior

The control receives focus, as it does on every sibling input — VTextField, VTextarea, VFileInput, VSelect, VAutocomplete, VCombobox, VNumberInput, VDateInput, VSlider, VRangeSlider and VOtpInput all expose a working focus().

Actual Behavior

TypeError: focus is not a function — the method does not exist.

Probed in a real browser against 4.1.10:

[VCheckbox]   typeof focus = undefined | THREW TypeError | exposes: reset, validate, resetValidation, isValid
[VRadioGroup] typeof focus = undefined | THREW TypeError | exposes: reset, validate, resetValidation, isValid
[VSwitch]     typeof focus = undefined | THREW TypeError | exposes: reset, validate, resetValidation, isValid
[VColorInput] typeof focus = undefined | THREW TypeError | exposes: NOTHING
[VTextField]  typeof focus = function  | ok, activeElement = <input type=text> | exposes: focus, blur, reset, validate, resetValidation, isValid

Reproduction Link

https://play.vuetifyjs.com/#...

Other comments

Reported by @userquin in Discord; filed on their behalf. Four separate defects, three sharing one cause.

1–3. VCheckbox, VRadioGroup, VSwitch forward refs to a dead end.

All three end with:

return forwardRefs({}, inputRef)   // inputRef is the VInput wrapper

and VInput's setup returns only { reset, resetValidation, validate, isValid, errorMessages } — no focus, no element. So there is nothing for focus() to resolve to. That matches the probe exactly: the four validation members are present, focus/blur are not.

The fix has a precedent in-repo — VSlider/VRangeSlider:

return forwardRefs({ focus: () => thumbContainerRef.value?.$el.focus() }, inputRef)

For checkbox/switch no DOM query is needed: VSelectionControl already returns { isFocused, input }, where input is the native <input>. VRadioGroup needs group semantics — focus the checked radio, falling back to the first, since that is the group's tab stop.

4. VColorInput exposes nothing at all. Its setup (VColorInput.tsx:76) ends after useRender with no return statement and the file contains no refs, so it wraps a VTextField and discards that component's entire public API — not only focus() but reset(), validate(), resetValidation(), isValid and errorMessages. VDateInput and VNumberInput are the same wrapper shape and do forwardRefs({}, vTextFieldRef); one line would fix all of it.

Related: VRadioGroup does not emit update:focused. VCheckbox (:44) and VSwitch (:87) both declare it; VRadioGroup does not, so there is no way to observe its focus state either.

Related: programmatic focus cannot restore the focus ring. VSelectionControl gates .v-selection-control--focus-visible (VSelectionControl.sass:96) on the JS flag isFocusVisible (VSelectionControl.tsx:267), which is only ever set inside onFocus (:192-199). Calling .focus() on an already-focused input fires no focus event, so the ring never reappears — even with focus({ focusVisible: true }). If the new focus() accepts FocusOptions, it should set isFocusVisible directly when options.focusVisible is true rather than infer it from the event. The browser heuristic should stay in place for user-initiated focus; this is only about making an explicit programmatic call authoritative.

Source references

  • packages/vuetify/src/components/VCheckbox/VCheckbox.tsx:101
  • packages/vuetify/src/components/VRadioGroup/VRadioGroup.tsx:139
  • packages/vuetify/src/components/VSwitch/VSwitch.tsx:303
  • packages/vuetify/src/components/VColorInput/VColorInput.tsx:76
  • packages/vuetify/src/components/VInput/VInput.tsx:263-269
  • packages/vuetify/src/components/VSelectionControl/VSelectionControl.tsx:192-199, :267, :330
  • packages/vuetify/src/components/VSlider/VSlider.tsx — the pattern to copy

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions