react-select: Do we need CSS as well just to display a normal select list? - reactjs

I have following code which displays a normal select list. It is working fine logically and i am able to log when any change happens. But the problem is, very small box is being displayed instead of a text box which is adequate to show the options.And my placeholder is being displayed as a normal text. But when i click on placeholder, all values are being displayed.
import Select from 'react-select;
class SelectList extends Component {
onChange() {
console.log("Value Changed");
}
render() {
var Select = require('react-select');
var options = [
{ value: 'one', label: 'India' },
{ value: 'two', label: 'Singapore' },
{ value: 'three', label: 'IceLand' }
];
return(
<Select
name="Select List"
value="one"
options={options}
onChange={this.onChange.this(bind}}
/>
);
}
do I need any CSS things here. Could anybody let me know what am I missing here?
[1]: https://i.stack.imgur.com/NA4hT.png

You're already importing Select at the top, why are you doing
var Select = require('react-select');
again in the render function?
Also your onChange handler should be onChange={this.onChange} and you can bind the handler at the top in the constructor like this:
this.onChange = this.onChange.bind(this);
Or you can pass it to the Select component like this
onChange={() => {this.onChange()}}
As for the CSS issue, from github:
// Be sure to include styles at some point, probably during your bootstrapping
import 'react-select/dist/react-select.css';

Related

React-Select: Page turns white after selecting an option

I am pretty new to React and am trying to use React-Select for a simple dropdown menu.
When you selected an option it should display the value under it, for that I'm using the onChange function and the useState Hook, but everytime I select something, the whole page just turns white.
App.js
import "./App.css";
import Select from "react-select";
import { useState } from "react";
function App() {
const [selected, setSelected] = useState(0);
const options = [
{ value: "1", label: "a" },
{ value: "2", label: "b" },
{ value: "3", label: "c" },
];
return (
<div>
<Select placeholder="Choose one"
defaultValue={selected}
onChange={setSelected}
options={options}
/>
<h1>{selected}</h1>
</div>
);
}
export default App;
Any help is appreciated, thank you.
I've done a simple edit on your code:
When onChange fires you set the selected value with onChange={(e)=>setSelected(e.value)}
Here's a functional codesandbox.
If you inspect your console in the browser, you'll see the issue is coming from the h1 component. This is probably what you're trying to achieve:
<h1>{selected.value}</h1>
I found a way to solve this, however I don't know if this is the best solution.
I created a function handleChange
const handleChange = e => {
setSelected(e.value);
}
and added a value prop, and called the function with onChange
<Select placeholder="Choose one"
defaultValue={selected}
value={options.find(obj => obj.value === selected)}
onChange={handleChange}
options={options}
/>
I don't know why
<h1>{selected.value}<h1>
doesn't work, since it's practically the same what I'm doing in handleChange, right?

Select text in react-select

I'm able to focus a react-select programmatically, but I can't select the text in the input field.
I'm getting a reference to react-select like so:
const selectRef = useRef()
...
<Select
ref={selectRef}
// other props
</Select>
And then:
selectRef.current.focus()
selectRef.current.select()
Select focuses successfully, but for the second line (which I believe should work on an input element) I get:
TypeError: selectRef.current.select is not a function
How do I select text inside the react-select's input field?
react-select, I believe, handles this focus programmatically. The ref.current is not the search input.
We can achieve the expected behaviour using the same HTMLInputElement's select() method, however.
react-select provides a prop inputId whose value is attached directly to the input as an Id. onFocus we can select it.
import React, {Component} from "react";
import Select from "react-select";
function SingleSelect() {
const selectRef = useRef(null);
function handleButtonClick() {
selectRef.current.focus();
}
function handleFocus() {
document.querySelector("#select-input").select();
};
return (
<>
<Select
inputId = "select-input"
ref={selectRef}
onFocus = {handleFocus}
options = {[
{value: "1", label: "one"},
{value: "2", label: "two"},
]}
defaultInputValue = "some random string"
/>
<button onClick={handleButtonClick}>Focus!</button>
</>
);
}
Here is working example of the same code.
I hope this is what you wanted. Thanks :)
Just a couple of changes -:
<Select
ref={(n) => this.selectRef = n}
// other props
</Select>
And we can access the inputValue like this --> this.selectRef.select.props.inputValue
Working Fiddle -> https://stackblitz.com/edit/react-43utra
The reason your code doesn't work is because the ref to the Select component is not the same as the innerRef to the input. You would access it instead via the following object chain...
selectRef -> current (StateManager) -> select(Select Component) -> inputRef (Input Component)
selectRef.current.select.inputRef.select()

How to make the options in react-select dropdown accessible?

I am building a reactjs application and I am using a library called react-select for my dropdown which is searchable.
but the problem I am facing is that the options inside the select are not being read out by NVDA screenreader when using arrow keys.
and am not able to set focus on this dropdown as well for some reason.
I tried it via the official documentation but no luck as of now.
The library I am using:
React-select
https://react-select.com/home
The code:
import React, { Component, Fragment } from "react";
import Select from "react-select";
export const flavourOptions = [
{ value: "vanilla", label: "Vanilla", rating: "safe" },
{ value: "chocolate", label: "Chocolate", rating: "good" },
{ value: "strawberry", label: "Strawberry", rating: "wild" },
{ value: "salted-caramel", label: "Salted Caramel", rating: "crazy" }
];
export default class SampleDropdown extends Component {
state = {
isClearable: true,
isDisabled: false,
isLoading: false,
isRtl: false,
isSearchable: true
};
componentDidMount() {
document.getElementById("translate").focus();
}
render() {
const {
isClearable,
isSearchable,
isDisabled,
isLoading,
isRtl
} = this.state;
return (
<Fragment>
<Select
className="basic-single"
classNamePrefix="select"
defaultValue={flavourOptions[0]}
isDisabled={isDisabled}
isLoading={isLoading}
isClearable={isClearable}
isRtl={isRtl}
isSearchable={isSearchable}
name="color"
options={flavourOptions}
id="translate"
/>
</Fragment>
);
}
}
And here is a working example in codesandbox.
https://codesandbox.io/s/focused-clarke-euk0e
Actual result: When I enter the page, the dropdown does not have the focus. and am not able to read out options in the dropdown using arrow keys in NVDA screenreader.the options are being read out as blank.
Expected result: When I enter the page, the dropdown should have the focus. and the options in the dropdown should be read out when using arrow keys when NVDA screenreader is switched on.
I looked at using the same library but ran into accessibility issues as well. I ended up building my custom select element and manually handling the key presses, focus movement, and label announcements. If you're stuck on using react-select you'll probably need to amend it yourself or wait for a PR.
Otherwise, if you're up for the challenge, you can follow my tutorial on creating an accessible select component in React. You can pull apart the code on codesandbox as well. This might make it easier to port to the react-select as well.
And of course, I'd also recommend using the native select element, as that will handle accessibility best.
Reach UI has accessible components. This Combobox could be of use https://reach.tech/combobox

How do I trigger the change event on a react-select component with react-testing-library?

Given that I can't test internals directly with react-testing-library, how would I go about testing a component that uses react-select? For instance, if I have a conditional render based on the value of the react-select, which doesn't render a traditional <select/>, can I still trigger the change?
import React, { useState } from "react";
import Select from "react-select";
const options = [
{ value: "First", label: "First" },
{ value: "Second", label: "Second" },
{ value: "Third", label: "Third" },
];
function TestApp() {
const [option, setOption] = useState(null);
return (
<div>
<label htmlFor="option-select">Select Option</label>
<Select
value={option}
options={options}
onChange={option => setOption(option)}
/>
{option && <div>{option.label}</div>}
</div>
);
}
export default TestApp;
I'm not even sure what I should query for. Is it the hidden input?
My team has a test utility in our project that lets us select an item easily after spending too much time trying to figure out how to do this properly. Sharing it here to hopefully help others.
This doesn't rely on any React Select internals or mocking but does require you to have set up a <label> which has a for linking to the React Select input. It uses the label to select a given choice value just like a user would on the real page.
const KEY_DOWN = 40
// Select an item from a React Select dropdown given a label and
// choice label you wish to pick.
export async function selectItem(
container: HTMLElement,
label: string,
choice: string
): Promise<void> {
// Focus and enable the dropdown of options.
fireEvent.focus(getByLabelText(container, label))
fireEvent.keyDown(getByLabelText(container, label), {
keyCode: KEY_DOWN,
})
// Wait for the dropdown of options to be drawn.
await findByText(container, choice)
// Select the item we care about.
fireEvent.click(getByText(container, choice))
// Wait for your choice to be set as the input value.
await findByDisplayValue(container, choice)
}
It can be used like this:
it('selects an item', async () => {
const { container } = render(<MyComponent/>)
await selectItem(container, 'My label', 'value')
})
You can try the following to get it working:
Fire focus event on the ReactSelect component .react-select input element.
Fire a mouseDown event on the .react-select__control element
Fire a click on the option element that you want to select
You can add a className and classNamePrefix props with the value of "react-select" in order to specifically select the component you are trying to test.
PS: In case you are still stuck I'd encourage you to take a look at this conversation from where the above answer is borrowed - https://spectrum.chat/react-testing-library/general/testing-react-select~5857bb70-b3b9-41a7-9991-83f782377581

How to simulate selecting from dropdown in Jest / enzyme testing?

I'm trying to write jest tests for my React component that has a dropdown like this:
<select id="dropdown" onChange={this.handlechange} ref={this.refDropdown}>
{this.props.items.map(item => {
return (
<option key={item.id} value={item.id}>
{item.name}
</option>
);
})}
</select>
and the handler looks like this:
handlechange = () => {
const sel = this.refDropdown.current;
const value = sel.options[sel.selectedIndex].value;
//...
}
I want to simulate a user selecting the 2nd entry (or anything other than the first) in the list but am having trouble. If I simulate a "change" event it does fire the call to handlechange() but selectedIndex is always set to zero.
I tried this code in the jest test but it doesn't cause selectedIndex to be accessible in the handler.
const component = mount(<MyComponent/>);
component.find("#dropdown").simulate("change", {
target: { value: "item1", selectedIndex: 1 }
});
What happens is almost correct. If I look at the incoming event, I can see e.value is set to "item1" as I set, but it doesn't act like the selection was actually made.
I've also tried trying to send "click" simulations to the Option element directly but that does nothing.
What's the right way to simulate a selection from a dropdown?
Try this approach:
wrapper.find('option').at(0).instance().selected = false;
wrapper.find('option').at(1).instance().selected = true;
You can trigger a change event since you have your this.handlechange trigger onChange:
const component = mount(<MyComponent/>);
component.find('#dropdown').at(0).simulate('change', {
target: { value: 'item1', name: 'item1' }
});
I would say you have to add .at(0) because Enzyme will find a list of values even if you only have one element with that ID.
Try changing the html "onInput" to "onChange" because you are simulating the "change" event in jest.
Short Answer -
Use the following snippet
import userEvent from "#testing-library/user-event";
userEvent.selectOptions(screen.getByTestId("select-element-test-id"), ["option1"]);
Detailed Answer -
.tsx file
.
.
<Form.Select
aria-label="Select a value from the select dropdown"
required
onChange={(e) => {
console.log("Option selected from the dropdown list", e.target.value);
optionChangedHandler(e.target.value);
}}
data-testid="select-element-test-id"
>
...CODE FOR RENDERING THE LIST OF OPTIONS...
</Form.Select>
.
.
.test.tsx file
import userEvent from "#testing-library/user-event";
it("Check entire flow", async () => {
render(
<YourComponent/>
);
// CHECK IF SELECT DROPDOWN EXISTS
const selectDropdown = await waitFor(
() => screen.getByTestId("select-element-test-id"),
{
timeout: 3000,
}
);
expect(selectDropdown ).toBeInTheDocument();
//"option2" is the element in the select dropdown list
userEvent.selectOptions(screen.getByTestId("select-element-test-id"), [
"option2",
]);
}
The above code will trigger the onChange function of the select element.

Resources