How can I do to select only one item in my react-multi-select-component? - reactjs

I would like to choose only one option in my react-multi-select-component. Usually with other libraries I use isOptionDisabled but with that library the attribute is not valid.
Here is my code :
import React, { useState } from "react";
import ReactDOM from "react-dom";
import MultiSelect from "react-multi-select-component";
import "./styles.css";
const App = () => {
const options = [
{ label: "Grapes 🍇", value: "grapes" },
{ label: "Mango 🥭", value: "mango" },
{ label: "Strawberry 🍓", value: "strawberry" }
];
const [selected, setSelected] = useState([]);
return (
<div>
<MultiSelect
options={options}
selected={selected}
onChange={setSelected}
labelledBy={"Select"}
/>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The project is there :
https://codesandbox.io/s/react-multi-select-component-example-forked-9xjdpr?file=/src/index.js:0-666
My problem is that I am allowed to select multiple options and I want to be restricted to selecting only one of them.
Thank you very much for your help !

As i’ve just saw from the npm doc of react-multi-select-component, that you can add the attribute hasSelectAll and set the value to false.
After the first step, the only way that i’ve found throught the API, is to add a logic with the isCreatable attribute.
react-multi-select-component
From my point of view, if you want to use a single selection, i wouldn’t rather use a multi selection library to make it a single selection, instead i would look for some library that makes my work easier.

MultiSelect react dropdown let you add a selection limit by adding selectionLimit={SELECTION_LIMIT}
So you have to add it like that:
<MultiSelect
selectionLimit={1}
options={options}
selected={selected}
onChange={setSelected}
labelledBy={"Select"}
/>
Resources
For more info about selection limit Take a look here
You can also see other useful options from the official page here

Related

Hide current value on focus of react-select

When a user sets focus on the edit box of a single-selection react-select component, the currently-selected option will continue to be shown until the user types a key. I don't like this behavior. Instead I'd like do to what Google does which is to clear the edit box on focus.
How to do this?
I noticed there's a long-open issue in the react-select GitHub repo, but the solutions on that issue seem either complex or have UX tradeoffs that I'd like to avoid.
It took some experimentation, but the easiest solution was creating a custom SingleValue component that renders no content when the menu is open. Posting the answer here to save the time of others who may run into the same problem.
Note that the text shown isn't actually in the edit box. It's a separate HTML element of static text that's hidden by the library when the user types. I'm just accelerating that process. :-)
Live example
import { Fragment, useState } from "react";
import Select, { components } from "react-select";
const options = [
{ label: "one", value: 1 },
{ label: "two", value: 2 },
{ label: "three", value: 3 }
];
export default function App() {
const [selectedOption, setSelectedOption] = useState(options[1]);
return (
<Select
options={options}
components={{ SingleValue }}
value={selectedOption}
onChange={setSelectedOption}
placeholder="Pick a number"
/>
);
}
/** Hide the selected value when showing the dropdown list */
function SingleValue(props) {
const { children, ...rest } = props;
const { selectProps } = props;
if (selectProps.menuIsOpen) return <Fragment></Fragment>;
return <components.SingleValue {...rest}>{children}</components.SingleValue>;
}
// For TypeScript, declare like this:
// function SingleValue<T>(props: SingleValueProps<T, false>) {
If you want to show a placeholder instead of a blank edit box, you can just return your placeholder in place of children. Like this: (live example)
/** Show a placeholder instead of the selected value when showing the dropdown list */
function SingleValue(props) {
const { children, ...rest } = props;
const { selectProps } = props;
let contents = children;
if (selectProps.menuIsOpen) {
contents = selectProps.placeholder ? (
<div style={{ color: "#999" }}>{selectProps.placeholder}</div>
) : (
<Fragment></Fragment>
);
}
return <components.SingleValue {...rest}>{contents}</components.SingleValue>;
}
It's a shame that the docs for each react-select prop are so limited and they lack a table of contents to easily navigate. It's really full-featured once you figure out how the advanced stuff works like replacing custom components. If you're considering contributing feature PRs to that library, consider enhancing the docs too!

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?

Displaying placeholder content when an element is removed by an extension or adblocker

I trying to integrate some user feedback tools into out application, and part of this required embedding an iframe into the application. I am using Privacy Badger, and it blocks all the content of the iframe and changes the display property to none all the time. That's all well and good, i'm not trying to force this past peoples privacy extensions, however I do want to be able to detect if it has been removed, and just display some placeholder text like "This was removed by an adblocker. Feel free to email us if you have feedback" so that the app doesn't feel broken.
My Current Attempt: Code Sandbox
This however doesn't display the fallback as I would like it to.
I have tried various renditions of this method, all without luck.
Essentially what I want is this:
Detect if the iframe element has it's display property set to none, and render a fallback in it's place if that is true.
The problem is that you are immediately checking if the style is none, but your test doesn't change it until 5 sec later, so you'd have to wait some time before checking the style.
import React, { useState } from "react";
import ReactDOM from "react-dom";
function App() {
const [fallback, setFallback] = useState(0);
const setNoneDisplay = () => {
const el = document.getElementById("red-rover");
el.style.display = "none";
};
React.useEffect(() => {
window.setTimeout(setNoneDisplay, 5000);
});
const refCallback = React.useCallback((node) => {
if (node) setTimeout(() => setFallback(node.style.display === "none"), 6000);
}, []);
return (
<div className="App">
<div id="red-rover" ref={refCallback}>
Displayed
</div>
{fallback ? <div>Only Displayed As Fallback</div> : null}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

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 Manipulate Dropdown placeholder, onFocus?

Am new to ReactJS. I need to make the "placeholder" which is set to "State" initially to Empty/Null when onClicked or onFocus and then when it's not focused on, it goes back to "State" again. Can someone help me with this, am very new to react so any help will be appreciated.
import React from "react";
import { render } from "react-dom";
import { Container, Button, Modal, Dropdown } from "semantic-ui-react";
const stateOptions = [
{ key: "AL", value: "AL", text: "Alabama" },
{ key: "NY", value: "NY", text: "New York" }
];
const App = () => (
<Dropdown
placeholder="State"
fluid
multiple
search
selection
options={stateOptions}
/>
);
render(<App />, document.getElementById("root"));
From React's perspective, placeholder is a state that needs to be changed according to user's actions (onClick, onBlur)
So create a state to hold placeholder value that need to change.
There are two ways (since v16.8.0 with the introduction of React Hooks).
Using Class Component
class DropDown extends React.Component {
defaultPlaceholderState = "State";
state = { placeholder: this.defaultPlaceholderState };
clearPlaceholder = () => this.setState({ placeholder: "" });
resetPlaceholder = () =>
this.setState({ placeholder: this.defaultPlaceholderState });
render() {
return (
<Dropdown
onClick={this.clearPlaceholder}
onFocus={this.clearPlaceholder}
onBlur={this.resetPlaceholder}
placeholder={this.state.placeholder}
fluid
multiple
search
selection
options={stateOptions}
/>
);
}
}
In the code above, placeholder declared as a state with default value set to this.defaultPlaceholderState.
When a user clicks on the dropdown, onClick clears the placeholder value by setting it to an empty string. Same for onFocus when the Dropdown is on focus.
When a user clicks outside (onBlur), resetPlaceHolder sets the placeholder value to the default this.defaultPlaceholderState.
Using Function Component with useState hook
React v16.8.0 introduces Hooks, which enables Function Components (not a Functional Component, as it refers to Functional Programming) to hold states.
You can use React.useState hook to hold placeholder value.
const DropDownUsingHook = () => {
const defaultPlaceholderState = "State";
const [placeholder, setPlaceholder] = React.useState(defaultPlaceholderState);
const clearPlaceholder = () => setPlaceholder("");
const resetPlaceholder = () => setPlaceholder(defaultPlaceholderState);
return (
<Dropdown
onClick={clearPlaceholder}
onFocus={clearPlaceholder}
onBlur={resetPlaceholder}
placeholder={placeholder}
fluid
multiple
search
selection
options={stateOptions}
/>
);
};
⚠ Note: Unlike the Class version, clearPlaceholder, resetPlaceholder methods and placeholder state don't use this. prefix.
The implementation is similar but you use useState hook to declare the state and the setter (setPlaceholder).
Refer to the Hooks documentation, Using State Hook for more info.
You can play around with the working code on CodeSandbox.

Resources