I am using react-admin framework and I am trying to put the <Edit> component inside <Drawer> component. I need this in order to be able to save changes done in my JsonInput.
This is my custom component:
import React, { Component, Fragment } from 'react';
import { fetchUtils, CardActions, EditButton, Button, List, Datagrid, Edit } from 'react-admin';
import Drawer from '#material-ui/core/Drawer';
import JsonInput from 'react-json-view';
import EditIcon from '#material-ui/icons/Edit';
import IconKeyboardArrowRight from '#material-ui/icons/KeyboardArrowRight';
import { SimpleForm } from 'ra-ui-materialui/lib/form';
const divStyle = {
width: '400px',
margin: '1em'
};
export default class JsonEditButton extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { showPanel: false };
}
handleClick = () => {
this.setState({ showPanel: true });
};
handleCloseClick = () => {
this.setState({ showPanel: false });
};
onChange = (value) => {
const { updated_src, name, namespace, new_value, existing_value } = value;
//this.onChange(updated_src);
}
render() {
const { showPanel } = this.state;
return (
<div>
<Button label="Upravit JSON" onClick={this.handleClick}>
<EditIcon />
</Button>
<Fragment>
<Drawer
anchor="right"
open={showPanel}
onClose={this.handleCloseClick}
>
<div>
<Button label="Zavřít" onClick={this.handleCloseClick}>
<IconKeyboardArrowRight />
</Button>
</div>
<div style={divStyle}>
{props => (
<Edit {...props}>
<SimpleForm>
{this.props.record && <JsonInput src={this.props.record} name={null} displayDataTypes={false} displayObjectSize={false} onEdit={this.onChange} onAdd={this.onChange} onDelete={this.onChange} />}
</SimpleForm>
</Edit>
)}
</div>
</Drawer>
</Fragment>
</div>
);
}
};
However this doesnt return any HTML.
Any idea how to solve this?
Thank you in advance.
Maybe this may help
<div style={divStyle}>
<Edit {...this.props}>
<SimpleForm>
{this.props.record && <JsonInput src={this.props.record} name={null} displayDataTypes={false} displayObjectSize={false} onEdit={this.onChange} onAdd={this.onChange} onDelete={this.onChange} />}
</SimpleForm>
</Edit>
</div>
the previous code where {props => ()} is actually a function or you could try this
<div style={divStyle}>
{props => (
return(
<Edit {...props}>
<SimpleForm>
{this.props.record && <JsonInput src={this.props.record} name={null} displayDataTypes={false} displayObjectSize={false} onEdit={this.onChange} onAdd={this.onChange} onDelete={this.onChange} />}
</SimpleForm>
</Edit>)
)}
</div>
Related
I have a component of "Drawer", I am opening and closing this drawer with component state and passing this state down to the Drawer Component and Also passing a callback function that can help me to close the drawer,
Now the issue is that when ever I am trying to open that drawer, the whole ui is disappearing. Need help
here is the code
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import Drawer from '../Drawer';
import Header from '../Header';
export default class LandingPage extends Component {
constructor() {
super();
this.state = {
openDrawer: false,
};
}
handleDrawer = (state) => {
this.setState({
openDrawer: state,
});
};
render() {
return (
<div className='landing-page-container'>
<div className='menu-btn'>
<IconButton
edge='start'
color='inherit'
aria-label='menu'
onClick={() => this.handleDrawer(true)}
>
<MenuIcon />
</IconButton>
</div>
<Drawer
handleDrawer={this.handleDrawer}
openDrawer={this.state.openDrawer}
/>
</div>
);
}
}
Here is Drawer
import React, { Component } from 'react';
import {
Drawer,
IconButton,
Divider,
List,
ListItem,
ListItemIcon,
Button,
ListItemText,
makeStyles,
} from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import {
ChevronLeft,
NoteAdd,
Person,
PersonAdd,
AttachMoney,
Build,
Settings,
} from '#material-ui/icons';
const styles = makeStyles((theme) => ({
root: {
display: 'flex',
},
drawerPaper: {
backgroundColor: theme.primary,
},
}));
export class DrawerMenu extends Component {
render() {
const { classes, openDrawer, handleDrawer } = this.props;
return (
<div className='drawer-container'>
<Drawer
variant='persistent'
anchor='left'
open={false}
classes={{
paper: classes.drawerPaper,
}}
backgroundColor='primary'
>
<div className='logo-icon'>
<div className='logo'>Logo</div>
<div className='collapse-icon'>
<IconButton onClick={() => handleDrawer(false)}>
<ChevronLeft />
</IconButton>
</div>
</div>
<Divider />
<div className='drawer-menu-container'>
<div className='drawer-menu'>
<List>
{[
'Booking',
'Positions',
'User Management',
'Trading',
'Instruments',
].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index === 0 && <NoteAdd />}
{index === 1 && <Person />}
{index === 2 && <PersonAdd />}
{index === 3 && <AttachMoney />}
{index === 4 && <Build />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
</div>
<div className='drawer-action-button'>
<div className='setting-logout'>
<div className='setting'>
<Settings />
</div>
<div className='logout'>
<Button variant='contained' color='primary'>
Logout
</Button>
</div>
</div>
</div>
</div>
</Drawer>
</div>
);
}
}
export default withStyles(styles, { withTheme: true })(DrawerMenu);
Your problem could be related to the fact that your Drawer is not on a Fragment. Try to modify your Drawer component code like:
import React, { Component, Fragment } from 'react'; //<-- import Fragment
...
export class DrawerMenu extends Component {
render() {
<div className='drawer-container'>
<Fragment>
<Drawer
variant='persistent'
anchor='left'
open={false}
classes={{
paper: classes.drawerPaper,
}}
backgroundColor='primary'
>
...
</Drawer>
</Fragment>
</div>
};
}
This should solve your problem.
first time exploring Gatsby and tried to install react-modal. Added modal component inside my ComponentOne and inside the modal is another component which is ComponentTwo where the close trigger resides. Already passed the onClick to ComponentTwo but still no luck. I think I miss something.
ComponentOne
import React, { Component } from "react"
import ComponentTwo from "./ComponentTwo"
import Modal from 'react-modal'
export default class ComponentOne extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
}
handleOpenModal() {
this.setState({ showModal: true });
}
handleCloseModal() {
this.setState({ showModal: false });
}
render() {
return (
<div className="container mx-auto h-50">
<Modal
isOpen={this.state.showModal}
ariaHideApp={false}
style={cotentStyle}
>
<ComponentTwo onClick={this.handleCloseModal} /> // this is component two
</Modal>
</div>
)
}
}
ComponentTwo
import React from 'react'
import { TiDelete } from 'react-icons/ti'
const NavContent = ({ data }, props) => (
<>
<div className="bg-black z-10 absolute inset-0">
<button onClick={props.onClick} className="text-white absolute top-0 right-0 my-4 mx-4">
<TiDelete size={'2em'} />
</button>
</div>
</>
)
export default function Nav(props) {
return (
<StaticQuery
query={worksQuery}
render={data => <NavContent data={data} {...props} />}
/>
)
}
Try with:
export default function Nav({onClick}) {
return (
<StaticQuery
onClick={onClick}
query={worksQuery}
render={data => <NavContent data={data} {...props} />}
/>
)
}
I´m a bit new to React and cant get this marginTop to work:
const pStyle = {
marginTop: '40px'
};
nothing happens when I apply it in the div and I think I missed something please advice
import React, { Component } from "react";
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
/* Import Components */
import Input from "../components/input/Input";
import Button from "../components/button/Button";
import { actionCreators } from '../store/ContainerReducer';
const pStyle = {
marginTop: '40px'
};
class FormContainer extends Component {
constructor(props) {
super(props);
this.state = {
localBook: {
title: "",
author: "",
genre: "",
price: ""
},
};
this.handleTitle = this.handleTitle.bind(this);
this.handleAuthor = this.handleAuthor.bind(this);
this.handleGenre = this.handleGenre.bind(this);
this.handlePrice = this.handlePrice.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleClearForm = this.handleClearForm.bind(this);
}
/* This lifecycle hook gets executed when the component mounts */
handleTitle(e) {
let value = e.target.value;
this.setState(
prevState => ({
localBook: {
...prevState.localBook,
title: value
}
}),
() => console.log(this.state.localBook)
);
}
handleAuthor(e) {
let value = e.target.value;
this.setState(
prevState => ({
localBook: {
...prevState.localBook,
author: value
}
}),
() => console.log(this.state.localBook)
);
}
handleGenre(e) {
let value = e.target.value;
this.setState(
prevState => ({
localBook: {
...prevState.localBook,
genre: value
}
}),
() => console.log(this.state.localBook)
);
}
handlePrice(e) {
let value = e.target.value;
this.setState(
prevState => ({
localBook: {
...prevState.localBook,
price: value
}
}),
() => console.log(this.state.localBook)
);
}
handleFormSubmit(e) {
e.preventDefault();
this.props.requestBooks(this.state.localBook);
}
handleClearForm(e) {
e.preventDefault();
this.setState({
localBook: {
title: "",
author: "",
genre: "",
price: ""
}
});
}
render() {
return (
<div class="pStyle">
<form className="container-fluid" onSubmit={this.handleFormSubmit}>
<Input
inputType={"text"}
title={"Title"}
name={"title"}
value={this.state.localBook.title}
placeholder={"Enter Title"}
handleChange={this.handleTitle}
/>{" "}
{/* Title */}
<Input
inputType={"text"}
title={"Author"}
name={"author"}
value={this.state.localBook.author}
placeholder={"Enter Author"}
handleChange={this.handleAuthor}
/>{" "}
{/* Author */}
<Input
inputType={"text"}
title={"Genre"}
name={"genre"}
value={this.state.localBook.genre}
placeholder={"Select Genre"}
handleChange={this.handleGenre}
/>{" "}
{/* Genre */}
<Input
inputType={"text"}
title={"Price"}
name={"price"}
value={this.state.localBook.price}
placeholder={"Select Price"}
handleChange={this.handlePrice}
/>{" "}
{/* Price */}
<Button
action={this.handleFormSubmit}
type={"primary"}
title={"Submit"}
style={buttonStyle}
/>{" "}
{/*Submit */}
<Button
action={this.handleClearForm}
type={"secondary"}
title={"Clear"}
style={buttonStyle}
/>{" "}
{/* Clear the form */}
</form>
</div>
);
}
}
const buttonStyle = {
margin: "10px 10px 10px 10px"
};
export default connect(
null,
dispatch => bindActionCreators(actionCreators, dispatch)
)(FormContainer);
I have this App.js
import React from 'react';
import { Route } from 'react-router';
import Layout from './components/Layout';
import Posts from './components/Posts';
import FormContainer from './components/FormContainer';
import 'typeface-roboto';
export default () => (
<Layout>
<Route exact path='/' component={FormContainer} />
<Route exact path='/' component={Posts} />
</Layout>
);
I have this Layout.js
import React from 'react';
import { Container } from 'reactstrap';
import '../../node_modules/primereact/resources/primereact.css';
import '../../node_modules/primereact/resources/themes/nova-dark/theme.css';
import NavMenu from './NavMenu';
export default props => (
<div>
<NavMenu />
<Container>
{props.children}
</Container>
</div>
);
you have to set your style to the div
<div style={pStyle}>
</div>
You should add your style not by using class keyword, but by using style keyword. And you should wrap it in curly braces, because pStyle is a JSX constant, not a string.
P.S.: Also, in React you should use className instead of class. Just FYI.
Here is how things should work in your case:
<div style={pStyle}>
<form className="container-fluid" onSubmit={this.handleFormSubmit}>
<Input
inputType={"text"}
title={"Title"}
name={"title"}
value={this.state.localBook.title}
placeholder={"Enter Title"}
handleChange={this.handleTitle}
/>{" "}
{/* Title */}
<Input
inputType={"text"}
title={"Author"}
name={"author"}
value={this.state.localBook.author}
placeholder={"Enter Author"}
handleChange={this.handleAuthor}
/>{" "}
{/* Author */}
<Input
inputType={"text"}
title={"Genre"}
name={"genre"}
value={this.state.localBook.genre}
placeholder={"Select Genre"}
handleChange={this.handleGenre}
/>{" "}
{/* Genre */}
<Input
inputType={"text"}
title={"Price"}
name={"price"}
value={this.state.localBook.price}
placeholder={"Select Price"}
handleChange={this.handlePrice}
/>{" "}
{/* Price */}
<Button
action={this.handleFormSubmit}
type={"primary"}
title={"Submit"}
style={buttonStyle}
/>{" "}
{/*Submit */}
<Button
action={this.handleClearForm}
type={"secondary"}
title={"Clear"}
style={buttonStyle}
/>{" "}
{/* Clear the form */}
</form>
</div>
I'm trying to implement Sub-menu (nested menu).
It's worth to mention that I'm using hydra component and don't have previous experience with redux (started learning it a few days ago because of this specific problem).
I've followed the example provided on material-ui for nested list https://material-ui.com/demos/lists/#nested-list. And tutorial from
https://marmelab.com/react-admin/Theming.html#using-a-custom-menu for custom menu implementation.
So I have a few questions.
1) Can I have stateful component (MyMenu) just for handling toggling of menu items?
An example is not related to react-admin but its just example what I mean.
import React, { Component } from "react";
import { connect } from "react-redux";
import { addArticle } from "../actions/index";
const mapDispatchToProps = dispatch => {
return {
addArticle: article => dispatch(addArticle(article))
};
};
class ConnectedForm extends Component {
constructor() {
super();
this.state = {
title: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
const { title } = this.state;
const id = uuidv1();
this.props.addArticle({ title, id });
this.setState({ title: "" });
}
render() {
const { title } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
value={title}
onChange={this.handleChange}
/>
</div>
<button type="submit" className="btn btn-success btn-lg">
SAVE
</button>
</form>
);
}
}
const Form = connect(null, mapDispatchToProps)(ConnectedForm);
export default Form;
2) If not, can I achieve that by declaring a new state in store, for example, open: false, and then using the custom reducer to handle that.
3(bonus). If it's not a problem I would appreciate if someone can put me in the right direction which things to start learning first so I can less painfully manage to solve issues related to this amazing framework :)
The react-admin demo now shows a way to do so in examples/demo/src/layout/Menu.js:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import SettingsIcon from '#material-ui/icons/Settings';
import LabelIcon from '#material-ui/icons/Label';
import { withRouter } from 'react-router-dom';
import {
translate,
DashboardMenuItem,
MenuItemLink,
Responsive,
} from 'react-admin';
import visitors from '../visitors';
import orders from '../orders';
import invoices from '../invoices';
import products from '../products';
import categories from '../categories';
import reviews from '../reviews';
import SubMenu from './SubMenu';
class Menu extends Component {
state = {
menuCatalog: false,
menuSales: false,
menuCustomers: false,
};
static propTypes = {
onMenuClick: PropTypes.func,
logout: PropTypes.object,
};
handleToggle = menu => {
this.setState(state => ({ [menu]: !state[menu] }));
};
render() {
const { onMenuClick, open, logout, translate } = this.props;
return (
<div>
{' '}
<DashboardMenuItem onClick={onMenuClick} />
<SubMenu
handleToggle={() => this.handleToggle('menuSales')}
isOpen={this.state.menuSales}
sidebarIsOpen={open}
name="pos.menu.sales"
icon={<orders.icon />}
>
<MenuItemLink
to={`/commands`}
primaryText={translate(`resources.commands.name`, {
smart_count: 2,
})}
leftIcon={<orders.icon />}
onClick={onMenuClick}
/>
<MenuItemLink
to={`/invoices`}
primaryText={translate(`resources.invoices.name`, {
smart_count: 2,
})}
leftIcon={<invoices.icon />}
onClick={onMenuClick}
/>
</SubMenu>
<SubMenu
handleToggle={() => this.handleToggle('menuCatalog')}
isOpen={this.state.menuCatalog}
sidebarIsOpen={open}
name="pos.menu.catalog"
icon={<products.icon />}
>
<MenuItemLink
to={`/products`}
primaryText={translate(`resources.products.name`, {
smart_count: 2,
})}
leftIcon={<products.icon />}
onClick={onMenuClick}
/>
<MenuItemLink
to={`/categories`}
primaryText={translate(`resources.categories.name`, {
smart_count: 2,
})}
leftIcon={<categories.icon />}
onClick={onMenuClick}
/>
</SubMenu>
<SubMenu
handleToggle={() => this.handleToggle('menuCustomer')}
isOpen={this.state.menuCustomer}
sidebarIsOpen={open}
name="pos.menu.customers"
icon={<visitors.icon />}
>
<MenuItemLink
to={`/customers`}
primaryText={translate(`resources.customers.name`, {
smart_count: 2,
})}
leftIcon={<visitors.icon />}
onClick={onMenuClick}
/>
<MenuItemLink
to={`/segments`}
primaryText={translate(`resources.segments.name`, {
smart_count: 2,
})}
leftIcon={<LabelIcon />}
onClick={onMenuClick}
/>
</SubMenu>
<MenuItemLink
to={`/reviews`}
primaryText={translate(`resources.reviews.name`, {
smart_count: 2,
})}
leftIcon={<reviews.icon />}
onClick={onMenuClick}
/>
<Responsive
xsmall={
<MenuItemLink
to="/configuration"
primaryText={translate('pos.configuration')}
leftIcon={<SettingsIcon />}
onClick={onMenuClick}
/>
}
medium={null}
/>
<Responsive
small={logout}
medium={null} // Pass null to render nothing on larger devices
/>
</div>
);
}
}
const mapStateToProps = state => ({
open: state.admin.ui.sidebarOpen,
theme: state.theme,
locale: state.i18n.locale,
});
const enhance = compose(
withRouter,
connect(
mapStateToProps,
{}
),
translate
);
export default enhance(Menu);
I've searched the same question. But couldn't find a nested menu implementation. So I wrote my own. Check the code below;
import React, { Component, createElement } from "react";
import {
Admin,
Resource,
Layout,
MenuItemLink,
getResources
} from "react-admin";
import jsonServerProvider from "ra-data-json-server";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import {
List,
ListItem,
Collapse,
ListItemText,
ListItemIcon
} from "#material-ui/core";
import { ExpandLess, ExpandMore, StarBorder, LabelIcon } from "#material-ui/icons";
import { withStyles } from "#material-ui/core/styles";
const menuStyles = theme => ({
nested: {
paddingLeft: theme.spacing.unit * 4
}
});
class Menu extends Component {
menuList = [
{ name: "A", label: "Top menu 1", icon: <LabelIcon /> },
{ name: "B", label: "Top menu 2", icon: <LabelIcon /> },
{ name: "c", label: "Top menu 3", icon: <LabelIcon /> }
];
constructor(props) {
super(props);
this.state = { open: "A" };
}
render() {
const { resources, onMenuClick, logout } = this.props;
return (
<div>
<List component="nav">
{this.menuList.map(menu => {
return (
<div key={menu.name}>
<ListItem
button
onClick={() => this.setState(state => ({ open: menu.name }))}
>
<ListItemIcon>{menu.icon}</ListItemIcon>
<ListItemText inset primary={menu.label} />
{this.state.open == menu.name ? (
<ExpandLess />
) : (
<ExpandMore />
)}
</ListItem>
<Collapse
in={this.state.open == menu.name}
timeout="auto"
unmountOnExit
>
<List component="div" disablePadding>
{resources
.filter(x => x.options.menu == menu.name)
.map((resource, i) => (
<MenuItemLink
key={"m" + i}
to={`/${resource.name}`}
primaryText={resource.options.label || resource.name}
leftIcon={
resource.icon
? createElement(resource.icon)
: undefined
}
onClick={onMenuClick}
className={this.props.classes.nested}
/>
))}
</List>
</Collapse>
</div>
);
})}
</List>
</div>
);
}
}
var MenuWithStyles = withStyles(menuStyles)(Menu);
const MyMenu = withRouter(
connect(state => ({
resources: getResources(state)
}))(MenuWithStyles)
);
const MyLayout = props => <Layout {...props} menu={MyMenu} />;
const App = () => (
<Admin
...
appLayout={MyLayout}
>
<Resource
...
options={{ label: 'Page 1' menu: "A" }}
/>
<Resource
...
options={{ label: 'Page 2' menu: "A" }}
/>
<Resource
...
options={{ label: 'Page 3' menu: "B" }}
/>
</Admin>
);
I need to build a drop down menu in a modal popup window with using Material UI
I have drop down menu in my modal window, and as a value I see the last item is "Third" and when I want to select for instance "First" it doesn't work, the menu doesn't select it and still having the last one item as a value in menu
I have 2 files App.js and list.js
App.js code:
import React, { Component } from 'react';
import './App.css';
import {AppButtons} from './components/button'
import {AppList} from './components/list'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
export default class App extends Component {
constructor (props) {
super (props)
this.state = {
isModalOpen: false,
itemsList: [
{name: 'First'},
{name: 'Second'},
{name: 'Third'}
],
}
this.handleChange = this.handleChange.bind(this)
}
handleChange = () => this.setState({this.state.itemsList});
render() {
const actions = [
<FlatButton
label="Save"
primary={true}
onClick={() => this.setState({isModalOpen: false})}
/>,
<FlatButton
label="Cancel"
primary={true}
onClick={() => this.setState({isModalOpen: false})}
/>,
];
return (
<div className="container">
<AppButtons
openModal = {() => this.setState ({isModalOpen: true})}
/>
<Dialog
title="Numbers structure"
actions={actions}
open={this.state.isModalOpen}
onRequestClose={() => this.setState({isModalOpen: true})}
>
<AppList
items = {this.state.itemsList}
/>
</Dialog>
</div>
);
}
}
And list.js code:
import React from 'react'
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
const styles = {
customWidth: {
width: 200,
},
};
export const AppList = (props) => {
return (
<div>
<DropDownMenu
style={styles.customWidth}
onChange={this.handleChange}
>
{props.items.map((item, key) => {
return (
<MenuItem
primaryText = {item.name}
openImmediately={true}
autoWidth={false}/>
)
})
}
</DropDownMenu>
<br />
</div>
)
}
Here is the shot
It's because you aren't passing down your handleChange function as a prop to <AppList/>:
<AppList
items = {this.state.itemsList}
/>
Change it to:
<AppList
items = {this.state.itemsList}
handleChange={this.handleChange}
/>
And then in your AppList component, the Dropdown component needs to use this.props.handleChange instead of this.handleChange:
<DropDownMenu
style={styles.customWidth}
onChange={this.props.handleChange}
>
...
</DropDownMenu>