Im using Material UI's Stepper component and trying to get the fill of a stepper that is in its error state to be red.
For reference, here is the Stepper component in Material UI's docs.
So im trying to show an error state. Material UI's has an error prop that will show the error state, however, I dont want the icon provided.
I just want it to be like any other stepper, just with a red background.
Is there any way I can get rid of that icon, and just show a red fill?
Been search all over but seems like no one really asked about this.
Here is my code:
<Stepper alternativeLabel activeStep={this.determineFormStep()} connector={<StepConnector />} className={classes.stepper}>
{formLabels.map((label) => {
return (
<Step key={label}>
<StepLabel
error
StepIconProps={{
classes: {
root: classes.step,
completed: classes.completed,
active: classes.active,
error: classes.error,
disabled: classes.disabled
}
}}>
<div className={classes.stepLabelRoot}>
<Typography className={classes.label}>
{label.label}
</Typography>
<span className={classes.sublabel}>
{label.sublabel1}
</span>
<span className={classes.sublabel}>
{label.sublabel2}
</span>
<span className={classes.sublabel}>
{label.sublabel3}
</span>
</div>
</StepLabel>
</Step>);
})}
</Stepper>
The docs for StepLabel show that it can take an optional icon prop, which is a node that overrides the step icon.
As an example, if you wanted to hide the icon entirely you could pass a Fragment element as the icon:
<StepLabel
error
icon={<></>}
>
...
</StepLabel>
Or you can change the StepIconComponent or StepIconProps.
Here is an example turns off the error state on the icon only:
<StepLabel
error
StepIconProps={ {error: false} }
>
...
</StepLabel>
Related
I have a storybook project and created a new custom component. I used a hover state and when I hover the component, it updates its className and it just works fine. Named: ProductSize
And then, I created a new component to group the ProductSize component and named it as ProductSizeGroup and grouped them by the Json inside the ProductSizeGroup stories.
And here is the final product screen:
Here, I want to see the sizes when I hover the boxes. But, it shows me all the sizes all alone like this. Apparently, I only want to see XSmall when I hover to XS, Small in S etc..:
Edit: Many people asked for the coding side and that is here - a live coding example:
https://codesandbox.io/s/usestateissue-10l4l
So, how to solve it?
Here is the ProductSizeGroup component code displaying the ProductSize items and hover-triggered sizes
const ProductSizeGroup: React.FC<IProductSizeGroupProps> = (props) => {
const { ProductSizes } = props;
const [inHover, setHover] = useState(false);
return (
<Box style={{ width: "100%" }}>
<Typography>
{" "}
Size:
{ProductSizes.map((products: any) =>
inHover ? products.name : undefined
)}
</Typography>
<Box display="flex" justifyContent="flex-start" p={1} m={1}>
{ProductSizes.map((products: any) => (
<Box
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<ProductSize
inStock={products.inStock}
sizeText={products.sizeText}
name={products.name}
/>
</Box>
))}
</Box>
</Box>
);
};
The issue is that you're displaying the size via the following
Size:
{ProductSizes.map((products: any) =>
inHover ? products.name : undefined
)}
where inHover is simply a Boolean value. So this will either show all name values or nothing.
I think what would work better is something like the following where you set the hovered state to the value you want and simply display it
const [hovered, setHovered] = useState<string | undefined>();
return (
<!-- snip -->
<Typography>Size: {hovered}</Typography>
<!-- snip -->
{ProductSizes.map(product => (
<Box
onMouseEnter={() => setHovered(product.name)}
onMouseLeave={() => setHovered(undefined)}
>
<!-- etc -->
</Box>
))}
)
Take note that I've also removed some of your any typings in the sandbox.
In order to handle states, you can use the react context provider: https://reactjs.org/docs/context.html or react redux https://react-redux.js.org/.
Depending on how many states you have and what you want to do with them you can choose one of the two. React-redux loads only the states you need, it is faster, but it has a specific structure that you need to follow. It is more complex at the beginning, but you can easier handle multiple states.
Context provider is already installed with react, it is easier to set up, but it is slower since it loads all states on each page load.
import "./styles.css";
import React, { useState } from "react";
import { IconButton } from "#material-ui/core";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
export default function App() {
const [expand, setExpand] = useState({
arrowA: false,
arrowB: false,
arrowC: false,
arrowD: false
});
const handleChange = (e) => {
setExpand({
...expand,
[e.currentTarget.name]: !expand.arrowA,
[e.currentTarget.name]: !expand.arrowB,
[e.currentTarget.name]: !expand.arrowC,
[e.currentTarget.name]: !expand.arrowD
});
};
return (
<div className="App">
<h1>React conditional rendering</h1>
<h2>
The component below should render if the user clicks on the arrow, and
be removed if the user clicks the arrow again.
</h2>
<h5>
This arrow should show the first part of the text.{" "}
<IconButton
variant="contained"
size="small"
color="primary"
aria-label="expand"
name="arrowA"
onClick={handleChange}
>
{<ExpandMoreIcon />}
</IconButton>
</h5>
<h5>
This arrow show the second part of the text if clicked, and be possible
to remove.
<IconButton
variant="contained"
size="small"
color="primary"
aria-label="expand"
name="arrowB"
onClick={handleChange}
>
{<ExpandMoreIcon />}
</IconButton>
</h5>
<h5>
This arrow should show the third part of the text below.{" "}
<IconButton
variant="contained"
size="small"
color="primary"
aria-label="expand"
name="arrowC"
onClick={handleChange}
>
{<ExpandMoreIcon />}
</IconButton>
</h5>
<h5>
This was really the last part.{" "}
<IconButton
variant="contained"
size="small"
color="primary"
aria-label="expand"
name="arrowD"
onClick={handleChange}
>
{<ExpandMoreIcon />}
</IconButton>
</h5>
{expand.arrowA ? (
<h2>
This is the first start of the story, let's see if we can add the
rest.
</h2>
) : null}
{expand.arrowB ? (
<h2>This is the middle part, it starts to break soon.</h2>
) : null}
{expand.arrowC ? (
<h2>
We are nearing the end. But as you see, this state management is not
optimal.
</h2>
) : null}
{expand.arrowD ? (
<h2>
This is was all I wanted to show. But why is removing this so hard?
How to make this better?
</h2>
) : null}
</div>
);
}
first of all, thanks for being here! I am a beginner in React and have a question regarding how to make my state management more optimal and better working.
I am trying to render multiple components in one React hook, to avoid having to create many different hooks. But my solution isn't working properly and not very optimal. I lack knowledge how to make the state more dynamic, but I feel I am close. Here is the example in: Code Sandbox
If you open the Code Sandbox, in the second attempt file, I am trying to set [e.currentTarget.value]: [e.currentTarget.status] to try to get the status from the IconButton, which I there store as status={!expand.checkedA} . But that isn't how React state management works apparently 😅.
Could someone enlighten me on what's best practice here? If you have a recommendation for an alternative way to conditionally render multiple components, I'd also be curious.
Cheers.
I think you're doing fine I would just change a few things to make it work:
First make the handleChange do this instead of the current code:
const handleChange = (e) => {
setExpand({
...expand,
[e.currentTarget.name]: !expand[e.currentTarget.name]
});
};
In this way you will only change the one you're clicking and the rest will stay the same.
Secondly you don't need the ternary operator in your jsx, you can use the && operator:
{expand.arrowA && (
<h2>
This is the first start of the story, let's see if we can add the
rest.
</h2>
)}
Check the forked sandbox if you have questions:
https://codesandbox.io/s/cool-fog-h7vct?file=/src/App.js:2073-2226
In my react app, I'm trying to achieve the results of toggling background colors of a graph, even if a graph is already loaded/rendered on screen.
With my current code, if I click the checkbox to toggle the colors it works perfectly fine as far as setting the colors in state and passing those into my component, but I have to reload the component on screen to see the changes, but they do show at that point.
I've tried to use forceUpdate() to rectify this but to no avail
What should I do at this point to make sure that when the state changes in this function it reloads the component completely?
state = {
renderCalories: true,
graphBackgroundColor: '#fff',
graphLabelColor: '#000'
};
toggleCOlor = event => {
console.log('toggling color');
this.setState({
graphBackgroundColor: '#000',
graphLabelColor: '#fff'
});
this.forceUpdate();
}
render() {
return (
<div>
<div className={styles.dropDown}>
<FormControl style={{minWidth: 120}} className={styles.formControl}>
<FormControlLabel
value="end"
control={<Checkbox color="primary" />}
label="Toggle Graph Background Color"
labelPlacement="end"
onChange={this.toggleCOlor}
/>
</FormControl>
</div>
{this.state.renderCalories && <NetCalorieTrend backgroundColor={this.state.graphBackgroundColor} labelColor={this.state.graphLabelColor} />}
...
}
I am a beginner, trying to understand react-popper. On their github, this is the example code:
import { Manager, Reference, Popper } from 'react-popper';
const Example = () => (
<Manager>
<Reference>
{({ ref }) => (
<button type="button" ref={ref}>
Reference element
</button>
)}
</Reference>
<Popper placement="right">
{({ ref, style, placement, arrowProps }) => (
<div ref={ref} style={style} data-placement={placement}>
Popper element
<div ref={arrowProps.ref} style={arrowProps.style} />
</div>
)}
</Popper>
</Manager>
);
Is this as it stands there supposed to be working? I am not sure I understand how I could use this code, how it would look like if I were to import/use this Example Component into my own parent component; Would I need to pass refs? How so? It would be great if somebody could give me a working example of this code, I'd just like to see how to make use of this.
I am guessing that you aim for a tooltip-like functionality. Is it what you are trying to achieve ?
Bear in mind that react-popper has a low level API. It is a positional engine rather than a tooltip library. By using it you would have to implement the logic of displaying the Popper yourself. For instance you could have a variable shouldShowElement and trigger changes to this
...
<Manager>
<Reference>
{({ ref }) => (
<button
type="button"
ref={ref}
onMouseEnter={() => {
/* [[SOME CODE TO SET shouldShowElement TO TRUE]] */
}}
onMouseLeave={() => {
/* [[SOME CODE TO SET shouldShowElement TO FALSE]] */
}}
>
Reference element
</button>
)}
</Reference>
{shouldShowElement ? (
<Popper placement="right">
{({ ref, style, placement, arrowProps }) => (
<div ref={ref} style={style} data-placement={placement}>
Popper element
<div ref={arrowProps.ref} style={arrowProps.style} />
</div>
)}
</Popper>
) : null}
</Manager>
...
If you want a more high-level API you can use react-tippy
or react-popper-tooltip
Don't bother too much with the render props. In most of the cases, you won't need to modify them.
Just keep in mind that
ref render prop from Reference should be attached to the
reference element (your item where is attached your popper).
ref render prop from Popper should be attached on the popper
(your tooltip or whatever).
style and placement render prop is for the geographical position on the page.
In resume, this example will render a button with an arrowed popperized <div> which contains "Popper element".
Currently i have the following structure
<OverlayTrigger trigger={["hover", "focus", "click"]} placement="bottom" overlay={(
<Popover className="timeline-popover-container" id="tpc-1">
<TimelinePopover
alert={session}
previousAlert={prevSession}
nextAlert={nextSession}
status={status}
/>
</Popover>
)}>
<div className="myclass">
<div>{img}</div>
</div>
</OverlayTrigger>
So, when the popover is triggered and i try to hover over the popover, the popover dissapear.
I want to be able to click inside de popover, do things inside of that and just dissapear when i move the mouse out of it.
I did a little component to handle this use case (inspired from the answer by #AnnieP).
https://gist.github.com/lou/571b7c0e7797860d6c555a9fdc0496f9
Usage:
<PopoverStickOnHover
component={<div>Holy guacamole! I'm Sticky.</div>}
placement="top"
onMouseEnter={() => { }}
delay={200}
>
<div>Show the sticky tooltip</div>
</PopoverStickOnHover>
I manage to make that work using one of the comments that ochi posted.
<OverlayTrigger trigger={["hover"]} placement="bottom" overlay={(
<Popover onMouseOver={this.showOverlay} onMouseOut={this.hideOverlay}>
content
</Popover>
)}>
<div onMouseOver={this.showOverlay} onMouseOut={this.hideOverlay}>
<div>bla bla bla</div>
</div>
</OverlayTrigger>
adding trigger on the popover and on the div i want to trigger worked.
I was looking to do the same thing using React Bootstrap except with a tooltip instead of a popover. The answer here gave me a good jumping off point, which I mixed with the Custom Overlay example in the React Bootstrap docs.
I replaced OverlayTrigger with a custom Overlay, which wraps the Tooltip but is outside of the target element, whereas the OverlayTrigger wraps the target element and calls Tooltip through the overlay prop. I added onMouseEnter() and onMouseLeave() to both the tooltip and the target element to toggle the tooltip visibility state so that leaving either one will close the tooltip.
This is a bare-bones version of my implementation:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Tooltip, Overlay } from 'react-bootstrap';
const TooltipExample extends Component {
constructor(props) {
super(props);
this.state = {
showTooltip: false,
};
}
render() {
let myTooltip = (
<Tooltip
onMouseEnter={() => this.setState({ showTaskTooltip: true })}
onMouseLeave={() => this.setState({ showTaskTooltip: false })}
>
I'm a tooltip and I'll stay open when you leave the target element and hover over me!
</Tooltip>
);
return(
<div>
<h3
ref="target"
onMouseEnter={() => this.setState({ showTooltip: true })}
onMouseLeave={() => this.setState({ showTooltip: false })}
>
Hover over me!
</h3>
<Overlay
show={this.state.showTooltip}
onHide={() => this.setState({ showTooltip: false })}
placement="bottom"
target={() => ReactDOM.findDOMNode(this.refs.target)}
>
{myTooltip}
</Overlay>
</div>
);
}
}
export default TooltipExample;