React MUI Multiple Select with Checkboxes not Check marking Initial Selected Options - javascript

I am still new in React and MUI, so please spare my life. I am learning to build Multiple Select Options with Checkboxes and I have populated the Dropdown Options from an Array.
I also have set up the initial/default state options. The initial/default state options (Nintendo & XBoX) are showing up in the main Input Select Field but unfortunately not being checked marked in the Dropdown Checkboxes, such as in the screenshot below.
Screenshot 1
What I'm trying to achieve is to be like this (all the selected options (Nintendo & XBoX) from the default state is showing on the checkboxes at initial page load):
Screenshot 2
This is my Live Demo coding in https://stackblitz.com/edit/react-rahqrq?file=demo.js
I'm confused, I can tick all the other option checkboxes in the dropdown except the default Initial Options from the State. It's unclickable and unchangeable, I have no idea why. Any tips and clues are greatly appreciated, Thanks in advance, please pardon my poor Grammar and Best Regards.
import * as React from 'react';
import OutlinedInput from '#mui/material/OutlinedInput';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import ListItemText from '#mui/material/ListItemText';
import Select from '#mui/material/Select';
import Checkbox from '#mui/material/Checkbox';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
const variants = [
{
id: 3,
name: 'Voucher',
},
{
id: 1,
name: 'Top Up',
},
{
id: 2,
name: 'Game Key',
},
{
id: 12,
name: 'Other',
},
{
id: 11,
name: 'Nintendo',
},
{
id: 10,
name: 'Xbox',
},
];
export default function MultipleSelectCheckmarks() {
const [variantName, setVariantName] = React.useState([{
id: 11,
name: 'Nintendo',
},{
id: 10,
name: 'Xbox'
},]);
const handleChange = (event) => {
const {
target: { value },
} = event;
const preventDuplicate = value.filter((v, i, a) => a.findIndex((t) => t.id === v.id) === i);
setVariantName(
// On autofill we get a the stringified value.
typeof preventDuplicate === 'string' ? preventDuplicate.split(',') : preventDuplicate
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-multiple-checkbox-label"
id="demo-multiple-checkbox"
multiple
value={variantName}
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map((x) => x.name).join(', ')}
MenuProps={MenuProps}
>
{variants.map((variant) => (
<MenuItem key={variant.id} value={variant}>
<Checkbox checked={variantName.indexOf(variant) > -1} />
<ListItemText primary={variant.name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}

You need to findIndex of each item in the initial list to make them checked.
Try like below
{
variants.map(variant => (
<MenuItem key={variant.id} value={variant}>
<Checkbox
checked={
variantName.findIndex(item => item.id === variant.id) >= 0
}
/>
<ListItemText primary={variant.name} />
</MenuItem>
));
}
Code sandbox => https://stackblitz.com/edit/react-rahqrq-sd2m2b?file=demo.js

Related

Using a Prop to populate an array - React useState

I have 3 dropdowns, each which controls state. When this dropdown is selected it will set the target and send it to redux. For example.
const [interviewStep1, setinterviewStep1] = useState('Phone Screening')
const [interviewStep2, setinterviewStep2] = useState('Formal Interview')
const [interviewStep3, setinterviewStep3] = useState('Reference Check')
This is sent to redux in this manner.
<Dropdown_Content>
{interviewStageSelection.map((option) => (
<Dropdown_Item
key={option}
onClick={(e) => {
setinterviewStep1(option)
setisActive(!isActive)
console.log(interviewStep1)
setisActive(false)
updateInterview1(dispatch, option)
}}
>
<Typography variant="subtitle5" color="black" sx={{ "&:hover": { color: "white" } }}>{option}</Typography>
</Dropdown_Item>
))}
</Dropdown_Content>
I then pass this state as props into my next component.
export default function JobPostInterviewVerticalStepper(interviewStep1, interviewStep2, interviewStep3)
this does come through, but then I want to display in my array. How do I use these props?
const steps = [
{
label: 'Phone Screening',
//I WANT A interviewStep1 here!
},
{
label: 'Formal Interview',
},
{
label: 'Reference Check',
},
{
label: 'Offer',
},
];

Getting content of currently active Text component wrapped inside popover of antd

I am using antd components for my react app. I have a Text component wrapped inside of Popover component. Now in my case this Popover is applied to one particular column of table, i.e. every row-element in that column has a Popover component rendered for it upon mouse hovering.
title: "Name",
dataIndex: "name",
key: "name-key",
sortType: "string",
sortDirections: ["descend", "ascend"],
sorter: (a, b) => a.name.length - b.name.length,
render: (text, record) => (
<Popover>
<Text onMouseOver={handleOnMouseOverCommitId}> {name} </Text>
</Popover>
)
I want to get hold of the row-element's value, the one contained by the above Text component whenever I hover over it. In this case the value denoted by {name} above.
I tried getting it with e.target.value via onMouseOver event, but it returned undefined.
I think I get the reason behind it, because the event.target returns an html node of type <span>.
With a normal div element e.target.value has worked in the past for me. But doing the same thing with a predefined component like antd's Text seems a bit trickier.
Just to elaborate, the Popover has two buttons and based on which button user clicks, I need to render some other components, something like an overlay component.
But in order to do that I would also need to get hold of the text value which originally triggered the Popover.
Below is the code(most of the things removed for preciseness).
record.name is what I ultimately need to capture.
<Popover
content={
<>
<Space>
<Button onClick={showSomeOverlayPaneForName}>
{"View Details for record.name"}
</Button>
<Button href={"https://abc.xyz.com/" + record.role}>
{"View Role Details"}
</Button>
</Space>
</>
}
trigger={"hover"}
>
<Text style={{"color": blue.primary}} copyable={true} onMouseOver={handleOnMouseOverName}>{record.name}</Text>
</Popover>
The handleOnMouseOverName function(which doesn't work anyway) :
const handleOnMouseOverName = (e) => {
//console.log("e.target.value :--- ", e.target.value);
setCurrentActiveName(e.target.value)
}
And once my currentActiveName variable is set(via useState), I use that value inside my function showSomeOverlayPaneForName
const showSomeOverlayPaneForName = (e) => {
axios
.get(
`some-url`,
{
params: {name: currentActiveName}
}
)
.then((response) => {
setData(response.data);
}).catch(reason => {
//xyz
});
}
You need to pass on the record of the enclosing render function to the handleOnMouseOverName function.
Check the following example
import React from 'react';
import 'antd/dist/antd.css';
import './index.css';
import { Space, Table, Button, Popover } from 'antd';
const App = () => {
const data = [
{
key: '1',
name: 'John Brown',
address: 'New York No. 1 Lake Park',
role: 'admin',
},
{
key: '2',
name: 'Jim Green',
address: 'London No. 1 Lake Park',
role: 'user',
},
{
key: '3',
name: 'Joe Black',
address: 'Sidney No. 1 Lake Park',
role: 'manager',
},
];
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (name, record) => {
const content = (
<>
<Space>
<Button
onClick={() => {
viewDetail(record);
}}
>
{'View Details for ' + record.name}
</Button>
<Button href={'https://abc.xyz.com/' + record.role}>
{'View Role Details'}
</Button>
</Space>
</>
);
return (
<>
<Popover content={content} title="Details">
<div
onMouseOver={() => {
handleOnMouseOverName(record);
}}
>
{name}
</div>
</Popover>
</>
);
},
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
const handleOnMouseOverName = (record) => {
console.log(record);
};
const viewDetail = (record) => {
console.log(record);
};
return <Table columns={columns} dataSource={data} />;
};
export default App;
Output:
I hope this helps.
From antd docs: https://ant.design/components/popover/#header
Apparently you're supposed to render the <Popover/> with a content={content}-prop
For example
const content = <div>Content to render under title</div>
const App = () => {
const value = "Text to hover";
return (
<Popover content={content} title="Title">
<Text>{value}</Text>
</Popover>
)
}

ReactJS get values of dynamically created Radio Buttons and pass them into state (usestate)

I have a problem with my ReactJS App. I want to get values of checked radio buttons and after selecting I want to display the values of selected radio buttons.
The form is generated from a json file
[
{
variantId: 1,
variantName: 'Size',
variantOptions: [
{
variantOptionId: 1,
variantOptionName: 'S',
variantOptionPriceChange: 4.5
},
{
variantOptionId: 2,
variantOptionName: 'M',
variantOptionPriceChange: 4.5
},
]
},
{
variantId: 2,
variantName: 'Color',
variantOptions: [
{
variantOptionId: 3,
variantOptionName: 'Red',
variantOptionPriceChange: 4.5
},
{
variantOptionId: 4,
variantOptionName: 'Blue',
variantOptionPriceChange: 4.5
},
]
}
]
Demo of the problem is visible here: https://codesandbox.io/s/epic-http-bgmx3?file=/src/App.js
I want to display all selected items, not only the last one.
The problem is in this part of code, but I dont know how to rewrite it to achieve the desired behavior.
const addOption = (o) => {
setOptions({
optionId: o.variantOptionId,
optionName: o.variantOptionName,
optionPriceChange: o.variantOptionPriceChange
});
};
Thank you for your help, hope I described it clearly.
Simplest solution would be creating an object in useState with props keys for each variant and then store the selected option of that variant in related object prop
It should work like this:
export default function App() {
const [options, setOptions] = useState({});
const addOption = (name, o) => {
setOptions({ ...options, [name]: o });
};
return (
<div className="App">
{variants.map((variant, index) => {
return (
<Variant
options={options}
variant={variant}
addOption={addOption}
key={index}
/>
);
})}
<h3>
Selected variants are:
<ul>
{Object.keys(options).map((name, i) => {
return (
<li key={i}>
{name}: {options[name].variantOptionName}
</li>
);
})}
</ul>
</h3>
</div>
);
}

How to clear Multiselect Dropdown in Semantic UI React using a button?

I have a Semantic UI React Multiselect Dropdown inside a React functional component and want to have buttons inside the menu, as such (still need to center them...)
How can I clear the selected values using the 'Clear' button?
I am able to clear the selection using the 'x' icon, but that's built into the component.
<Dropdown
search
multiple
selection
clearable
closeOnSelectionChange={false}
options={filterInitialSuggestions()}
className='selectDropdown'
header={dropdownButtons()}
/>
const dropdownButtons = () => {
return (
<div>
<Button positive size='mini'>
Save
</Button>
<Button grey size='mini' onClick={() => console.log('I want to reset the multi select dropdown')}>
Clear
</Button>
<Divider />
</div>
);
};
'Save' and 'Clear' buttons with React useState(). In Reactjs you don't need to use DOM queryselector.
https://codesandbox.io/s/white-leftpad-q6re3?file=/src/Fun.jsx
import React, { Component } from 'react';
import { Dropdown } from 'semantic-ui-react';
const options = [
{ key: 1, text: 'Choice 1', value: 1 },
{ key: 2, text: 'Choice 2', value: 2 },
{ key: 3, text: 'Choice 3', value: 3 },
{ key: 4, text: 'Choice 4', value: 4 },
{ key: 5, text: 'Choice 5', value: 5 },
{ key: 6, text: 'Choice 6', value: 6 },
]
class Example extends Component {
state = {
dropval: []
}
onDropChange = (e, { value }) => {
this.setState(
(prevState) => { return { ...prevState, dropval: value } },
// () => console.log(this.state)
)
}
render() {
return (
<div>
<Dropdown
search
multiple
selection
clearable
closeOnSelectionChange={false}
options={options}
className='selectDropdown'
onChange={this.onDropChange}
value={this.state.dropval}
style={{ width: 300 }}
/>
</div>
);
}
}
export default Example;
I figured out how to solve this issue. I'm not sure if this is the best way, but it seems to work decently.
const dropdownButtons = () => {
return (
<>
<div className='dropdown-saveButton'>
<Button
positive
size='mini'
onClick={() => {
saveValues();
}}
>
Save
</Button>
<Button size='mini' onClick={clearDropdown}>
Clear
</Button>
</div>
<Divider inverted />
</>
);
};
const clearDropdown = e => {
var element = dropdownRef.current.querySelector('[aria-selected="true"]');
if (element) {
dropdownRef.current.querySelector('.clear')?.click();
}
};
<Dropdown
multiple
selection
fluid
clearable
header={dropdownButtons} />

How to sync two dropdown lists with different display in React?

I have a list of countries, with key, value, text.
I would like to have two Dropdown (https://react.semantic-ui.com/modules/dropdown/) list, one shows the key, the other the text.
The goal is to allow to choose by key of by text (we can type in the dropdown); if I update one, the other is synchronized immediately.
How can I achieve this ?
<Dropdown
id='form-input-country'
label='Country'
placeholder='Select Country'
fluid
search
selection
options={countryISOOptions} // will show text
/>
<Dropdown
id='form-input-country'
label='Country'
placeholder='Select Country'
fluid
search
selection
options={countryISOOptions} // want to show key + want to sync in both direction
/>
I import countryISOOptions which looks like:
export const countryISOOptions = [
{key: 'AF', value: '4', text: 'Afghanistan'},
{key: 'AL', value: '8', text: 'Albania'},
{key: 'DZ', value: '12', text: 'Algeria'},
...
Maintain 2 option arrays. One for text and other for keys(derived from the first options array). Then maintain just one state and an onChange for both dropdowns and you will be fine.
See working copy of your code.
See code snippet:
import React, { useState } from "react";
import { Dropdown } from "semantic-ui-react";
import "./styles.css";
const countryISOOptions = [
{ key: "AF", value: "4", text: "Afghanistan" },
{ key: "AL", value: "8", text: "Albania" },
{ key: "DZ", value: "12", text: "Algeria" }
];
const countryKeys = countryISOOptions.map(({ key, value }) => ({
value,
text: key
}));
export default function App() {
const [text, setText] = useState("");
const onChangeTextDropdown = (e, d) => {
console.log("onChangeTextDropdown", e.target.value);
console.log("d", d);
setText(d.value);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Dropdown
id="form-input-countryz"
label="Country"
placeholder="Select Country - text"
value={text}
onChange={onChangeTextDropdown}
fluid
search
selection
options={countryISOOptions} // will show text
/>
<Dropdown
id="form-input-country"
label="Country"
placeholder="Select Country - key"
value={text}
onChange={onChangeTextDropdown}
fluid
search
selection
options={countryKeys} // want to show key + want to sync in both direction
/>
</div>
);
}
If you are using controlled version, then each Dropdown is a typical Inputthat supports two props called value and onChange. I'll use hook in the following example,
const [value1, setValue1] = setState('')
const [value2, setValue2] = setState('')
const onValue1Change = e => {
const value = e.target.value
setValue1(value)
if (value === 'key') setValue2('country')
}
return (
<div>
<Dropdown
value={value1}
onChange={onValue1Change}
...
/>
<Dropdown
value={value2}
...
/>
</div>
)

Categories

Resources