Current Behavior 😯
When having a Button with a "contained" variant in a Transition (Slide, Grow or Fade) the Button's variant is no longer recognized.
Expected Behavior 🤔
The Button should have a background color.
Steps to Reproduce 🕹
`import { ButtonGroup, Button, Slide } from '#material-ui/core';`
<ButtonGroup variant="text" color="primary" aria-label="menu" id="buttongroup">
{sections.map(x => <Button onClick={() => window.location.href = `#${x.name.toLowerCase()}`} key={x.name}>{x.name}</Button>)}
<Button onClick={() => window.open('tel:00918779839201')}>(+91) 8779839201</Button>
<Slide
direction="left"
in={useScrollTrigger({threshold: document.documentElement.clientHeight/1.5})}
mountOnEnter
unmountOnExit
>
<Button variant="contained">Schedule Site Visit</Button>
</Slide>
</ButtonGroup>
You can see an example here.
Just scroll down and you'll see the Button sliding in on the top right of the Appbar.
ButtonGroup assumes that its direct children are Button elements. Unless the child has the variant prop specified, ButtonGroup will give the child its own variant. In your case, you have a child that is a Slide element and that Slide element does not have a variant specified, so ButtonGroup gives it one (text in your case). Slide passes this property on to the element it wraps and thus overrides the variant of your contained Button.
You can overcome this by specifying the variant on the Slide element:
import React from "react";
import Button from "#material-ui/core/Button";
import ButtonGroup from "#material-ui/core/ButtonGroup";
import Slide from "#material-ui/core/Slide";
export default function App() {
const [slideIn, setSlideIn] = React.useState(false);
return (
<ButtonGroup variant="text">
<Button onClick={e => setSlideIn(true)}>Slide In</Button>
<Button onClick={e => setSlideIn(false)}>Slide Out</Button>
<Slide
variant="contained"
direction="left"
in={slideIn}
mountOnEnter
unmountOnExit
>
<Button variant="contained">Schedule Site Visit</Button>
</Slide>
</ButtonGroup>
);
}
Related
Everything is in the title. Here is the CodeSandbox with reproduction !
When using MUI's ButtonGroup, the layout is fine with three "regular" buttons:
<ButtonGroup variant="contained" aria-label="outlined primary button group">
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
Result:
But as soon as we add an href to one of the buttons, it breaks the layout, like so:
<ButtonGroup variant="contained" aria-label="outlined primary button group">
<Button href="test.com">One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
Result:
Is there any way to fix this ? I don't want to have to use the trick of onClick and location.replace or location.href...
Also, maybe I should post this as an issue on MUI's GitHub? I'm guessing only if it is relevant and not fixable, but there are so many I doubt they would see it.
Use the prop LinkComponent="button":
import * as React from "react";
import Button from "#mui/material/Button";
import ButtonGroup from "#mui/material/ButtonGroup";
export default function BasicButtonGroup() {
return (
<ButtonGroup variant="contained" aria-label="outlined primary button group">
<Button LinkComponent="button" href="test.com">
One
</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
);
}
Demo: https://codesandbox.io/s/basicbuttongroup-material-demo-forked-43pmhn?file=/demo.js:0-430
I am a new material UI, I am trying to use Typography, if I don't use variant in the typography tag, it is fine
<div className="login-details">
<Typography>{details ? details.email : "N/A"} |</Typography>
<Button type="link" onClick={()=> document.dispatchEvent(new CustomEvent("user.start_logout"))}>
logout
</Button>
</div>
if I use a variant like below, the UI is falling apart
<div className="login-details">
<Typography variant="h2">{details ? details.email : "N/A"} |</Typography>
<Button type="link" onClick={() => document.dispatchEvent(new CustomEvent("user.start_logout"))}>
logout
</Button>
</div>
Please help me.
Thanks in Advance.
Not using a variant is equivalent to variant="body1". Check your h2 style, particularly the line height and margins. Refer to the documentation on how to customise the default typography.
Basically the title. I want to manually show the ripple effect on my collection of MUI's Buttons - the ripple effect works when I click the button, but I can't find a way to show the ripple effect programmatically.
Do I need to disable the MUI's ripple effect and then make my own ripple effect function, that I can attach to onClick?
{this.buttons.map((button) => (
<React.Fragment key={button.name}>
{button.render ? (
<div className="col-3">
<Button
autoFocus={true}
className="w-100 p-3"
variant="contained"
color="primary"
classes={{root: classes.button}}
disableElevation
onClick={() => {updateState(button.onClick(text))}}
>
{button.keyCode}
</Button>
</div>
) : (<></>)}
</React.Fragment>
))}
Button uses ButtonBase which uses TouchRipple under the hood. Here is a snippet of ButtonBase definition:
function ButtonBase(props) {
// ...
return (
<ButtonBaseRoot>
{children}
<TouchRipple ref={rippleRef} center={centerRipple} {...TouchRippleProps} />
</ButtonBaseRoot>
)
}
There is no API to trigger the ripple manually. It's handled internally inside the button, so in order to do that, you need to create and control your own ripple component provided by MUI:
import TouchRipple from '#mui/material/ButtonBase/TouchRipple';
const rippleRef = React.useRef(null);
const buttonRef = React.useRef(null);
const triggerRipple = () => {
const container = buttonRef.current;
const rect = container.getBoundingClientRect();
rippleRef.current.start(
{
clientX: rect.left + rect.width / 2,
clientY: rect.top + rect.height / 2,
},
// when center is true, the ripple doesn't travel to the border of the container
{ center: false },
);
setTimeout(() => rippleRef.current.stop({}), 320);
};
return (
<div>
<Button onClick={triggerRipple}>start ripple</Button>
<Box display="flex" justifyContent="center" m={10}>
<Button
variant="contained"
color="primary"
ref={buttonRef}
sx={{ display: 'relative' }}
>
My little ripple
<TouchRipple ref={rippleRef} center />
</Button>
</Box>
</div>
);
Live Demo
Related answers
Is it possible to use the touch ripple effect of mui on a div?
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 am building a site using material-UI and have run into a bit of a snag. It seems that the button is being rendered with a span element around the button's text. This makes it so the click event only fires when you click outside the span element.
As you can imagine it's not the greatest UI to have users click buttons that don't do anything. I am sure that I am not the first one to go through this, is there a way that I can propagate the event down to the child element programmatically? Below is my component snipit:
import withRoot from '../onepirate/modules/withRoot';
// --- Post bootstrap -----
import React from 'react';
import { useState, useStyles } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Grid from '#material-ui/core/Grid';
import Link from '#material-ui/core/Link';
import Typography from '../onepirate/modules/components/Typography';
import AppFooter from '../onepirate/modules/views/AppFooter';
import AppForm from '../onepirate/modules/views/AppForm';
import Button from '#material-ui/core/Button';
import MuiTextField from '#material-ui/core/TextField';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import ListItemAvatar from '#material-ui/core/ListItemAvatar';
import Avatar from '#material-ui/core/Avatar';
import AddCircleIcon from '#material-ui/icons/AddCircle';
import WorkIcon from '#material-ui/icons/Work';
import BeachAccessIcon from '#material-ui/icons/BeachAccess';
import Divider from '#material-ui/core/Divider';
const Tickets = props => {
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1),
backgroundColor: "#ff3366",
color: "#000000"
},
input: {
display: 'none',
},
ListItemText: {
marginLeft: 105,
}
}));
const classes = useStyles();
return (
<React.Fragment>
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Order Tickets
</Typography>
<Typography variant="body2" align="center">
<Link href="/premium-themes/onepirate/sign-in/" underline="always">
Already have an account?
</Link>
</Typography>
</React.Fragment>
<Grid container spacing={2}>
<Grid item xs={12} sm={12}>
<List className={classes.root}>
<ListItem>
<Button
variant="contained"
className={classes.button}
onClick={props.quantity}
id="basic">
Add
</Button>
<ListItemText
primary="Basic"
secondary="$450"
className={classes.ListItemText}/>
{props.init.basic}
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<Button
variant="contained"
className={classes.button}
onClick={props.quantity}
id="exec" >
Add
</Button>
<ListItemText
primary="Executive"
secondary="$550"
className={classes.ListItemText}/>
{props.init.exec}
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<Button
variant="contained"
className={classes.button}
onClick={props.quantity}
id="vip">
Add
</Button>
<ListItemText
primary="VIP"
secondary="$750"
className={classes.ListItemText}/>
{props.init.vip}
</ListItem>
</List>
<Typography variant="h2">
{`Total: $ ${(props.init.basic * 450) + (props.init.exec * 550) + (props.init.vip * 750)}`}
</Typography>
<Button
className={classes.button}
size="large"
color="secondary"
fullWidth
onClick={props.price}
id=""
>
{'Proceed to Checkout'}
</Button>
</Grid>
</Grid>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Tickets);
And the rendered HTML output of one of the buttons:
<button class="MuiButtonBase-root-307 MuiButton-root-290 makeStyles-button-87 MuiButton-contained-298"
tabindex="0"
type="button"
id="exec">
<span class="MuiButton-label-291">Add</span>
<span class="MuiTouchRipple-root-391"></span>
</button>
It seems that Material UI v4 accepts both e.target.value and e.currentTarget.value.
However, if you use e.target.value to deliver buttons' value, and click on the span node (nested inside of button), the value won't be taken from the button node that you assigned it to, but the span HTML element that does not have the value property and so the value is undefined.
To remedy the above, use e.currentTarget.value as it seems to pick up the value property from the button, even if you click on the span node.
Below sandbox should show you the difference in using e.target.value and e.currentTarget.value with material UI buttons.
https://codesandbox.io/s/elated-glitter-wqtt3?file=/src/App.js
Open up the console and click interchangeably on span and button elements and observe logs when you do that.
I have not tested the latest v5 from Material UI, it could be that they changed (improved?) this behaviour.
try wrapping span and button inside a div or other wrapper component and call onClick on that