Skip to content

Can’t differentiate between focus coming outside and focus coming after the click by option #1458

@helgablazhkun

Description

@helgablazhkun

One of the most straightforward use case is to automatically open the dropdown component on focus if the operation has been initiated from the outside.

Power Select Focus handling documentation says that we can check the 'event.relatedTarget' to know the element that had the focus before. Code example suggests the following:

handleFocus(select, e) {
    console.debug('EPS focused!');
    if (this.focusComesFromOutside(e)) {
      select.actions.open();
    }
  }
 
  // Methods
  focusComesFromOutside(e) {
    let blurredEl = e.relatedTarget;
    if (isBlank(blurredEl)) {
      return false;
    }
    return !blurredEl.classList.contains('ember-power-select-search-input');
  }

If the relatedTarget is not null then we can assume that focus comes from outside and trigger dropdown open event.

But the weak part comes into play when the user focuses dropdown after the non-focusable element (using Tab key)
or when dropdown is the first element in the form. In this case the relatedTarget will be null. And dropdown element won't be opened.
Also when the user picks the dropdown option from the list, the input is refocused. The relatedTarget will be `null as well.
So We can’t distinguish between the cases when the focus comes outside to the dropdown trigger when there was no focused element and when it comes after clicking the dropdown option.

Please look at the twiddle example:

  1. Press 'tab' key focus on city-picker
    Expected result: dropdown is opened. Actual result: dropdown is not opened.
  2. Press 'tab' key to focus on the next input
  3. Press 'shift+tab' key to return back to the city-picker
    Dropdown is opened now.
    Behaviour is not consistent.

I suggest the following solution:
Make dropdown option as focusable element by setting "-1" tabindex. On blur/focus events check the 'event.relatedTarget' to identify exactly that we faced with inner interaction (not the outside element)

function relatedTargetIsTimeOption(relatedTarget: EventTarget | null): boolean {
  if (!relatedTarget) {
    return false;
  }

  return (relatedTarget as HTMLElement).classList.contains('ember-power-select-option');
}

And use it in blur/focus events:

@action
 onFocus(dropdown: Select, event: FocusEvent): void {
   if (!relatedTargetIsTimeOption(event.relatedTarget)) {
     dropdown.actions.open();
   }
 }
 
 @action
 onBlur(_dropdown: Select, event: FocusEvent): void {
   if (relatedTargetIsTimeOption(event.relatedTarget)) {
     return;
   }
 // do required actions on blur
 }

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions