Button with Icon component - reactjs

I have a <Button /> component and an <Icon/> component.
I try to implement a button with an icon.
The Button.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Button from "../components/Button";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Button/Primary", module)
.add("With icon", () => (
<Button><Icon type={iconTypes.arrowRight}/></Button>
))
That works fine but I would like the api for a Button with an icon to be-
<Button icon={icons.arrow}>Click Me</Button>
How can I achieve that?
The Icon.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Icon", module)
.add("Arrow Right", () => (
<Icon type={iconTypes.arrowRight}>
</Icon>
))
.add("Arrow Left", () => (
<Icon type={iconTypes.arrowLeft}>
</Icon>
));
The <Button /> component:
import React from 'react';
import { css, cx } from 'emotion';
import colors from '../styles/colors';
export default function Button({
children,
...props
}) {
const mergedStyles = cx(
// css
);
// other css stuff
return (
<button {...props} disabled={disabled} className={mergedStyles}>
{children}
</button>
);
And <Icon /> component:
import React from "react";
import { css } from 'emotion';
import ArrowRight from "./arrow-right2.svg";
import ArrowLeft from "./arrow-left2.svg";
export const iconTypes = {
arrowRight: 'ARROW_RIGHT',
arrowLeft: 'ARROW_LEFT',
}
const iconSrc = {
ARROW_RIGHT: ArrowRight,
ARROW_LEFT: ArrowLeft,
}
const circleStyles = css({
width: 24,
height: 24,
borderRadius: "50%",
backgroundColor: "#f7f7f9",
display: "flex",
justifyContent: "center"
});
export default function Icon({ type }) {
return (
<div className={circleStyles}>
<img src={iconSrc[type]} />
</div>
)
};
Any help would be appreciated.

import React from 'react';
import {css, cx} from 'emotion';
import colors from '../styles/colors';
//import your ICON component & make sure your path is right
import Icon from "./../Icon";
export default function Button({
children,
disabled,
icon,
...props
}) {
const mergedStyles = cx(//your css);
return (
<button {...props} disabled={disabled} className={mergedStyles}>
// If icon prop is provided then render ICON component
{icon && <Icon type={icon}/>}
//Other children
{children}
</button>
);
}

in render of Button, you can do something like that :
Button.js:
render(){
const { icon } = this.props
return(
<Button>
{icon && <Icon type={icon}/>}
<Button>
)
}

Related

Why top navbar and sidebar not visible in grapejs?

Can you explain me the problem that I have in my code...I am trying to get grapesjs editor into my code...Can anyone help me out on this one??
import React, {useState, useEffect} from 'react';
import grapesjs from 'grapesjs';
import Basics from "grapesjs-blocks-basic"
import BaseReactComponent from "../../base-react-component";
import ReactComponents from "../../react-components";
import MuiComponents from "../../mui-components/index"
import Listing from '../../Listing';
import {Button, Slider, Snackbar} from "#material-ui/core"
import MuiButton from "mui-button";
const EditStepUI = (props) => {
const [editor, setEditor] = useState(true)
useEffect(() => {
const editor = grapesjs.init({
container: "#gjs",
height: "100%",
storageManager: false,
noticeOnUnload: false,
plugins: [Basics, BaseReactComponent, ReactComponents, MuiComponents]
});
console.log(editor);
}, [])
return (
<div id="editor">
editor.setComponents(`
<div>
<span>
Foo
</span>
<Listing>
<div>
Bar
</div>
<MuiButton variant='contained' color='primary'>
Click Me
</MuiButton>
<Slider />
</Listing>
<Snackbar>
<MuiButton variant='contained' color='secondary'>
Click Me
</MuiButton>
</Snackbar>
<Slider />
</div>
`);
</div>
)
}
export default EditStepUI;

Passing useState setter function to child component for a click event but getting setState is not a function

I am using Material UI Drawer for sidebar and passing the setReplyDrawer to child ReplyReview so that I can close it from the child with a click event but getting error that setReplyDrawer is not a function.
replyDrawer state is used for opening and close a modal.
<Button onClick={()=>setReplyDrawer(false)}>Back</Button>
can anyone please let me know what am i doing wrong or how to correct it?
import { Avatar, Box, Button, Container, CssBaseline, Divider, Rating, Select, Stack, SwipeableDrawer } from '#mui/material'
import React, { useState } from 'react'
import { AiFillCaretDown } from "react-icons/ai";
import MouseOverPopover from '../common/MouseOverPopover';
import SelectOptions from '../common/SelectOptions';
import ReplyReview from './ReplyReview';
import { BsChat } from "react-icons/bs";
const ReviewList = ({name,comment,createTime,reply,star}) => {
const [action, setAction] = useState('')
const [showMore, setShowMore] = useState(false)
const [replyDrawer, setReplyDrawer] = useState(false)
return (
<div className='review-list-container'>
{/* some code here*/}
<div className='review-buttons'>
{/* Reply Button */}
<MouseOverPopover
mainContent={
<Button size='medium' onClick={()=>setReplyDrawer(true)} variant="outlined">
<BsChat style={{fontSize:'27px'}} />
</Button>}
popoverContent={`Add Tyson Hinton as a contact to start messaging`}
/>
<div>
<SwipeableDrawer
sx={{
width: drawerWidth,
zIndex: (theme) => theme.zIndex.drawer + 1,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
},
padding:'20px'
}}
anchor={'right'}
open={replyDrawer}
onClose={()=>setReplyDrawer(false)}
onOpen={()=>setReplyDrawer(true)}
>
<ReplyReview name={name} replyDrawer={replyDrawer} setReplyDrawer={setReplyDrawer} />
</SwipeableDrawer>
</div>
</div>
</div>
)
}
export default ReviewList
ReplyReview.jsx
import { Button, Drawer, IconButton, InputBase, Paper, SwipeableDrawer } from '#mui/material'
import React, { useState } from 'react'
import { BsSearch } from "react-icons/bs";
import { AiOutlinePlusCircle } from "react-icons/ai";
import { BiArrowBack } from "react-icons/bi";
import './ReplyReview.css'
const ReplyReview = (name,replyDrawer,setReplyDrawer,first,setFirst) => {
const [buttonClicked, setButtonClicked] = useState(false)
const drawerWidth = 500;
console.log(replyDrawer,setReplyDrawer)
return (
<div className='reply-review-container'>
<header>
<Button onClick={()=>setReplyDrawer(false)}><BiArrowBack/></Button>
<strong>Start messaging {name.name}</strong>
</header>
</div>
)
}
export default ReplyReview
In ReplyReview the button is used to close the modal
You forgot to deconstruct the props
const ReplyReview = (name,replyDrawer,setReplyDrawer,first,setFirst) => {
to
const ReplyReview = ({name,replyDrawer,setReplyDrawer,first,setFirst}) => {
"name.name" should now be just "name"
Maybe you don't call that function in the Correct way
function ReplayReview(props){
// here props an object so you need to pull up any value from that object
return <Button onClick={()=>props.setReplyDrawer(false)}>Back</Button>
}
or
function ReplayReview({setReplayDrawer})=>{
//You can use object destructure in square bracket into function arguments. For Example: const obj={a:5,b:10}
// const {a,b}=obj or const a=obj.a ,const b=obj.b
return <Button onClick={()=>setReplayDrawer(false)}>back</Button>
}

Export variable - React

I have a component called Component1 in which I have the following code:
import React, { useState } from "react";
import Popover from "material-ui-popup-state/HoverPopover";
import Fab from "#material-ui/core/Fab";
import {
usePopupState,
bindHover,
bindPopover
} from "material-ui-popup-state/hooks";
import PaletteIcon from "#material-ui/icons/Palette";
import Colors from "./Colors";
const DEFAULT_COLOR = "red";
const COLORS = [/*list of colors*/];
const Component1 = ({ classes }) => {
const popupState = usePopupState({
variant: "popover",
popupId: "demoPopover"
});
const [selectedColor, setSelectedColor] = useState(DEFAULT_COLOR);
return (
<div className="box" style={{ backgroundColor: selectedColor }}>
<Fab variant="extended" {...bindHover(popupState)}>
<PaletteIcon />
</Fab>
<Popover
>
<div className="color-palette">
{COLORS.map((color) => (
<Colors
key={color}
selected={selectedColor === color}
onClick={setSelectedColor}
color={color}
/>
))}
</div>
</Popover>
</div>
);
};
export default Component1;
This component is imported in Component2 where the code is:
import React from "react";
import Component1 from "./Component1";
import Fab from "#material-ui/core/Fab";
import DeleteIcon from "#material-ui/icons/Delete";
function Component2(props) {
function handleClick() {
props.onDelete(props.id);
}
return (
<div className="note" style={{ backgroundColor: "selectedColor" }}>
<h1>{props.title}</h1>
<p>{props.content}</p>
<Fab onClick={handleClick}>
<DeleteIcon fontSize="small" />
</Fab>
<HoverPopover />
</div>
);
}
export default Component2;
In component2 I need to use the const selectedColor for styling purpose for div with class="note". However the issue is when I select colors from COLORS list the background-color of div with class="note" is not changing. I tried many options but I don't understand how to do it correctly. Please tell me how to do it right.
To share the "selectedColor" variable, which is actually a state, you would have to pass it through the props to the child component
Your "Component2" should declare the state "selectedColor", and this state and its function must be passed by the props to your "Component1".
https://reactjs.org/tutorial/tutorial.html#lifting-state-up

Converting class component (Navbar) to functional using hooks

I am converting the React + Material UI + Firebase project found on the Material UI doc's page. I am trying to open the SignUp Dialog (i.e. SignUp Modal). Here is a simplified version of the 4 corresponding files and I need help opening up the sign up modal.
App.js
import React, { useState } from 'react';
import { ThemeProvider } from '#material-ui/core/styles';
import Navbar from './components/Navbar'
import DialogHost from './components/DialogHost';
import Loading from './components/Loading'
import theme from './theme';
function App() {
const [signedIn] = useState(false)
const [ready] = useState(true) //!toggle for testing
const [dialog, setDialog] = useState({
isOpenSignUp: false,
isOpenSignIn: false
});
const openDialog = e => {
setDialog({...dialog,[e.target.name]: true});
}
const closeDialog = e => {
setDialog({...dialog,[e.target.name]: false});
}
return (
<ThemeProvider theme={theme}>
{!ready &&
<Loading />
}
{ready &&
<>
<Navbar
signedIn={signedIn}
onSignUpClick={openDialog}
onSignInClick={openDialog}
/>
<DialogHost
signedIn={signedIn}
dialogs={
{
signUpDialog: {
dialogProps: {
open: dialog.isOpenSignUp,
//onClose below hasn't been converted... not entirely sure how
onClose: (callback) => {
this.closeDialog('signUpDialog');
if (callback && typeof callback === 'function') {
callback();
}
}
}
},
}
}
/>
</>
}
</ThemeProvider>
);
}
export default App;
Navbar.js
import React from 'react'
import PropTypes from 'prop-types';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import Button from '#material-ui/core/Button';
const Navbar = ({ signedIn, onSignUpClick, onSignInClick }) => {
return (
<AppBar color="primary" position="static">
<Toolbar variant="regular">
<Box flexGrow={1}>
<Typography color="inherit" variant="h6">{process.env.REACT_APP_NAME}</Typography>
</Box>
{!signedIn &&
<>
<Box mr={1}>
<Button name="isOpenSignUp" color="secondary" variant="contained" onClick={onSignUpClick}>Sign Up</Button> //GAVE THE BUTTON NAMES, BUT THIS DOESN'T SEEM CORRECT...
</Box>
<Button name="isOpenSignIn" color="secondary" variant="contained" onClick={onSignInClick}>Sign In</Button>
</>
}
</Toolbar>
</AppBar>
)
}
Navbar.defaultProps = {
signedIn: false
};
Navbar.propTypes = {
signedIn: PropTypes.bool.isRequired
};
export default Navbar
DialogHost.js
import React from 'react'
import PropTypes from 'prop-types';
import Hidden from '#material-ui/core/Hidden';
import SignUpDialog from '../../pages/SignUpDialog';
const DialogHost = ({ signedIn, dialogs }) => {
const signUpDialog = dialogs.signUpDialog;
return (
<>
<Hidden xsDown>
{!signedIn &&
<>
<SignUpDialog
dialogProps={signUpDialog.dialogProps}
{...signUpDialog.props}
/>
</>
}
</Hidden>
<Hidden smUp>
{!signedIn &&
<>
<SignUpDialog
dialogProps={{
fullScreen: true,
...signUpDialog.dialogProps
}}
{...signUpDialog.props}
/>
</>
}
</Hidden>
</>
)
}
DialogHost.defaultProps = {
signedIn: false
};
DialogHost.propTypes = {
signedIn: PropTypes.bool.isRequired,
dialogs: PropTypes.object.isRequired
};
export default DialogHost
I also have a SignUpDialog.js file, but all it is is rendering a functional component with a title of 'Sign Up'

implement BackButton in react-admin

I need to implement a <BackButton /> in react-admin for example when I open show page for a resource I need able to back to the list page.
Can you guide me to implement this?
I'm not familiar with react-admin routing mechanism.
Now I'm using this component in my edit form actions props:
const MyActions = ({ basePath, data, resource }) => (
<CardActions>
<ShowButton basePath={basePath} record={data} />
<CloneButton basePath={basePath} record={data} />
{/* Need <BackButton /> here */}
</CardActions>
);
export const BookEdit = (props) => (
<Edit actions={<MyActions />} {...props}>
<SimpleForm>
...
</SimpleForm>
</Edit>
);
You can use react-router-redux's goBack() function to achieve this.
For example, your button component will look something like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '#material-ui/core/Button';
import { goBack } from 'react-router-redux';
class BackButton extends Component {
handleClick = () => {
this.props.goBack();
};
render() {
return <Button variant="contained" color="primary" onClick={this.handleClick}>Go Back</Button>;
}
}
export default connect(null, {
goBack,
})(BackButton);
Now use that button component in your CardActions.
You can get help from an example which uses react-router-redux's push() function in a similar way from the official docs.
Create a back button. This one will pass props and children (text) and uses react-router directly, which I think makes more sense and keeps your code simple.
// BackButton.js
import React from 'react'
import Button from '#material-ui/core/Button'
import { withRouter } from 'react-router'
const BackButton = ({ history: { goBack }, children, ...props }) => (
<Button {...props} onClick={goBack}>
{children}
</Button>
)
export default withRouter(BackButton)
Example usage:
import { Toolbar, SaveButton } from 'react-admin'
import BackButton from './BackButton'
const SomeToolbar = props => (
<Toolbar {...props}>
<SaveButton />
<BackButton
variant='outlined'
color='secondary'
style={{ marginLeft: '1rem' }}
>
Cancel
</BackButton>
</Toolbar>
)
The complete code is below.
//BackButton.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import compose from 'recompose/compose';
import { withStyles, createStyles } from '#material-ui/core/styles';
import { translate } from 'ra-core';
import Button from '#material-ui/core/Button';
import ArrowBack from '#material-ui/icons/ArrowBack';
import classnames from 'classnames';
import { fade } from '#material-ui/core/styles/colorManipulator';
const styles = theme =>
createStyles({
deleteButton: {
color: theme.palette.error.main,
'&:hover': {
backgroundColor: fade(theme.palette.error.main, 0.12),
// Reset on mouse devices
'#media (hover: none)': {
backgroundColor: 'transparent',
},
},
},
});
const sanitizeRestProps = ({
basePath,
className,
classes,
label,
invalid,
variant,
translate,
handleSubmit,
handleSubmitWithRedirect,
submitOnEnter,
record,
redirect,
resource,
locale,
...rest
}) => rest;
class BackButton extends Component {
static contextTypes = {
router: () => true, // replace with PropTypes.object if you use them
}
static propTypes = {
label: PropTypes.string,
refreshView: PropTypes.func.isRequired,
icon: PropTypes.element,
};
static defaultProps = {
label: 'ra.action.back',
icon: <ArrowBack />,
};
render() {
const {
className,
classes = {},
invalid,
label = 'ra.action.back',
pristine,
redirect,
saving,
submitOnEnter,
translate,
icon,
onClick,
...rest
} = this.props;
return (
<Button
onClick={this.context.router.history.goBack}
label={label}
className={classnames(
'ra-back-button',
classes.backButton,
className
)}
key="button"
{...sanitizeRestProps(rest)}>
{icon} {label && translate(label, { _: label })}
</Button>
)
}
}
const enhance = compose(
withStyles(styles),
translate
);
export default enhance(BackButton);
//Toolbar.js
import React from 'react';
import {
Toolbar,
SaveButton,
DeleteButton,
} from 'react-admin';
import { withStyles } from '#material-ui/core';
import BackButton from './BackButton'
const toolbarStyles = {
toolbar: {
display: 'flex',
justifyContent: 'space-between',
},
};
export const CustomEditToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<DeleteButton/>
<BackButton/>
</Toolbar>
));
export const CustomCreateToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<BackButton/>
</Toolbar>
));

Resources