React-Select conflicting with MUI Accordion - reactjs

I'm stumped. I've done a number of Google searches as well as looked at react-select and Material UI Accordion docs. No one seems to have encountered this issue before, which surprises me, as these are very popular component libraries.
I've been using the <CreatableSelect> component on its own, no problem, with isSearchable and isMulti properties activated. Behavior is as expected. When I type in the box it auto-filters the dropdown list of options accordingly.
However, things get glitchy only when I put this component into the content pane of an MUI <Accordion> component: the typing action of the user after two characters causes the select widget to lose focus, the page scrolls to some unknown anchor point, and the user's typed text does not remain in the select box, and the dropdown list of options doesn't even appear. This glitch occurs even if there are only a small number of options (e.g. 5-10) in the options list.
Does anyone have any experience with the interactivity of these two components? Is there some component property toggle I am missing? I am guessing that the Accordion is responding to keystrokes in a way that is overriding the CreatableSelect's behavior.
import MuiAccordion from "#mui/material/Accordion";
import MuiAccordionSummary from "#mui/material/AccordionSummary";
import MuiAccordionDetails from "#mui/material/AccordionDetails";
import ExpandMoreIcon from "#mui/icons-material/ExpandMore";
import CreatableSelect from "react-select/creatable";
const FilterBox = ({.... various props .... }) => {
const Accordion = styled(MuiAccordion)(({ theme }) => ({
}));
const AccordionSummary = styled(MuiAccordionSummary)(({ theme }) => ({
}));
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
}));
return (
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel-candidates-content"
id="panel-candidates-header"
>Search Items
</AccordionSummary>
<AccordionDetails>
<CreatableSelect
instanceId={label}
aria-label={label}
styles={selectCustomStyles}
options={showOptions}
isSearchable
isMulti
value={value}
filterOption={handleFilterOption} />
</AccordionDetails>
</Accordion>
)
}

Upon further investigation, I figured out that the problem is the styled() function from MUI legacy styling. When I switched over to the newer sx prop paradigm, the issue went away. I still have no idea why styled() would interfere with user interaction. But given that this approach is deprecated, I'm not going to bother to figure that out! Just switch over to MUI "system" and you're good to go.

I have same problem, but i used the Select from Material-UI. That problem appear when i set props disablePortal like "False" for Select. Just try use Select from Material-UI with portal or just portal.
<Select
MenuProps={{
disablePortal: true,
}}>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>

Related

react-select doesn't play well in mobile when dropdown icon is "overridden" and onMenuOpen+onMenuClose are used

I am using react-select for value input in my web app. I have to override dropdown indicator icon according to design and some dynamic modifications has to be performed depending on the opened/closed status of the menu. I am using styled-components and I have simplified the code a bit to be presented here:
const DropdownIndicator = (props) => {
return (
<components.DropdownIndicator {...props}>
</components.DropdownIndicator>
);
};
<S.InputField
disabled={disabled}
labelLeft={labelLeft}
noLabel={!label}
className={className}
fieldType={state?.type}
inputType={state?.inputType}
notClearable={notClearable}
extendOnOpen={extendOnOpen && menuIsOpen}
menuIsOpen={menuIsOpen}
menuHeight={menuHeight}
>
<label htmlFor={id}>{label}</label>
<div className="input-wrapper">
<Select
id={id}
placeholder={'test'}
components={{ DropdownIndicator }}
// menuIsOpen={menuIsOpen}
onMenuOpen={() => {
console.log('menu opened');
setMenuIsOpen(() => true);
}}
onMenuClose={() => {
console.log('menu closed');
setTimeout(() => setMenuIsOpen(() => false), 3000);
}}
/>
</div>
</S.InputField>
Now, when I try to open the menu clicking on the Select's control field, the menu opens as expected, but when I want to open it clicking on the dropdown icon, the operation becomes hardly predictable - basically I get the 'menu opened' and 'menu closed' directly after, so menu doesn't stay open. Why could that be? Is it a bug in the react-select design?
The funny thing is, if I comment either components={{ DropdownIndicator }} or one of onMenuOpen or onMenuClosed, the menu opens/closes as expected, but never when both ("overridden" DropdownIndicator and onMenuOpen/onMenuClosed) are employed. And I put "overridden" in quotes as it is not technically overridden (I've removed the icon change from <components.DropdownIndicator {...props}></components.DropdownIndicator> as it doesn't impact the outcome), it's actually mimicking (as I understand) the default behaviour of react-select's menu flow.
Important! It only happens in mobile resolutions. Desktop res works just fine. So my understanding this has got something to do with onFocus/onBlur and the way they are treated in touch devices.
Any thoughts?

In react select how to allow user to select text of selected option to copy its text

React select does not allow text selection of selected option. I want user to be able to select text of selected option. Whenever user tries to select the text of selected option, react select's menu gets open(pops up).
link for code sandbox:
https://codesandbox.io/s/bzdhr?module=/example.js
Any help appreciated, Thanks in advance.
react-select doesn't allow us to select the text.. neither in options or the selected value for that matter..
the input component dominated the div that keeps the value.. there is no way to select that div.
however.. i managed to find a work-around for you.. this should do..
https://codesandbox.io/s/react-codesandboxer-example-5jhzf
This happens because of the react-select onMouseDown event.
You can better handle it by overriding react-select custom renderers, wrap the single value renderer in a div which doesn't propagate onMouseDown events.
import React from 'react';
import Select, { Props, components, SingleValueProps } from 'react-select';
const SingleValueRenderer: React.FC<SingleValueProps<any>> = ({ children, ...props }) => (
<div
onMouseDown={(event) => {
event.stopPropagation();
}}>
<components.SingleValue {...props}>{children}</components.SingleValue>
</div>
);
const Dropdown: React.FC<Props> = (props) => (
<Select
components={{ SingleValue: SingleValueRenderer }}
{...props}
/>
);
export default Dropdown;

How to I keep a Material-ui Select open when I click on only one of the items in it

I have been writing a custom Material-UI Select dropdown which has an optional text field at the top to allow the user to search / filter items in the Select if there were many entries.
I am struggling with how to keep the Select open when I click on the text field (rendered as an InputBase) and just have the normal behavior (of closing the Select when a regular MenuItem is selected.
CodeSandbox here : https://codesandbox.io/s/inspiring-newton-9qsyf
const searchField: TextField = props.searchable ? (
<InputBase
className={styles.searchBar}
onClick={(event: Event) => {
event.stopPropagation();
event.preventDefault();
}}
endAdornment={
<InputAdornment position="end">
<Search />
</InputAdornment>
}
/>
) : null;
return (
<FormControl>
<Select
className={styles.root}
input={<InputBase onClick={(): void => setIconOpen(!iconOpen)} />}
onBlur={(): void => setIconOpen(false)}
IconComponent={iconOpen ? ExpandMore : ExpandLess}
{...props}
>
{searchField}
{dropdownElements.map(
(currEntry: string): HTMLOptionElement => (
<MenuItem key={currEntry} value={currEntry}>
{currEntry}
</MenuItem>
)
)}
</Select>
</FormControl>
);
As you can see above I've tried using stopPropagation and preventDefault but to no avail.
check out this codesandbox link: https://codesandbox.io/s/busy-paper-9pdnu
You can use open prop of Select API
I was able to make a controlled open select by providing open prop as a react state variable and implementing correct event handlers. To make it controlled you must provide onOpen and onClose props of the Select and make sure the open prop stays true when the custom textfield is clicked.
One more important thing I had to do was override the default keyDown behavior of the Select component. If you open up a Select and start typing into it, it shifts focus to the select option that matches what you are typing. For example, if you Select had an option with the text Foobar and if you start typing Food and water, it would cause focus to shift from your custom text input onto the Foobar option. This behavior is overridden in the onKeyDown handler of the custom textfield
Working sandbox here
Edit: even though this worked in the codepen, I had to add onChange={handleOpen} to the Select as well to get it working on a real browser with React and Next.
You can still use stopPropagation to make it work
// open state
const [isSelectorOpen, setisSelectorOpen] = useState(false)
// handle change
const handleChange = event => {
const { value } = event.target
event.stopPropagation()
// set your value
}
// selector
<Select
multiple
open={isSelectorOpen}
onChange={handleChange}
input={(
<Input
onClick={() => setisSelectorOpen(!isSelectorOpen)}
/>
)}
// other attribute
>
<MenuItem>a</MenuItem>
<MenuItem>b</MenuItem>
<MenuItem>c</MenuItem>
</Select>
In my case, none of the above worked, but this did the trick to stop closing the Select:
<MenuItem
onClickCapture={(e) => {
e.stopPropagation();
}}>
You can also change onMouseEnter to change some default styling that comes with using MenuItem (like pointer cursor when mouse enters its layout)
onMouseEnter={(e) => {
e.target.style.backgroundColor = "#ffffff";
e.target.style.cursor = "default";
}}
In my case i also needed to remove the grey clicking effect that MenuItem makes on click, which is a new object generated in MuiTouchRipple-root, so changing display to none did the trick.
sx={{
"& .MuiTouchRipple-root": {
display: "none",
},
}}

How to change checkbox color and other styling for react-instantsearch?

I have ToggleRefinement checkboxes in my project and was trying to style it using ".ais-ToggleRefinement-checkbox {}" but the only thing that seems to be able to change is the font-size to make the checkbox bigger. Color and background-color doesn't do anything. I'd like to change the colors (especially the color when the checkbox is checked) and possibly other styling of the checkbox since I'm using BlueprintJS custom checkboxes in other parts of my website and I'd like the styling to be consistent between them.
Is there any way to achieve this?
To use the BlueprintJS components, you can use a connector. The docs for that are here and a guide for connectors in general here. This would look slightly like this:
import React from "react";
import { Checkbox } from "#blueprintjs/core";
import { connectToggleRefinement } from "react-instantsearch/connectors";
const Toggle = ({ refine, currentRefinement, label }) => (
<Checkbox
checked={currentRefinement}
label={label}
onChange={() => refine(!currentRefinement)}
/>
);
const ToggleRefinement = connectToggleRefinement(Toggle);
const App = () => (
<InstantSearch>
{/* add the rest of the app */}
<ToggleRefinement
attribute="materials"
value="Made with solid pine"
label="Solid Pine"
/>
</InstantSearch>
);

Material UI: Menu refactoring

was trying to refactor my menu, which uses List from Material UI.
The unrefactored menu looks like this
<SelectableList id="menu" value={location}>
<ListItem primaryText="Encoding" primaryTogglesNestedList={true} value="/encoding" nestedItems={[
<ListItem primaryText="Base32" containerElement={<Link to="/base32"/>} value="/base32"/>,
<ListItem primaryText="Base32Hex" containerElement={<Link to="/base32Hex"/>} value="/base32Hex"/>,
<ListItem primaryText="Base64" containerElement={<Link to="/base64"/>} value="/base64"/>,
and there is clearly a lot of boilerplate, so I decided to create a custom component, which would handle the duplicities in the code
import React from 'react';
import {ListItem} from 'material-ui/List'
import {Link} from 'react-router'
const MenuItem = ({anchor, ...other}) => (
<ListItem containerElement={<Link to={anchor} />} value={anchor} {...other} key={"item-"+ anchor}/>
);
export default MenuItem;
The problem is, that when I used it
<MenuItem primaryText="Base32" anchor="/base32" />
The menuItem ceased to be selectable. Further more, to recall the value from SelectableList id="menu" value={location} (to expand the menu, when page is reloaded), I had to to add value tag to MenuItem as well (duplicity back).
How should I handle this refactoring?
Update
JSFiddle (simplified example): https://jsfiddle.net/1vk8wzoc/27/
Ok, so looking at your JSFiddle and the Material UI source, it seems like they reject children without certain properties:
https://github.com/callemall/material-ui/blob/master/src/List/makeSelectable.js#L18
extendChild(child, styles, selectedItemStyle) {
if (child && child.type && child.type.muiName === 'ListItem') {
....
So your component is never receive the styles to indicate that its selected.
I would do one of two things here:
Possibly raise a PR with the library to support HOC's for
Listitems
Or, use React.cloneElement which should copy all
the required stuff across so it appears to be the right element to
the makeSelectable function

Resources