I am trying to determine if SelectField is expanded or not, i.e. if the dropdown and it's MenuItems are visible.
Currently I use roughly following approach:
<SelectField onClick={() => this.setState({ isExpanded: true })} >
<MenuItem primaryText={
<MenuItemContent onHide={() => this.setState({ isExpanded: false })}} />
}>
</SelectField>
and in MenuItemContent I implement
class MenuItemContent extends React.Component {
componentWillUnmount = () => this.props.onHide()
}
This approach has a drawback, that when you click outside the menu, the componentWillUnmount call is not triggered immidiately, but cca after 200ms, despite the MenuItems are no longer visible.
The Select field menu can be expanded by clicking it, and when is expanded, it will hide either when some menu item is clicked, or user clicks outside the menu.
This is how the SelectField looks when is expanded:
And here is collapsed:
For the better handling of SelectField close event you can use property dropDownMenuProps:
/**
* Object that can handle and override any property of component DropDownMenu.
*/
dropDownMenuProps: PropTypes.object,
By using this prop we can pass to DropDownMenu component onClose prop with handler function, which will be fired instantly after DropDownMenu close (caused by ESC, outside click or item select). Unfortunately DropDownMenu component dont provide similar prop to determine the opening so the only way (without
extending the component) is to follow your approach with onClick event handler.
Here is my working test example:
onSelectClose = () => {
console.log("close")
}
onSelectOpen = () => {
console.log("open")
}
render() {
return (
<MuiThemeProvider>
<div className="App">
<SelectField
floatingLabelText="Frequency"
onClick={this.onSelectOpen}
dropDownMenuProps={{
onClose: this.onSelectClose
}}
value={this.state.value}
onChange={this.handleChange}>
<MenuItem value={1} primaryText="Never" />
<MenuItem value={2} primaryText="Every Night" />
<MenuItem value={3} primaryText="Weeknights" />
<MenuItem value={4} primaryText="Weekends" />
<MenuItem value={5} primaryText="Weekly" />
</SelectField>
</div>
</MuiThemeProvider>
);
}
Detecting if React Material UI Select Field is expanded
The answer is available in Materia-UI Select-API documentation.
Link: https://material-ui.com/api/select/
Related
I'm using antd version 3 and want to change the outcome onChange when Tab is clicked.
Here the picture of my desire objective enter image description here
My tab title has a checkbox. Outcome should be in this way: when user is stand at Tab1 should have a possibility to change value checkbox of Tab2 or Tab3 without setting active Tab or moving to Tab2 or Tab3. I have read that tab didn't support HOC... I tried a lot of ways to solve but didn't work out with that. event.PreventDefault / event.stopPropagation similarly result. Even if handlers like onTabClick / onChange didn't set active Tab with a new key after checked checkbox active key is old but tab is render with new information.
Here example of custom tab title component with some extract from code
<Tabs
defaultActiveKey={'Tab 1'}
onChange={(key) => onChangeHandler(key)}
onTabClick={(key: string, event: MouseEvent) => onTabClick(key, event)}
>
<TabPane
tab={<Header name={'Tab1'} />}
key={'Tab 1'}
/>
<TabPane
tab={<Header name={'Tab 2'} />}
key={'Tab 2'}
/>
<TabPane
tab={<Header name={'Tab 3'} />}
key={'Tab 3'}
/>
const Header = ({
name,
onChange,
isActive,
id,
}) => {
return (
<div>
<Checkbox
isActive={isActive}
value={id}
checked={isActive}
onChange={onChange}
/>
<Title >{name}</Title>
</div>
)
}
As far as I understand you want the tabs not to change when you click on the check box.
To do this, you can pass the onClick function to the Checkbox and stop the event bubbling there
const Header = ({ name, onChange, isActive, id }) => {
return (
<div>
<Checkbox
isActive={isActive}
value={id}
checked={isActive}
onChange={onChange}
onClick={(e) => {
e.stopPropagation();
}}
/>
<Title>{name}</Title>
</div>
);
};
thanks for being here! My question is related to how I can create an 'onChange' property in the Material UI Menu component. When I am using the 'onChange' property of the Select component, I can easily alter my state after the user clicks on it. I would like to create a similar effect, but then using a Menu instead of a Select component. Note that I am using a function inside a hook, which might complicate things.
Below I could show you how an example of how my code looks:
const [sortingMethod, setSortingMethod] = useState(() => sortHighestSummerTemp);
const onSortMethod = (e) => {
setSortingMethod(e.target.value);
};
<FormControl>
<InputLabel shrink>Sort By </InputLabel>{' '}
<Select defaultValue="" onChange={onSortMethod}>
<MenuItem value={() => sortHighestSummerTemp}>☀️ Hottest summers</MenuItem>
<MenuItem value={() => sortLowestWinterTemp}>🥶 Coldest winters</MenuItem>
<MenuItem value={() => sortMostHotDays}>🥵 Most hot days</MenuItem>
</Select>
</FormControl>;
That's my select component in action, which is working. And here is the same Menu, where I don't know how to implement the "onChange":
<FormControl className={classes.formControl}>
<PopupState variant="popover" popupId="demo-popup-menu">
{(popupState) => (
<React.Fragment>
<Button
variant="contained"
color="primary"
startIcon={<SortIcon />}
{...bindTrigger(popupState)}
>
Sort by
</Button>
<Menu
value=""
// onChange={onSortMethod} <-- How to do this? ⚠
{...bindMenu(popupState)}
>
<MenuItem
onClick={popupState.close}
value={() => sortHighestSummerTemp}
>
☀️ Hottest summers
</MenuItem>
<MenuItem
onClick={popupState.close}
value={() => sortLowestWinterTemp}
>
🥶 Coldest winters
</MenuItem>
<MenuItem onClick={popupState.close} value={() => sortMostHotDays}>
🥵 Most hot days
</MenuItem>
</Menu>
</React.Fragment>
)}
</PopupState>
</FormControl>;
I would be blessed if you could explain how to achieve a similar effect with the Menu component!
Thank you for reading.
I think you should do that per MenuItem (at the onClick property). The Menu itself doesn't have that kind of property: Material-UI page
Secondly, I don't like value as a function. I think you can just pass the variable (sortHighestSummerTemp or sortLowestWinterTemp) to a state. React page reference
This is my JSX:
<FormControl>
<ButtonGroup className="groupedHorizontal">
<InputLabel htmlFor="category">Category:</InputLabel>
<Select onChange={(event) => that.handleCategoryChange(event)} native={true} id="category">
<option></option>
{catOptions}
</Select>
<BrandsPopup />
<Button onClick={(e) => that.removeCategory(e)}>Del</Button>
</ButtonGroup>
</FormControl>
The BrandsPopup is a component which render a material-ui Button within <React.Fragment>. The select and the "Del" button are fine bordered as ButtonGroup elements. The problem is, BrandsPopup is not bordered and does not appear as part of the group. How to apply ButtonGroup styles on the button, rendered from the child component?
ButtonGroup uses cloneElement and thereby assigns its own props to its children. You should be able to log them to the console inside BrandsPopup and then just need to assign them to your button component. It is, of course, possible that this conflicts with how you are using BrandsPopup elsewhere in your app.
And if BrandsPopup indeed only contains one Button component you don't need the Fragment wrapper.
<ButtonGroup className="groupedHorizontal">
<BrandsPopup />
</ButtonGroup>
const BrandsPopup = (props) => (
<React.Fragment>
<Button
// these come from ButtonGroup 🧙
className={props.className}
color={props.color}
variant={props.variant}
>
click me
</Button>
</React.Fragment>
);
I have been using a Premade React-Bootstrap Typeahead search-bar found here
I am having trouble accessing the child elements from the dropdown. I want to be able to have an Onclick function for the dropdown items, but it doesn't seem like I have access to the children.
Below is the code I have currently, where I use typeahead
<div className="search-bar">
<Typeahead
id="sample"
options= {cities.map((city) => (city))}
labelKey="name"
placeholder="Enter a city"
/>
</div>
How do I get an onclick for these element cities listed?
You can customize menu rendering and behavior by using the renderMenu prop:
<Typeahead
options={options}
renderMenu={(results, menuProps) => (
<Menu {...menuProps}>
{results.map((result, index) => (
<MenuItem
onClick={() => console.log('click!')}
option={result}
position={index}>
{result.label}
</MenuItem>
))}
</Menu>
)}
/>
With the menu items exposed, you're free to add your own onClick handler to each item. Here's a working example.
Is there a way to nest a dropdown component with MenuItem? I would like to declare one component whenever I need to use a drop down instead of nesting two.
Am I on the right path or should drop down components be handled differently than normal React components?
App.js (this currently works nesting MenuItem within DropDown)
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
>
{stateAbbreviationData.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</DropDown>;
App.js (how I would like to use my component, does not work)
Shows code (data.map(option => ( ))} ) instead of a list when clicked. Further code below.
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData="stateAbbreviationData"
menuKey={stateAbbreviationData.value}
menuValue={stateAbbreviationData.value}
menuOptionLabel={stateAbbreviationData.label}
/>;
DropDown.js
import React from "react";
import TextField from "material-ui/TextField";
import DropDownMenu from "./DropDownMenu";
const styles = {
minWidth: 175
};
class DropDown extends React.Component {
render() {
return (
<TextField
select
label={this.props.label}
value={this.props.value}
style={styles}
onChange={this.props.onChange}
margin="normal"
InputLabelProps={{
shrink: true
}}
menuData={this.props.menuData}
>
<DropDownMenu
key={this.props.menuKey}
value={this.props.menuValue}
optionLabel={this.props.menuOptionLabel}
/>
</TextField>
);
}
}
export default DropDown;
DropDownMenu.js
import React from "react";
import MenuItem from "material-ui/Menu/MenuItem";
const styles = {
minWidth: 175
};
class DropDownMenu extends React.Component {
render() {
return (
<div>
{this.props.menuData.map(option => (
<MenuItem key={this.props.key} value={this.props.value}>
{this.props.optionLabel}
</MenuItem>
))}
</div>
);
}
}
export default DropDownMenu;
StateAbbreviationData.js
const stateAbbreviationData = [
{
value: "AL",
label: "AL"
},
{
value: "AK",
label: "AK"
}
// ...
];
export default stateAbbreviationData;
I think your first example is the one that you want to create with your DropDown component. Try removing DropDownMenu component, and doing all of the logic inside DropDown:
import React from "react";
import TextField from "material-ui/TextField";
import MenuItem from "material-ui/Menu/MenuItem";
const styles = {
minWidth: 175
};
class DropDown extends React.Component {
render() {
return (
<TextField
select
label={this.props.label}
value={this.props.value}
style={styles}
onChange={this.props.onChange}
margin="normal"
InputLabelProps={{
shrink: true
}}
>
{this.props.menuData.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
);
}
}
export default DropDown;
And then you can use it the way you want to:
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData} // <-- Put in braces
/>
Note:
With Material UI, I've used MenuItem components inside of the Select component, rather than the TextField component like you're doing:
https://material-ui.com/demos/selects/
There were a couple issues with your existing code I'll point out.
As I indicated above, you need to pass the data stateAbbreviationData into the DropDown component's props using curly braces:
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData} // <-- This change
menuKey={stateAbbreviationData.value}
menuValue={stateAbbreviationData.value}
menuOptionLabel={stateAbbreviationData.label}
/>
There's another bug with the way you're setting the menuKey, menuValue, and manuOptionLabel props. With the code you included, it seems like stateAbbreviationData is an array of objects.
In the props, you're trying to access properties on that array that don't exist: stateAbbreviationData.value and stateAbbreviationData.label. The objects in the array have those bits, but the array itself doesn't.
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData}
menuKey={stateAbbreviationData.value} // <-- Doesn't exist
menuValue={stateAbbreviationData.value} // <-- Doesn't exist
menuOptionLabel={stateAbbreviationData.label} // <-- Doesn't exist
/>
There's also a bug where you're passing menuData to the TextField component, but instead you should pass it to DropDownMenu.
That said, I recommend going with the first solution I proposed. Good luck!