ReactJS: How to dynamically render Material-UI's <MenuItem/> inside <DropDownMenu/>? - javascript

Using ReactJS + Material-UI, I have an array called colors and contains strings of different colors. Say for example the array colors has 3 color strings: "white", "blue", "green. Then I would like to render each color string has a <MenuItem/> inside a <DropDownMenu/> (http://www.material-ui.com/#/components/dropdown-menu). And once a <MenuItem/> is selected, I'd like to console log that particular color like, say chose "white": console.log("white").
So I used .forEach yet the does not show any strings and it is empty. What could I be doing wrong?
Here is the code:
constructor() {
super()
this.state = {
value: 1,
}
}
dropDownColorChange(event, index, value) {
this.setState({value: value})
//Not sure how to implement here dynamically based on array size. Would like to console.log the color string of the selected
}
render() {
var colors = ["white", "blue", "green"] //would be able to handle any array size
return (
<div>
<DropDownMenu
value={this.state.valueTwo}
onChange={this.dropDownColorChange}
>
{
<MenuItem value={1} primaryText="Select" />
colors.forEach(color => {
<MenuItem primaryText={color}/>
})
}
</DropDownMenu>
</div>
)
}
Thank you

You've almost got it right. You have to map over available colors and return a MenuItem for each color:
const colors = ['white', 'blue', 'green'];
class ColorChanger extends Component {
constructor() {
super();
this.state = {
selectedColorValue: 1,
};
}
handleColorChange(event, index, value) {
console.log(`You have selected ${colors[value]} color`);
this.setState({
selectedColorValue: value
});
}
render() {
return (
<div>
<DropDownMenu value={this.state.selectedColorValue} onChange={this.handleColorChange}>
{colors.map((color, index) =>
<MenuItem key={index} value={index} primaryText={color} />
)}
</DropDownMenu>
</div>
);
}
}
map (contrary to forEach) returns an array where each element is the return value of predicate function. In your case it returns a <MenuItem />.

I used the react hook to set the menu items on clicking my menu icon and I also set the value I want to pass to my action method.
const [menuItems, setMenuItems] = React.useState<IMenuItem[]>();
const [menuValue, setMenuValue] = React.useState<IMenuValue>();
const handleClickMenu = (
event: React.MouseEvent<HTMLElement>,
value: IMenuValue,
) => {
setMenuItems(value.menuItems);
setMenuTransaction(value);
setMenuAnchorEl(event.currentTarget);
};
return (
// ... code ...
<PositionedVertMenu
data-testid={`menu`}
open={Boolean(menuAnchorEl)}
anchorEl={menuAnchorEl}
onClick={(event: React.MouseEvent<HTMLElement>) => handleClickMenu(event, value)}
onClose={handleCloseMenu}
>
{menuValue &&
menuItems?.map((option, menuIndex) => (
<MenuItem
data-testid={`menu-item-${menuIndex}`}
onClick={() => option.action(menuValue, handleCloseMenu)}
>
<Typography>{translate(option.text)}</Typography>
</MenuItem>
))}
</PositionedVertMenu>
)

Related

How do I pass the data to the child component?

When I select a product, the other AutoComplete for colors should filter based on the available variations of colors where the quantity is not equal to 0. For example, if I'll select the product Tumbler, the colors should be Black, pink, and green. And if I'll select the product Notebook, the color should be Green, Red, and Black.
The list of the products shows in the AutoComplete as options (working)
According to the what product was selected, it shows the available colors whose quantity is not equal to 0 for that selected product. For example, I chose Tumbler, this will show the list of colors the tumbler has which are Black, Pink, and Green. And if I’ll choose the product Shirt, the list of colors that will display are Blue and Black. (Not working)
Pass the data that contains the available colors of the selected product to the NestedArray component and show up as options of the AutoComplete. (Not working)
How can I do this? Thank you.
I have recreated the problem in codesandbox:
FieldArray Component
const FieldArray = ({ products }) => {
const options = products.map(
(object) =>
object.prodName +
" - " +
object.size +
);
console.log(fields, "f");
const selectedProduct = fields.map((object) => object.product);
console.log(selectedProduct, "selectedProduct");
return (
<div>
<ul>
{fields.map((item, index) => {
return (
<li key={item.id} style={{ listStyleType: "none" }}>
<Controller
control={control}
name={`order.${index}.product`}
render={({ field: { onChange, value = "", ...rest } }) => (
<Autocomplete
{...rest}
onInputChange={(e, newValue) => {
onChange(newValue);
}}
inputValue={value}
options={options}
isOptionEqualToValue={(option, value) =>
option?.value === value?.value
}
renderInput={(params) => (
<TextField
{...params}
label="Product"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
<NestedArray
nestIndex={index}
{...{ control, register, products }}
/> //pass the corresponding colors here of the selected product (AutoComplete) in the above component
</li>
);
})}
</ul>
<section>
//button to add more product
</section>
</div>
);
};
export default FieldArray;
NestedArray Component:
To show the corresponding colors here according to what Product (AutoComplete) was selected in the above component
const NestedArray = ({ nestIndex, control, register, products }) => {
const { fields, remove, append } = useFieldArray({
control,
name: `order[${nestIndex}].variation`,
});
return (
<div>
{fields.map((item, k) => {
return (
<div key={item.id} style={{ marginLeft: 20 }}>
<label>{k + 1}</label>
//the quantity input here
<Controller
control={control}
name={`order[${nestIndex}].variation[${k}].color`}
render={({ field: { onChange, value = "", ...rest } }) => (
<Autocomplete
{...rest}
onInputChange={(e, newValue) => {
onChange(newValue);
}}
inputValue={value}
options={options}
isOptionEqualToValue={(option, value) =>
option?.value === value?.value
}
renderInput={(params) => (
<TextField
{...params}
label="Color"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
</div>
);
};
export default NestedArray;
I did not dig in to see how react-hook-form works, so you can probably come up with a nicer solution.
Here is my solution in codesandbox: https://codesandbox.io/s/react-hook-form-wizard-form-from-reddit-with-data-setting-up-a-setproduct-forked-wyo8i2?file=/src/index.js
To describe the changes I made:
I added a state variable in FieldArray to hold the currently selected product. (you probably incorporate this better using your form library). The value is then passed to NestedArray.
Edited the rendered <Autocomplete> props in FieldArray to include onChange instead of onInputChange (used onChange instead of onInputChange to get the product object instead of the generated label as an argument to the function).
I aslo had to map the colors of the product object to an array similar to the one you had created.
const options = Object.keys(product.colorMap).map((color) => ({
label: color,
value: color
}))
Ok Pennie, for building a dynamic options (if I understood what you are tring to develop here).
I think you should pass the options as props into the
nestedFieldsArray.js,
fieldsArray.js->
<NestedArraynestIndex={index}
{...{ control, register, options }}
/>
and in the nestedFieldsArray.js->
const NestedArray = ({ nestIndex, control, register, options }) => {
const { fields, remove, append } = useFieldArray({
control,
name: `order[${nestIndex}].variation`
});
and remove the hard coded options-
const options = [ //should be delete
{ label: "red", value: "red" },
{ label: "green", value: "green" },
{ label: "blue", value: "blue" }
];
https://codesandbox.io/s/react-hook-form-wizard-form-from-reddit-with-data-forked-ledb12?file=/src/nestedFieldArray.js

how to change style of a part of string when click :Reactjs

I will change style a part of string when click. example "TEXT" then click at "T" after that it will change style from black color to red color just T only
In my code, I split text and keep at "split" array when I click at text, it will call handleClick function and send index of character that I click is parameter. For example ("EXAMPLE") when I click E it will send 0 is parameter of handleClick function.
import React,{Component} from 'react'
export default class Test extends Component {
handleClick = (index) => {
console.log(index)
}
render() {
return(
<div>
{this.state.table.map((text) => {{this.state.split
&& this.state.split.map((item, index) => {
return(
<span key={index} onClick={() =>
this.handleClick(index)}>{item}
</span>
);
})}
</div>
)
}
}
You need a state which will maintain the clicked index. Then use that index while rendering your split spans to set different colored className.
You could then apply your style to that class.
export default class Test extends Component {
handleClick = (index) => {
this.setState({ clickedIndex: index });
}
render() {
return (
<div>
{this.state.table.map((text) => {
this.state.split && this.state.split.map((item, index) => {
return (
<span key={index} style={clickedIndex === index ? {color: 'red'} : {}} onClick={() =>
this.handleClick(index)}>{item}
</span>
);
})
})}
</div>
)
}
}

MaterialUI stepper use alternative color

How can I change the colour that a material ui Stepper uses? By default the material UI stepper's icons use the primary colour for the "active" as well as "completed" steps.
class HorizontalLinearStepper extends React.Component {
state = {
activeStep: 1,
skipped: new Set()
};
render() {
const { classes } = this.props;
const steps = getSteps();
const { activeStep } = this.state;
return (
<div className={classes.root}>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const props = {};
const labelProps = {};
return (
<Step key={label} {...props}>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
</div>
);
}
}
Now the stepper uses the theme's main colour as "icon colour". How can I change this to use the secondary colour instead? Adding a color props to any of the Stepper, Step or StepLabel doesn't seem to work, neither does style={{color: 'red', backgroundColor: 'red'}} give the expected results in any of those things.
How can I modify the colour?
A fiddle can be found here.
You can use the StepIconProps prop on StepLabel to customise the classes and change the colour e.g. https://codesandbox.io/s/k1wp19vz6o

How to enable/disable elements from an array in React JS?

I am trying to create a component where I have a bunch of boxes from an array, that can be turned 'on' and 'off' when each one is individually clicked.
Currently, only a single item from the array can be switched 'on' (shown by the item turning green), however, I would like to be able to turn each item on/off individually.
Interacting with one element should not affect any of the others.
How do I achieve this?
My click event:
handleOnClick = (val, i) => {
this.setState({active: i}, () => console.log(this.state.active, 'active'))
}
Rendering the boxes:
renderBoxes = () => {
const options = this.state.needsOptions.map((val, i) => {
return (
<button
key={i}
style={{...style.box, background: i === this.state.active ? 'green' : ''}}
onClick={() => this.handleOnClick(val, i)}
>
{val}
</button>
)
})
return options
}
Here's a Codepen
What I would do is to create a Box component with its own active state, and pass this to the map in renderBoxes. The benefit of doing it this way is that each Box component will have its own state independent of the parent. That way you can have more than one component as active.
so...
class Box extends React.Component {
constructor(props){
super(props)
this.state={
active: false
}
}
clickHandler = () => {
this.setState({active: !this.state.active})
}
render(){
const { key, children }= this.props
return (
<button
key={key}
style={{...style.box, background: this.state.active ? 'green' : ''}}
onClick={() => this.clickHandler()}
>
{children}
</button>
)
}
}
then have renderBoxes be...
renderBoxes = () => {
const options = this.state.needsOptions.map((val, i) => {
return (
<Box
key={i}
>
{val}
</Box>
)
})
return options
}
here is the codepen I forked off yours.

reactjs - Render single icon on hover for list item rendered from array

I have this sort of cards that are rendered from an array of objects.
Parent Component:
[{foo: 'bar', baz: [{ name: string, path: string: '/'}]
state = {isHovering: false}
handleMouseHover = () => {
const { isHovering } = this.state;
this.setState({ isHovering: !isHovering });
}
;
I'm passing down handleMouseHover() and isHovering down as props to a child component.
Resulting in something like this:
Child Component
<LinkContainer
onMouseEnter={handleMouseHover}
onMouseLeave={handleMouseHover}
className="linkContainer"
>
{isHovering && (
<FontAwesomeIcon
icon="copy"
className="copyIcon"
onClick={copyToClipboard}
/>
)}
The result is 4 cards that contain 3 links. Each time I hover over a link I want the copy to clipboard icon to show. However, at the moment when I hover over any item it sets isHovering to true thus making all the icons visible. Ideally I just want the the icon for the link I hover over to become visible. Can someone help me to figure out a better solution or a refinement of my already written code.
Much appreciated!
You could keep an object in your state instead of a boolean, that has a key indicating if the object with that particular key as index is hovered or not.
Example
class App extends React.Component {
state = {
arr: [{ text: "foo" }, { text: "bar" }],
isHovered: {}
};
handleMouseEnter = index => {
this.setState(prevState => {
return { isHovered: { ...prevState.isHovered, [index]: true } };
});
};
handleMouseLeave = index => {
this.setState(prevState => {
return { isHovered: { ...prevState.isHovered, [index]: false } };
});
};
render() {
const { arr, isHovered } = this.state;
return (
<div>
{arr.map((el, index) => (
<Child
onMouseEnter={() => this.handleMouseEnter(index)}
onMouseLeave={() => this.handleMouseLeave(index)}
text={el.text}
isHovering={isHovered[index]}
key={index}
/>
))}
</div>
);
}
}
function Child({ onMouseEnter, onMouseLeave, text, isHovering }) {
return (
<div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
{text} {isHovering && " (hovering!)"}
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Create a property isHovered on item of an array dynamically and onMouseHover pass the item which you get in .map, now toggle the isHovered property. Should work now.

Categories