I'm trying to create a custom select using React-Select options. I would like to have my search not in the control box, but rather in the menu. I tried this:
import React from "react";
import Select, { components } from "react-select";
import { colourOptions, groupedOptions } from "./docs/data";
const MenuList = props => {
return (
<components.MenuList {...props}>
<components.Input {...props} />;
{props.selectProps.inputValue.length > 1 ? props.children : ""}
</components.MenuList>
);
};
export default () => (
<Select
defaultValue={colourOptions[1]}
options={groupedOptions}
components={{ MenuList }}
/>
);
Problem is I'm getting an error saying
Uncaught Invariant Violation: input is a void element tag and must neither have children nor use dangerouslySetInnerHTML
I'm guessing the react select's components.Input
is rendering another div inside the input
tag or something like that.
Does anybody have an idea how this can be done maybe?
You should inspire yourself with what's suggested in the documentation in the section Advanced
here: https://react-select.com/advanced.
I have recreated a live example in CodeSandbox so you can see it in action and play with it. But the main idea is to embed the original Select
element inside some controlled element and them edit the style of your Select
to make it feel as one single MenuList
.
class PopoutExample extends Component<*, State> {
state = { isOpen: false, value: undefined };
toggleOpen = () => {
this.setState(state => ({ isOpen: !state.isOpen }));
};
onSelectChange = value => {
this.toggleOpen();
this.setState({ value });
};
render() {
const { isOpen, value } = this.state;
return (
<Dropdown
isOpen={isOpen}
onClose={this.toggleOpen}
target={
<Button
iconAfter={<ChevronDown />}
onClick={this.toggleOpen}
isSelected={isOpen}
>
{value ? `State: ${value.label}` : "Select a State"}
</Button>
}
>
<Select
autoFocus
backspaceRemovesValue={false}
components={{ DropdownIndicator, IndicatorSeparator: null }}
controlShouldRenderValue={false}
hideSelectedOptions={false}
isClearable={false}
menuIsOpen
onChange={this.onSelectChange}
options={stateOptions}
placeholder="Search..."
styles={selectStyles}
tabSelectsValue={false}
value={value}
/>
</Dropdown>
);
}
}
// styled components
const Menu = props => {
const shadow = "hsla(218, 50%, 10%, 0.1)";
return (
<div
css={{
backgroundColor: "white",
borderRadius: 4,
boxShadow: `0 0 0 1px ${shadow}, 0 4px 11px ${shadow}`,
marginTop: 8,
position: "absolute",
zIndex: 2
}}
{...props}
/>
);
};
const Blanket = props => (
<div
css={{
bottom: 0,
left: 0,
top: 0,
right: 0,
position: "fixed",
zIndex: 1
}}
{...props}
/>
);
const Dropdown = ({ children, isOpen, target, onClose }) => (
<div css={{ position: "relative" }}>
{target}
{isOpen ? <Menu>{children}</Menu> : null}
{isOpen ? <Blanket onClick={onClose} /> : null}
</div>
);
const Svg = p => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
focusable="false"
role="presentation"
{...p}
/>
);
const DropdownIndicator = () => (
<div css={{ color: colors.neutral20, height: 24, width: 32 }}>
<Svg>
<path
d="M16.436 15.085l3.94 4.01a1 1 0 0 1-1.425 1.402l-3.938-4.006a7.5 7.5 0 1 1 1.423-1.406zM10.5 16a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11z"
fill="currentColor"
fillRule="evenodd"
/>
</Svg>
</div>
);
const ChevronDown = () => (
<Svg style={{ marginRight: -6 }}>
<path
d="M8.292 10.293a1.009 1.009 0 0 0 0 1.419l2.939 2.965c.218.215.5.322.779.322s.556-.107.769-.322l2.93-2.955a1.01 1.01 0 0 0 0-1.419.987.987 0 0 0-1.406 0l-2.298 2.317-2.307-2.327a.99.99 0 0 0-1.406 0z"
fill="currentColor"
fillRule="evenodd"
/>
</Svg>
);