telekom / scale

Scale is the digital design system for Telekom products and experiences.
https://telekom.github.io/scale/
Other
374 stars 82 forks source link

how to focus a scale-dropdown-select from javascript/vue #2060

Open rbartholomay opened 1 year ago

rbartholomay commented 1 year ago

Scale Version ^3.0.0-beta.134

Framework and version Quasar v2.12.2 (Vite v1.4.3) - VueJS3

Code Reproduction

  <scale-dropdown-select
    ref="scaleDropdownSelectCmp"
    id="myCmp"
    :label="label"
    :value="value"
    :helper-text="helperText"
    :invalid="invalid"
    :disabled="disabled"
    :size="size"
    :variant="variant"
    :combobox-id="comboboxId"
    @scale-change="dispatchEvent"
    @scale-focus="dispatchEvent"
    @scale-blur="dispatchEvent"
    @scale-keydown="dispatchEvent"
  >
    <scale-dropdown-select-item
      v-for="item in options"
      :key="item[props.optionValue]"
      :value="item[props.optionValue]"
    >
      {{ item[props.optionLabel] }}
    </scale-dropdown-select-item>
  </scale-dropdown-select>

Desktop (please complete the following information):

Additional context The code above runs fine! I can handle all events and data. But i need a way to setup the focus manualy. So i try following:

onMounted(async () => {
  // set focus to Location
  locationComboCmp.value?.setFocus();
});

If i use a quasar component the component will be visually focused. When using the scale component this will not work, also when i try document.getElementByIf('myCmp").focus().

How can i focus an scale-element from JavaScript/Vue?

[edit] Just found in the codes that some components have the undocumented method setFoscus(). So when the method is available I can call locationComboCmp.value?.$el.setFocus(); Must I wait for updated scale-drop-down-select or is there any other way?

Darkavid commented 1 year ago

TLDR

I had the same problem but found a workaround so now I'm also waiting for a proper fix from the devs.

Probable reason

I think the problem is that the delegatesFocus property for the host element scale-dropdown-select is set to false, therefore it does not delegate the focus event into its inner shadow DOM where the tabindex prop is assigned to the relevant div element. If so, then this is not a support ticket but rather an actual bug.

Workaround

If you face an issue like this, you can always programmatically reach inside the shadow DOM to do the necessary changes. There are four steps necessary here:

  1. Create a simple wrapper div around your scale-dropdown-select
  2. Create a focus handling function
  3. Assign the focus handling function to your wrapper div
  4. Call the focus on the wrapper programmatically

NOTE: The wrapper div is necessary because Scale web-components does not work with Vue events like @focus and the @scale-focus is called only when the inner element gets focused so it does not help.

I won't copy-paste my exact solution but here is a simplified example code you can work with (you can use refs too instead of getElementById if you prefer):

<script setup lang="ts">

const handleFocus = () => {
  const dropdownElement = document.getElementById("my-dropdown-1")
  if (dropdownElement) {
    const shadowRoot = dropdownElement.shadowRoot
    if (shadowRoot) {
       const comboboxElement = document.getElementById("my-combobox-1")
       if (comboboxElement) {
         comboboxElement.focus()  // this is where the real focus happens in this webcomponent
       } else {
          console.log("Could not find combobox element inside shadow root.")
       }
    } else {
        console.log("Could not find shadow root under dropdown element.")
    }
  } else {
    console.log("Could not find dropdown element.") 
  }
}

const doFocus = () => {
  const wrapperElement = document.getElementById('dropdown-wrapper')
  if (wrapperElement) {
    wrapperElement.focus()  // this calls the `@focus` event of our wrapper `div`
  }
}

</script>

<template>
    <div id="dropdown-wrapper" @focus="handleFocus">
      <scale-dropdown-select
        id="my-dropdown-1"
        comboboxId="my-combobox-1"
      >
      <!-- options -->
     </scale-dropdown-select>
    </div>
    <button @click="doFocus">Test focus</focus>
</template>