JedWatson / react-select

The Select Component for React.js
https://react-select.com/
MIT License
27.68k stars 4.13k forks source link

Programmatically focus select component #856

Closed radar closed 4 years ago

radar commented 8 years ago

I'm looking to write an integration test for a form that uses a react-select component. This is the Ruby code that I have for it now:

find(".Select").click
find(".Select .Select-input input").native.send_key("Text goes here")
find(".Select-option", text: option_value.presentation).click

I would believe this would:

  1. Open the input field
  2. Send the phrase "text goes here" into the field.
  3. Find the option that matches that text, and then click it.

However, I'm getting this error in my tests:

Capybara::Poltergeist::MouseEventFailed:
       Firing a click at co-ordinates [356.5, 384] failed. Poltergeist detected another element with CSS selector 'html.js.touch body#en-AU-be.loggedin div.wrapper div.main div div.form--flat div.AdvertForm div.DisplayPanel div div.row div#option_type_wrapper_1.span6.error span.select div.Select.is-focused.is-open.is-searchable div.Select-control div.Select-placeholder' at this position. It may be overlapping the element you are trying to interact with. If you don't care about overlapping elements, try using node.trigger('click').

I would consider documentation for how to integration test a react-select (even if it is with some jQuery functions) would be something that this library should provide. I am considering this a "bug" with the documentation for this component, which is why I'm filing this issue.

Please add some examples of programatically triggering / filling in this element with jQuery.

mipearson commented 8 years ago

I've also looked at this, I work with Ryan.

The problem is that the placeholder element sits above the input element.

Sending keys to the placeholder element crashes PhantomJS 2.0 and 2.1, so that won't work.

Setting the value of the input element does not do anything. Using send_keys used to work, but no longer does.

I consider this a bug with react-select and not our testing as react-select is doing strange things to do the DOM that break how input elements are supposed to work.

mipearson commented 8 years ago

Oh, and we're using 1.0.0-beta5

rzane commented 8 years ago

Anybody figure this out?

mipearson commented 8 years ago

Unfortunately, we ended up rolling our own autocomplete instead.

rzane commented 8 years ago

I think I figured it out. I think it's something like:

$('.Select-input input').trigger('mousedown');
$('div.Select-option').first().trigger('mousedown');
ilantc commented 8 years ago

From what I saw when firing mousedown on the select the input.focus() is called, but the render is not (until I actually click on the screen). It seems that setState is not being called when programmatically clicking the select. Is there a way to fix this?

AlanFoster commented 8 years ago

@radar Can confirm this is awkward using Selenium too -

Selenium::WebDriver::Error::UnknownError: Element is not clickable at point (341, 45). Other element would receive the click: <div class="Select-placeholder" data-reactid=".0.1.1.0:$placeholder"></div>

The overlaying of elements is confusing the web driver it seems.

For what it's worth, this works completely fine with Capybara and the javascript_driver set to webkit, using the rack_test web driver. I'm not sure if you're in a position to switch that configuration though, but it helped unblock me.

timsheng commented 7 years ago

@ilantc have you found a solution to fix that issue?

ilantc commented 7 years ago

@timsheng What we ended up doing is firing focus on the hidden input before dispatching the click events on the select. Not ideal, but it works

liorbentov commented 7 years ago

@ilantc, can you write here that piece of code that worked?

AlanFoster commented 7 years ago

@liorbentov If you're using the react-addons-test-utils, something similar to:

const input = yourElement.querySelector('input');
TestUtils.Simulate.focus(input);

const selectArrow = yourElement.querySelector('.Select-arrow');
TestUtils.Simulate.mouseDown(selectArrow, { button: 0 });

It is possible to use enzyme etc, the important thing is that you need to focus on the input, then mouse down on the Select-arrow with button 0 set.

liorbentov commented 7 years ago

@AlanFoster thanks! But I'm actually trying to do this on selenium (or chrome), and can't seem to get it right

AlanFoster commented 7 years ago

@liorbentov Unfortunately I was only able to trigger the component to open with selenium/capybara with JavaScript directly, due to the component's DOM structure and how it's styled on the page.

leimonio commented 7 years ago

@JedWatson any update on this? is there any way that we can actually trigger programmatically the selection dropdown? I'm currently using NightmareJS and cannot find any working solution.

nathanielescribano commented 7 years ago

Looking for a similar thing. trying to write integration tests using zombiejs but unable to find the best way to programmatically trigger the dropdown.

leimonio commented 7 years ago

@nathanielescribano I finally found out a way to make tests work. I'm testing with NightmareJS, so there is a package https://github.com/Mr0grog/nightmare-real-mouse for triggering real mouse events on the browser. This solution worked fine for me. You may probably need a relative solution with ZombieJS, since it is also testing on headless browser.

ghost commented 7 years ago

see also #603 Document how to test with selenium

nakulthebuilder commented 7 years ago

This worked for me using Selenium:

const getSelect = () =>
  driver
  .findElement({
    className: 'Select-placeholder'
  })
  .findElement({'xpath': '//*[@id="react-select-2--value"]/div[2]/input'});

await driver.wait(until.elementLocated(getSelect));
const select = await getSelect();
await select.sendKeys(query);

The xpath there is to the input element

volkanunsal commented 6 years ago

Does anyone know how to simulate selection on this component using just Javascript? The code samples above did not work for me.

$('.Select-input input').trigger('mousedown');
$('div.Select-option').first().trigger('mousedown');

I need a solution I can use in my integration tests.

I'm using React 15.6.2 and react-select 1.2.1.

aterreno commented 6 years ago

@volkanunsal after a few failed tentatives, I ended up doing 'brute force' by tabbing, going down with the arrow key and then sending an enter.

    const TAB = '\u0009';
    const ARROW_DOWN = '\uE015';
    const ENTER = '\uE007';
    browser.keys([TAB, ARROW_DOWN, ENTER]);
nachikethashu commented 6 years ago

I was able to get the menu opened with following code: this.mouseEvent('mousedown', '.Select .Select-arrow-zone');

Note that this is casperJS syntax. However I feel you can extend this to any other framework.

sad148 commented 6 years ago

Was anyone able to find a fix for selenium?

arhtudormorar commented 6 years ago

I'm also not able to solve it - it seems jQuery is not an option: https://github.com/facebook/react/issues/3249

abidvf commented 5 years ago

I am also not been able to trigger it with jasmine.

seclace commented 5 years ago

It seems to changes in library itself, so we need to trigger event touchend on svg icon or its wrapper to open options list to be able to select one.

This link may be helpful to testing in chrome (presumably puppeteer for integration tests): https://stackoverflow.com/a/42447620

natew commented 5 years ago

A forceOpen property would be really nice -- right now I have a z-index issue where the menu is hidden on open, but I can't debug it easily, because it goes away once I try and inspect it.

bladey commented 4 years ago

Hi all,

Thank you everyone who had a part in addressing this question.

In an effort to sustain the react-select project going forward, we're closing issues that appear to have been resolved via community comments.

However, if you feel this issue is still relevant and you'd like us to review it, or have any suggestions regarding this going forward - please leave a comment and we'll do our best to get back to you!

josias-r commented 2 years ago

Tried all the options above, nothing worked in plain js for me. But after a while I figured out, this works:

selectContainer
  .querySelector(".YOUR_CUSTOM_CLASS__dropdown-indicator")
  .dispatchEvent(
    new MouseEvent("mousedown", {
      button: 0,
      bubbles: true, // this is key, otherwise it wont work
    })
  ); // open the menu by "clicking" dropdown indicator

Where selectContainer is just some higher level DOM element wrapping the specific select you are interested in. In my case I simply wrapped it with a new div, but you can of course you can querySelect an existing Select container

laurentiu-lese-collinson commented 1 year ago

if we want to do it via react test library I would always recommend to check the actual tests from this library, they are the key to understand what needs to be triggered. e.g.

  // Re-open Menu
  fireEvent.mouseDown(
    container.querySelector('div.react-select__dropdown-indicator'),
    {
      button: 0,
    }
  );

If we are talking about vanilla js, we can do it like this: (notice the selector that uses a pattern for class selection, you should replace the value with whatever class you have in your project for dropdown indicator)

const element = document.querySelector('div[class*=dropdown-indicator]')
element.dispatchEvent(new MouseEvent('mousedown', {
  bubbles: true,
  cancelable: true,
  view: window
}));