ListItem in material UI reactjs - reactjs

I have been trying to use custom buttons for ListItem with children. I am able to change the open button using rightIconButton props of ListItem, but unable to change it after toggling. I have tried using righToggle props of ListItem, but still, it's not working.
<ListItem
style={{height: 46}}
nestedListStyle={{padding: 0}}
key={key}
open={this.checkOpenMenu()}
primaryTogglesNestedList={true}
primaryText='test'
rightIconButton = {
<IconButton style={{}}><img src={baseUrl + "/open.svg" } alt="More" />
</IconButton>}
rightToggle = {
<IconButton style={{}}><img src={baseUrl + "/close.svg" } alt="More" />
</IconButton>}
nestedItems={children}
/>);

Related

MaterialUI-Next Close drawer on children click

I am running MaterialUI-Next and I've tried all variants of the drawer but I can't get it to close when an item in the drawer has been clicked. I know the temporary variant allows me to do that but it closes when anything is clicked. I need it to close only when a ListItem is clicked.
In my case, I have a ExpansionPanel in the drawer with Lists and ListItems in it. Clicking the ExpansionPanel shouldn't close the drawer, the drawer should remain open because I am not ready to navigate away. However when I click a ListItem in the drawer (They contain component="a" items) then the drawer should close and the component="a" navigated to its href.
My code is extremely basic and vanilla, practically the exact demo from MaterialUI-Next Drawers. They also have a playground to test code. But anyhow, here is some of my relevant code
My drawer:
<Drawer
variant="persistent" //I tried "temporary", and "permanent" as well
elevation={16}
anchor="right"
open={this.state.open}
// onClick={(open) => this.setState({ open: false })}
// onKeyDown={(open) => this.setState({ open: false })}>
<SystemMenu />
</Drawer>
My Content stored in SystemMenu:
import React from 'react';
import { withStyles } from 'material-ui-next/styles';
import List, { ListItem, ListItemText } from 'material-ui-next/List';
import ExpansionPanel, { ExpansionPanelDetails, ExpansionPanelSummary } from 'material-ui-next/ExpansionPanel';
import ListSubheader from 'material-ui-next/List/ListSubheader';
import {ChevronDown} from 'mdi-material-ui';
import Grid from 'material-ui-next/Grid';
const OTHERPAGES = require('data/pages.js');
const systemMenuData = OTHERPAGES['systemMenuPagesItems'];
class SystemMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: null,
};
}
handleChange = panel => (event, expanded) => {
this.setState({
open: false,
expanded: expanded ? panel : false,
});
};
render() {
const { expanded, open } = this.state;
return (
<div>
<Grid
container>
<Grid item xs={12} style={{padding: 0}}>
<ListSubheader component="div">MENU ITEMS</ListSubheader>
</Grid>
</Grid>
<Grid
container>
<Grid item xs={12}>
<ExpansionPanel
expanded={expanded === 'panel1'}
onChange={this.handleChange('panel1')}>
<ExpansionPanelSummary
expandIcon={<ChevronDown />}>
Players
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<List>
<ListItem
button
component="a"
href="/app/page1">
<ListItemText primary="Page 1" />
</ListItem>
<ListItem
button
component="a"
href="/app/page2">
<ListItemText primary="Page 2" />
</ListItem>
<ListItem
button
component="a"
href="/app/page3">
<ListItemText primary="Page 3" />
</ListItem>
</List>
</ExpansionPanelDetails>
</ExpansionPanel>
</Grid>
</Grid>
</div>
);
}
}
export default withStyles(styles)(SystemMenu);
I read in other posts that you can pass the onKeyDown={(open) => this.setState({ open: false })} to its children with props. But I am new in React and having some problems with this concept.
Basically, I need to make the onKeyDown work for the ListItems, not the actual drawer. I may or may not have to change my Drawer variant back to temporary for the onKeyDown property to work. Not too sure.
(Remember the link above offers a playground to test code. Much better than me trying to build a jFiddle for React)
EDIT
I worked out a different way but not my favorite.
By moving the content in the SystemMenu to the Menu.js file (where my Drawer imports, functions, and states live) I was able to create a handleClose function with timeout so that it closes. This is the function:
handleClose = () => {
if (!this.state.open) {
return;
}
this.timeout = setTimeout(() => {
this.setState({ open: false });
this.setState({ expanded: null });
});
};
This requires the persistent variant and a handleOpen() function to keep the drawer open until I click a List that has the handleClose() function. So, my Drawer looks like this now
<Drawer
variant="persistent"
elevation={16}
anchor="right"
open={this.state.open}
onClick={() => { this.handleOpen(); }}>
...
My System Menu code in here, not too happy about this part. :(
...
</Drawer>
Next, in my System Menu code that contains the Expansion Panels and Lists, I added the handleClose() function to the List and it navigates as well as closing the drawer and expansion panels. Like this:
<List
className="quickpanel-navigation"
onClick={() => { this.handleClose(); }}>
.... ListItems code here ....
</List>
This is an Ok method, but I would much rather I had the SystemMenu code in another file and imported to this file and using props to manipulate the closing functions in the Menu.js file from the SystemMenu.js file. If anyone else wants to keep helping me I'd appreciate it.
In the demo, they're wrapping all of the ListItems in a div that handles closing the drawer:
<div
tabIndex={0}
role="button"
onClick={this.toggleDrawer('left', false)}
onKeyDown={this.toggleDrawer('left', false)}
>
{sideList}
</div>
In this code, toggleDrawer is a function that sets the state used for the Drawer's open prop. So when a key is pressed or a click occurs in one of the List's children, the event bubbles up to these handlers (as long as event.stopPropagation() is not used) and state is changed.
In your code, you don't want the expander's click to close the drawer, so you have to handle these events differently.
You can add an onClick handler to the List:
<ExpansionPanelDetails>
<List onClick={ () => { /* close the drawer here */ } }>
<ListItem
button
component="a"
href="/app/page1">
<ListItemText primary="Page 1" />
</ListItem>
<ListItem
button
component="a"
href="/app/page2">
<ListItemText primary="Page 2" />
</ListItem>
<ListItem
button
component="a"
href="/app/page3">
<ListItemText primary="Page 3" />
</ListItem>
</List>
</ExpansionPanelDetails>
Then, when the ListItems are clicked, the event will bubble to the List's handler, which sets state, closing the Drawer.
Based on the structure of your code, you may need to modify your implementation of Drawer to pass a function to your SystemMenu component so that the click handler on its List can set the state of its parent because this.setState affects the state of this, which is the current instance of that component. It would not set the state of the Drawer.
UPDATE:
Based on your comments, I'm adding this bit.
In your SystemMenu component:
- accept a prop called closeDrawer.
- use this function, from props, as the onClick handler on List
In your Menu component:
add a function to close the Drawer, let's call it closeDrawer for now.
when rendering SystemMenu, pass this.closeDrawer as `closeDrawer
<SystemMenu closeDrawer={this.closeDrawer} ...>
This is one way to alter the local state of Menu, the parent component, from within SystemMenu, the child.
Assuming your Drawer is in a class component, you would do something like this to close it from a child component.
class MyComponent extends React.Component {
constructor(props){
super(props)
this.state = {
open: false,
}
}
closeDrawer = () => this.setState({ open: false })
render() {
return (
<Drawer
elevation={16}
anchor="right"
open={this.state.open}
onKeyDown={this.closeDrawer}
>
<SystemMenu
closeDrawer={this.closeDrawer}
/>
</Drawer>
)
}
}
Then you can access this.props.closeDrawer from within SystemMenu. It sounds like you want to add to the onClick handler for your list items:
<ListItem
button
component="a"
href="/app/page1"
onClick={this.props.closeDrawer}
>
<ListItemText primary="Page 1" />
</ListItem>

React native setState() not re-render as expected

I made a carousel with react-native-snap-carousel and below are the code for carousel. In carousel there're picker and textInput and etc.
For render function:
<Carousel
ref={(c) => { this._carousel = c; }}
data={this.state.question}
renderItem={this._renderItem}
sliderWidth={standard * 1.1}
itemWidth={standard * 0.9}
inactiveSlideScale={0.9}
inactiveSlideOpacity={0.7}
firstItem={0}
activeSlideAlignment={'center'}
containerCustomStyle={styles.slider}
contentContainerCustomStyle={styles.sliderContentContainer}
/>
For detail rendering:
_renderItem ({item, index}) {
var content;
switch (item.id) {
case 1:
content = <View style={styles.carousel_cell}>
<Text style={styles.carouselItemTitle}>{item.displayLabel}</Text>
<TextInput style={styles.editor} onChangeText={(text) => this.setState({comment: text})} value={this.state.comment} />
</View>;
break;
case 2:
content = <View style={styles.carousel_cell}>
<Text style={styles.carouselItemTitle}>{item.displayLabel}</Text>
<Picker mode="dropdown"
style={styles.picker}
selectedValue={this.state.priority}
onValueChange={(itemValue) => this.onPriorityChange(itemValue)}>
{this.state.list}
</Picker>
</View>;
break;
default:
content = <View style={styles.carousel_cell}>
<Text style={styles.carouselItemTitle}>{item.displayLabel}</Text>
<Text>Unknown</Text>
</View>;
break;
}
return content;
}
For event triggering:
onPriorityChange(value) {
this.setState({priority: value});
}
The problem here is that after I select any item in Picker the state did get updated but on the interface it's not. The render function of Picker will be invoked once I trigger the onChangeText event. At that time the display in Picker will be update as well.
However, according to the life cycle, shouldn't the carousel(including its cells) be updated/re-rendered when I setState?
The Stopwatch(from react-native-stopwatch-timer) in carousel will have the same problem.
I don't know this package but the Carousel component seems to inherit from FlatList component.
The documentation says:
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
So, try to add extraData={this.state} to your Carousel component.

ReactJS - AutoComplete inside AppBar in Material UI

I am learning ReactJS and I built an AppBar and added a TextField inside that. It was working perfectly. The following is my code:
class Test extends React.Component {
render() {
return (
<MuiThemeProvider>
<AppBar
title={"Benchmarking"}
iconElementLeft={<IconButton iconClassName="muidocs-icon-custom-github" />}
iconElementRight={
<div>
<TextField
hintText='this is a sample text'
/>
</div>
}
/>
</MuiThemeProvider>
)
}
}
Now I tried to add an AutoField in the place of TextField, its not throwing any error, but the AppBar is not displaying. What might be the problem? Kindly help.
an AutoComplete requires the dataSource and onUpdateInput props so you will have to provide that. Do something like this
state = {
dataSource: [],
};
handleUpdateInput = (value) => {
this.setState({
dataSource: [
value,
value + value,
value + value + value,
],
});
};
Then pass them as props in the AutoComplete
<AutoComplete
hintText="Type anything"
dataSource={this.state.dataSource}
onUpdateInput={this.handleUpdateInput}
/>
Here is the link to the AutoComplete page in Material-UI - http://www.material-ui.com/#/components/auto-complete

React Native : multiple Navigator navigationBar

I'm stuck with React Native.
I have a "Header" navigationBar, but I want to add another navigationBar to my Navigator component.
render() {
let currentRoute = this.props.route
return (
<Navigator
style={styles.container}
initialRoute={this.props.route}
renderScene={this.router.bind(this)}
navigationBar={<Header />} // << There, how can I simply add another navigationBar ?
/>
);
}
And here's the <Header/> component :
render() {
return <Navigator.NavigationBar
style={styles.navBarContainer}
navState={this.props.navState}
routeMapper={routeMapper}
/>
}
Now, I'm trying to add a <Footer/> component, which would render a similar component as <Header/>, in order to have 2 persistent navigation bar on my app.
How to achieve this ?
I also meet this question, and have resolved it. In React Native, it is not supported to add multiple navigationBar. But, if you want to add another "navigationBar", you can add this "navigationBar" as the sibling node of the Navigator, such as:
render() {
return (
<View style={styles.scene}>
<TopStatusBar
showBackIcon={false}
centerText={LocalizedStrings.appName}
rightIcon={require("../../res/icons/head.png")}
onRightPress={this._onHeadPress.bind(this)}
/>
<Navigator
initialRoute={ROUTE_STACK[0]}
renderScene={this._renderScene.bind(this)}
configureScene={() => Navigator.SceneConfigs.FadeAndroid}
navigationBar={
this.state.displayBottomTabBar ?
<BottomTabBar
ROUTE_STACK={ROUTE_STACK}
/>
:
null
}
onWillFocus={(route) => {
this.presentedRoute = route;
}}
sceneStyle={{flex: 1}}
/>
</View>
);
}
In the upper code, TopStatusBar is a composite component. It persists across scene transitions, just like the navigatorBar.
Good luck!

Material-ui svg-icons inside another element doesnt align to the center

I'm using the material ui library in my react project, and I have come across a strange issue, when I try to use svg icons inside a button-icon, the icom doesn't align to the center.
for example:
<ListItem key={product.id}
primaryText={product.title}
leftAvatar={<Avatar src={product.img}/>}
rightIcon={<IconButton><RemoveIcon/></IconButton>}/>
for this code I will get the following result:
And for this code:
<ListItem key={product.id}
primaryText={product.title}
leftAvatar={<Avatar src={product.img}/>}
rightIcon={<RemoveIcon/>}/>
I will get the following result :
My question is, how do i get to the result of my second example, but that the icon will we inside another element?
This is kind of late but I recently had the same issue and solved it by wrapping the IconButton component in a custom component and extending the css. You may have to change some other CSS to make it align perfectly but this worked for my use case.
import React, { PropTypes, Component } from 'react';
import IconButton from 'material-ui/IconButton';
const CustomIconButton = (props) => {
const { style } = props;
const additionalStyles = {
marginTop: '0'
};
return(
<IconButton {...props } style={{ ...style, ...additionalStyles }} iconStyle={{ fontSize: '20px' }}/>
);
};
CustomIconButton.PropTypes = {
// listed all the props that IconButton requires (check docs)
};
export default PPIconButton;
This is what a simplified usage of this custom IconButton looks like:
const deleteIconButton = (deleteFunc) => {
return <CustomIconButton
touch={true}
tooltip="Delete"
tooltipPosition="top-right"
onTouchTap={deleteFeed}
iconClassName="fa fa-trash"
/>;
};
class MyList extends Component {
render() {
return (
<div>
<List>
<ListItem value={ i } primaryText="My List Item" rightIcon={ deleteIconButton(() => this.props.deleteFeed(i) } />
) }
</List>
</div>
);
}
}
check this code, working fine for me
import React from 'react';
import List from 'material-ui/List';
import ListItem from 'material-ui/List/ListItem';
import Delete from 'material-ui/svg-icons/action/delete';
const MenuExampleIcons = () => (
<div>
<List style={{width:"300px"}}>
<ListItem primaryText="New Config" leftIcon={<Delete />} />
<ListItem primaryText="New Config" rightIcon={<Delete />} />
</List>
</div>
);
export default MenuExampleIcons;

Resources