How to pass a selected value from nested object to chip - javascript

I have an object of the following type:
[
{
"insInvolvementId": "01",
"clientId": "AB",
"client": {
"firstName": "JAMCN",
"middleName": null,
"lastName": "PERGY"
},
"involvementRole": [
{
"involvementRoleId": "01A",
"insInvolvedId": "01",
"involvementRoleDescription": "Driver"
},
{
"involvementRoleId": "01B",
"insInvolvedId": "01",
"involvementRoleDescription": "Owner"
},
{
"involvementRoleId": "01C",
"insInvolvedId": "01",
"involvementRoleDescription": "Insured"
}
]
}
]
I have the following code snippet to render the object:
import React from 'react';
import { useState } from 'react';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import CLAIMS from './Participant_data';
const useStyles = makeStyles((theme)=>({
typography : {
fontSize:'3.5 rem',
fontWeight:'550',
color:'#0E1941',
marginBottom:'30px',
[theme.breakpoints.down('md')]: {
fontSize: '0.5rem',
},
[theme.breakpoints.up('md')]: {
fontSize: '1.0rem',
},
[theme.breakpoints.up('lg')]: {
fontSize: '1.2rem',
}
},
font_class : {
font: 'normal normal 900 20px/24px Allstate Sans',
// letter-spacing: '0px',
color: '#0E1941',
opacity: '1',
fontSize:'1.2rem'
}
}));
const Checkbox = (props) => {
// creates all the checkbox based on the list array.
const classes = useStyles();
const checkbox = props.list.map((m) => (
// <span key={m.key}>
m.involvementRole.map((role) => (
<div>
<table>
<tbody style={{ width: "100%" }}>
{/* {CLAIMS.map((m) => */}
<tr>
<td style={{ width: "60px" }}>
{" "}
<input
type="checkbox"
id = {role.involvementRoleDescription}
onClick={() => props.handleCheckboxChange(m)
}
/>
</td>
<td style={{ width: "250px" }} value={role.involvementRoleDescription}>
{role.involvementRoleDescription}
</td>
<td style={{ width: "250px" }}>
{m.client.firstName + " " + m.client.lastName}
</td>
</tr>
</tbody>
</table>
</div>
))
));
return (
<div style={{width:'100%'}}>
<div style={{backgroundColor:'#E9E9E9' , width:'100%' , marginTop:'10px' ,
marginBottom:'10px'}}> <typography variant="body1" className={classes.typography} style=
{{marginTop:'10px' , marginBottom:'30px' , font: "normal normal medium 20px/24px Allstate
Sans W"}}> {props.title} </typography></div>
<div className = {classes.font_class}>{checkbox}</div>
</div>
);
};
export default Checkbox;
on click of the checkbox an array or selected values will be created and the selected values will be passed to the chip
const [chipData, setChipData] = React.useState([]);
const handlePerformerDelete = (chipToDelete) => () => {
setChipData((chips) => chips.filter((chip) => chip.key !== chipToDelete.key));
console.log(chipData);
};
const handleCheckboxChange = (insInvolvementId, setParticipants) => {
setParticipants((currentParticipants) =>
currentParticipants.includes(insInvolvementId )
? currentParticipants.filter((f) => f !== insInvolvementId)
: [...currentParticipants, insInvolvementId]
);
};
console.log(claimParticipants);
const handleSubmit=()=>{
setChipData(claimParticipants);}
The object I have rendered on screen is something like this
I want the firstname,lastname and role desc to be passes to the chip but I am not able to get the specific role for the checkbox we clicked.

Related

Filtering Mapped React Table Data with JSON Data

I successfully mapped JSON inventory data into a React Table. My goal is to use buttons to filter this table and render the table results based on the filter conditions. How do I do this?
Button Filters Component:
import React from 'react'
import Button from './Button'
const Form = ({reqType, setReqType, color, setColor, handleColorChange}) => {
return (
<form onSubmit={(e)=> e.preventDefault()}>
<Button
buttonText="Red"
>
</Button>
<Button
buttonText="White"
/>
</form>
)
}
export default Form
Table Component:
import React from 'react'
import Row from './Row'
const Table = ({ wines }) => {
return (
<table >
<tbody >
{wines.map((wine, key) =>(
<Row wine={wine} key={key}/>
))}
</tbody>
</table>
)
}
export default Table
Row Component:
import React from 'react'
import Cell from './Cell'
const Row = ({ wine }) => {
return (
<tr>
{Object.entries(wine).map(([key, value]) => {
return (
<Cell key={key} cellData={JSON.stringify(value)}/>
)
} ) }
</tr>
)
}
export default Row
Cell Component:
import React from 'react'
const Cell = ({cellData,wine}) => {
return (
<td >
{cellData}
</td>
)
}
export default Cell
App Component:
<Form
reqType={reqType}
setReqType={setReqType}
color={color}
setColor={setColor}
handleColorChange={handleColorChange}/>
<Table wines={wines}/>
I mentioned that you need a state for the filtered data. You don't. But you do need two - one to hold the full data set, and another to maintain the colour. You can then use a function to filter out objects based on their colour.
So I've played around with your code, removed a bunch of things to make this example readable, added a Button component, added a reset button, and added a basic dataset.
Hopefully you can see how it all fits together.
const { useState } = React;
// We passing in the data - you may not being doing this
// your list of wines may come from an API call, but I'm
// keeping this simple
function Example({ data }) {
// Set the wines state to the passed in data,
// and initialise the color state (to undefined)
const [ wines, setWines ] = useState(data);
const [ color, setColor ] = useState();
// Simple function to accept a color
// as an argument and update the state with it
function handleColor(color) {
setColor(color);
}
// If color is undefined return all the wines!
// Otherwise `filter` the wines based on their colour
function filterData(wines, color) {
if (!color) return wines;
return wines.filter(wine => wine.color === color);
}
// Pass down the handleColor function in the Form props,
// and call `filteredData` to have an updated list of wines
// that you pass as a props to the table
return (
<div>
<Form handleColor={handleColor} />
<Table wines={filterData(wines, color)} />
</div>
);
}
// Pass in the `handleColor` function
function Form({ handleColor }) {
// Buttons get their own `handleClick` function
// which extracts the colour from the button's dataset
// and then calls `handleColor` with that color
function handleClick(e) {
const { color } = e.target.dataset;
handleColor(color);
}
// Button components get text, color,
// and the `handleClick` function as props
return (
<div>
<Button
text="Red"
color="red"
handleClick={handleClick}
/>
<Button
text="White"
color="white"
handleClick={handleClick}
/>
<Button
text="Reset"
color=""
handleClick={handleClick}
/>
</div>
);
}
function Button({ text, color, handleClick }) {
return (
<button
data-color={color}
onClick={handleClick}
>{text}</button>
);
}
function Table({ wines }) {
return (
<table>
<tbody>
{wines.map(wine => (
<Row wine={wine} key={wine.id}/>
))}
</tbody>
</table>
);
}
function Row({ wine }) {
return (
<tr>
{Object.entries(wine).map(([key, value]) => {
return <Cell key={value} value={value} />;
})}
</tr>
)
}
function Cell({ value }) {
return <td>{value}</td>;
}
const data = [
{ id: 1, name: 'Plonk', color: 'red' },
{ id: 2, name: 'Rose', color: 'red' },
{ id: 3, name: 'Vanilla', color: 'white' },
{ id: 4, name: 'White', color: 'white' },
{ id: 5, name: 'Plonk', color: 'red' },
{ id: 6, name: 'Steve', color: 'white' }
];
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
button { margin-right: 0.2em; }
table { border-collapse: collapse; border: 1px solid #676767; margin-top: 1em; }
table td { padding: 0.4em; border: 1px solid #efefef; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

React ComboBox (npm component) returning [object Object]

I've currently set a task for myself of creating a language selection box, using https://react-combobox.netlify.app/demo as a base and working off of that as I learn React. The code so far I have is returning [object Object] as a result when I select an entry in the combobox.
Code:
import React, { useState } from "react";
import ComboBox from "react-responsive-combo-box";
export default function ComboBoxLang(props) {
const [selectedOption, setSelectedOption] = useState("");
const [highlightedOption, setHighlightedOption] = useState("");
const options = props.dataSet.map ( (inputValue) => {
// const columnWidth = props.columnsWidth.language_name + "px";
const columnWidth = props.columnsWidth;
let keys = Object.keys(columnWidth);
console.log ('keys are' + keys)
const renderedColumns = keys.map( columnName =>{
return(
<div style={{display:"inline-block", width:columnWidth[columnName]}}>{inputValue[columnName]} </div>
);
})
return (
<div key={inputValue.id}>
{renderedColumns}
</div>
)
})
return (
<div>
<h1>React Combo Box</h1>
<p>
The selected option -{" "}
<span style={{ fontWeight: "bold" }}>
{" "}
{selectedOption.length > 0 ? selectedOption : "None"}
</span>
</p>
<p>
The highlighted option -{" "}
<span style={{ fontWeight: "bold" }}>
{" "}
{highlightedOption.length > 0 ? highlightedOption : "None"}
</span>
</p>
<ComboBox
options={options}
placeholder="choose country"
defaultIndex={4}
optionsListMaxHeight={300}
style={{
width: "500px",
margin: "0 auto",
marginTop: "50px"
}}
focusColor="#20C374"
renderOptions={(option) => (
<div className="comboBoxOption">{option}</div>
)}
onSelect={(option) => setSelectedOption(option)}
onChange={(event) => console.log(event.target.value)}
enableAutocomplete
onOptionsChange={(option) => setHighlightedOption(option)}
/>
</div>
);
}
index.js code:
import React from 'react';
import ReactDOM from 'react-dom';
import ComboBoxLang from './combobox_test.js';
const languages = [
{
"id": 1,
"language_name": "Afrikaans",
"language_native_name": "Afrikaanse taal",
"language_full_name": "Afrikaanse taal (Afrikaans)",
"language_code": "af",
"translation_engine_id": 2,
"translation_engine_name": "Google"
},
{
"id": 2,
"language_name": "Albanian",
"language_native_name": "Gjuha shqipe",
"language_full_name": "Gjuha shqipe (Albanian)",
"language_code": "sq",
"translation_engine_id": 2,
"translation_engine_name": "Google"
}
];
class App extends React.Component {
render () {
return (
<div>
<ComboBoxLang dataSet={languages} columnsWidth={{language_name:200,language_native_name:300}}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('drop-down-menu')
);
The goal is to effectively have the language, and the native language (or language code at a later date) to display within the combo-box once selected. I've left the 2 "Displayed" and "Selected" boxes so that, once it works, I can understand a bit how it works, and allow other things to interact or reference those items. For now, I am stuck trying to figure out why it spit's out [object Object] and where it is actually in need of correcting.

How to update parent state and child components props at once (at the same time)

I'm new to React and basically I'm trying to update a parent App.js components' state and its child components (Team.js and Player.js) props at once. Currently only the parent components' state is being updated. I will try to explain it better with a step by step.
Here I have a parent component App.js
export default function App() {
const teams = [
{
Name: "Chicago Bulls",
Players: ["Michael Jordan", "Dennis Rodman", "Scottie Pippen"],
Championships: 6
},
{
Name: "Golden State Warriors",
Players: ["Stephen Curry", "Klay Thompson", "Draymond Green"],
Championships: 5
},
{
Name: "Los Angeles Lakers",
Players: ["Kobe Bryant", "LeBron James", "Magic Johnson"],
Championships: 17
}
];
const [selectedTeam, setSelectedTeam] = useState({});
const players = [
{ Name: "LeBron James", MVPs: 4 },
{ Name: "Michael Jordan", MVPs: 5 },
{ Name: "Stephen Curry", MVPs: "2" }
];
const [selectedPlayer, setSelectedPlayer] = useState({});
const [modalContent, setModalContent] = useState(false);
const clickedComponent = useRef(null);
const [show, setShowModal] = useState(false);
const showModal = () => {
setShowModal(true);
};
const hideModal = () => {
setShowModal(false);
};
const handleModalContent = (clicked) => {
switch (clicked) {
case "Team":
clickedComponent.current = (
<Team
teams={teams}
selectedTeam={selectedTeam}
setSelectedTeam={setSelectedTeam}
/>
);
break;
case "Player":
clickedComponent.current = (
<Player
players={players}
selectedPlayer={selectedPlayer}
setSelectedPlayer={setSelectedPlayer}
/>
);
break;
default:
clickedComponent.current = null;
break;
}
};
return (
<div className="App" style={{ justifyContent: "space-evenly" }}>
<div
style={{
justifyContent: "center",
width: "100%",
display: "flex",
flexWrap: "wrap",
margin: "40px 0px 0px 0px"
}}
>
<div
className="table-cell"
onClick={() => {
handleModalContent("Team");
setModalContent(true);
showModal();
}}
>
<div className="table-cell-text">Click to access Team component</div>
</div>
<div
className="table-cell"
onClick={() => {
handleModalContent("Player");
setModalContent(true);
showModal();
}}
>
<div className="table-cell-text">
Click to access Player component
</div>
</div>
</div>
<h3 style={{ marginTop: "30px" }}>
The last selected team was: {selectedTeam.Name}
<br />
The last selected player was: {selectedPlayer.Name}
</h3>
<Modal show={show} modalClosed={hideModal}>
{(modalContent && clickedComponent.current) || null}
</Modal>
</div>
);
}
This component has two arrays of objects (teams and players) that is sent to Team and Player components, respectively, as props. Team also receives selectedTeam and setSelectedTeam as props. Player receives selectedPlayer and setSelectedPlayer. Both components have a Modal component and a select input. In the Team component, the user will select a team and them it will be displayed the selected teams' players, while in the Player component a player will be select and them it will be displayed the amount of MVP of the selected player.
Team.js
const Team = (props) => {
return (
<div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
<h3>Select a team</h3>
<div className="input-group col">
<select
onChange={(e) => {
if (e === "") props.setSelectedTeam({});
else {
let foundTeam = props.teams.find(
(team) => team.Name === e.target.value
);
props.setSelectedTeam(foundTeam);
}
}}
>
<option value="">Select a team...</option>
{props.teams.map((team) => (
<option key={team.Name} value={team.Name}>
{team.Name}
</option>
))}
</select>
</div>
{Object.keys(props.selectedTeam).length > 0 ? (
<div>
<h3>{props.selectedTeam.Name} players: </h3>
<br />
{props.selectedTeam.Players.map((player, index) => (
<div key={index}>{player}</div>
))}
</div>
) : null}
</div>
);
};
export default Team;
Player.js
const Player = (props) => {
return (
<div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
<h3>Select a player</h3>
<div className="input-group col">
<select
onChange={(e) => {
if (e === "") props.setSelectedPlayer({});
else {
let foundPlayer = props.players.find(
(player) => player.Name === e.target.value
);
props.setSelectedPlayer(foundPlayer);
}
}}
>
<option value="">Select a player...</option>
{props.players.map((player) => (
<option key={player.Name} value={player.Name}>
{player.Name}
</option>
))}
</select>
</div>
{Object.keys(props.selectedPlayer).length > 0 ? (
<div>
<h3>
{props.selectedPlayer.Name} MVPs: {props.selectedPlayer.MVPs}
</h3>
</div>
) : null}
</div>
);
};
export default Player;
So my problem is, if I select an option in the child components, they don't receive the updated selected option (I mean selectedTeam for Team component and selectedPlayer for Player component) immediatelly but in the father component App I have them updated. So, if I want them to get updated, I need to select an option, close the modal and reopen them again.
For example, here I have App.js visual:
If I open Team.js and select a team, I have selectedTeam updated in App.js but not in Team.js:
So, if I close the modal and reopen Team component again, then I have props.selectedTeam updated. So I have the following:
I have the same problem with Player component, but in this case regarding props.selectedPlayer
How can I make it work properly, I mean, how can I have props.selectedTeam and props.selectedPlayer updated at once in App such as in Team and Player, respectively? Thank you!
CodeSandbox
https://codesandbox.io/s/young-sun-gs117?file=/src/Team.js:51-1127
Here is what you need to do, I refactored your code and add some comments on that so you know what I did that. one thing to remember is that almost you don't want to store a component in a state.
export default function App() {
const teams = [
{
Name: "Chicago Bulls",
Players: ["Michael Jordan", "Dennis Rodman", "Scottie Pippen"],
Championships: 6,
},
{
Name: "Golden State Warriors",
Players: ["Stephen Curry", "Klay Thompson", "Draymond Green"],
Championships: 5,
},
{
Name: "Los Angeles Lakers",
Players: ["Kobe Bryant", "LeBron James", "Magic Johnson"],
Championships: 17,
},
];
const players = [
{ Name: "LeBron James", MVPs: 4 },
{ Name: "Michael Jordan", MVPs: 5 },
{ Name: "Stephen Curry", MVPs: "2" },
];
// This makes typo mistake less and will give you auto complete option
const componentType = {
team: "Team",
player: "Player",
};
const [selectedTeam, setSelectedTeam] = useState({});
const [selectedPlayer, setSelectedPlayer] = useState({});
// the modalContent state and show state are doing the same thing so one of them is unneccessary
const [show, setShowModal] = useState(false);
const [clickedComponent, setClickedComponent] = useState("");
const showModal = () => {
setShowModal(true);
};
const hideModal = () => {
setShowModal(false);
};
const handleModalContent = (clicked) => {
setClickedComponent(clicked);
};
return (
<div className="App" style={{ justifyContent: "space-evenly" }}>
<div
style={{
justifyContent: "center",
width: "100%",
display: "flex",
flexWrap: "wrap",
margin: "40px 0px 0px 0px",
}}
>
<div
className="table-cell"
onClick={() => {
handleModalContent(componentType.team);
showModal();
}}
>
<div className="table-cell-text">Click to access Team component</div>
</div>
<div
className="table-cell"
onClick={() => {
handleModalContent(componentType.player);
showModal();
}}
>
<div className="table-cell-text">
Click to access Player component
</div>
</div>
</div>
<h3 style={{ marginTop: "30px" }}>
The last selected team was: {selectedTeam.Name}
<br />
The last selected player was: {selectedPlayer.Name}
</h3>
<Modal show={show} modalClosed={hideModal}>
{clickedComponent === componentType.player ? (
<Player
players={players}
selectedPlayer={selectedPlayer}
setSelectedPlayer={setSelectedPlayer}
/>
) : clickedComponent === componentType.team ? (
<Team
teams={teams}
selectedTeam={selectedTeam}
setSelectedTeam={setSelectedTeam}
/>
) : null}
</Modal>
</div>
);
}
The way I know how is to just use Hooks useState and useEffect and just update that state on select change.
Hope the below example for Player helps (worked in your code sandbox unless I am not answering your question):
import React, { useState, useEffect } from "react";
import "./styles.css";
const Player = (props) => {
const [test, setTest] = useState("");
useEffect(() => {
console.log("props:", props);
setTest(props.selectedPlayer);
}, [props]);
return (
<div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
<h3>Select a player</h3>
<div className="input-group col">
<select
value={props.selectedPlayer}
onChange={(e) => {
if (e === "") props.setSelectedPlayer({});
else {
let foundPlayer = props.players.find(
(player) => player.Name === e.target.value
);
props.setSelectedPlayer(foundPlayer);
setTest(foundPlayer);
}
}}
>
<option value="">Select a player...</option>
{props.players.map((player) => (
<option key={player.Name} value={player.Name}>
{player.Name}
</option>
))}
</select>
</div>
<h3>{test.Name} MVPs: {test.MVPs}</h3>
{/* {Object.keys(props.selectedPlayer).length > 0 ? (
<div>
<h3>
{props.selectedPlayer.Name} MVPs: {props.selectedPlayer.MVPs}
</h3>
</div>
) : null} */}
</div>
);
};
export default Player;

Controlling the way selected value inside the select box looks : Is there a way to render the selected item separately?

I am working with the antd' select box. I tried to customise the content inside Option which holds the regular text with some JSX. It looks as follows:
Here is also the small demo I prepared on sandbox:
Since I have customised the content inside the Option, the moment I make a choice with the Select Box, it gets shown as:
As you could see, the select box tries to show everything. Is there a way I could control how the select box looks just after the choice is made with the select box? I just want the name to be displayed after the selection is made. For example, product-1 must be displayed when the first option is selected.
For easier reference, I am also posting the code here:
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Select } from "antd";
const { Option } = Select;
const data = [
{
productName: "product-1",
productExternalId: "DT01A",
productionExternalId: "PL-DT01A",
quantity: "700 kg"
},
{
productName: "product-2",
productExternalId: "DT01A",
productionExternalId: "PL-DT01A",
quantity: "700 kg"
},
{
productName: "product-3",
productExternalId: "DT01A",
productionExternalId: "PL-DT01A",
quantity: "700 kg"
}
];
const ProductSelectBox = React.memo(props => {
const { details } = props;
function onSelect(value, option) {
console.log(value, "..", option);
}
function customizedOption({
productName,
productExternalId,
productionExternalId,
quantity
}) {
return (
<Option
className="product-select-box-item"
key={productName}
value={productName}
>
<div className="d-flex flex-column">
<div className="d-flex" style={{ marginBottom: "0.2rem" }}>
<div className="mr-auto-1 font-weight-bold">{productName}</div>
<div className="uppercase">{productionExternalId}</div>
</div>
<div className="d-flex" style={{ marginBottom: "0.01rem" }}>
<div className="mr-auto-1 uppercase">{productExternalId}</div>
<div>{quantity}</div>
</div>
</div>
</Option>
);
}
return (
<Select
// labelInValue
// defaultValue={{ key: "product-3", label: "product-3" }}
className="product-select-box"
size="large"
onSelect={onSelect}
>
{details.map(product => customizedOption(product))}
</Select>
);
});
ReactDOM.render(
<div>
<ProductSelectBox details={data} />
</div>,
document.getElementById("container")
);
Referring from your comment:
To fix your warnings, on customizedDisplayOnSelection and getSelectedMeta you should return a ReactNode and not a string, for example you can just return null which is a valid ReactNode or not return anything.
function customizedDisplayOnSelection(productName) {
if (productMap[productName]) {
...
}
// or
else {
return null;
}
}
Furthermore, you can take advantage of && short-circuit.
const customizedDisplayOnSelection = productName =>
productMap[productName] && (
<span className="font-weight-medium">
{productMap[productName].productExternalId} -{productName}
</span>
);
Check fixed example:
I was able to achieve this with the antd's value property on Select box. Here is the demo I updated in sandbox:
For easier reference, I am also posting the code here:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Select } from "antd";
const { Option } = Select;
const data = [
{
productName: "product-1",
productExternalId: "DT01A",
productionExternalId: "PL-DT01A",
quantity: "700 kg"
},
{
productName: "product-2",
productExternalId: "DT02A",
productionExternalId: "PL-DT02A",
quantity: "702 kg"
},
{
productName: "product-3",
productExternalId: "DT03A",
productionExternalId: "PL-DT03A",
quantity: "703 kg"
}
];
const ProductSelectBox = React.memo(props => {
const { details } = props;
let { defaultSelected } = props;
const productMap = {};
details.forEach(product => {
productMap[product.productName] = product;
});
const [selectedProduct, selectProduct] = useState(defaultSelected);
function onSelect(value) {
selectProduct(value);
}
function customizedDisplayOnSelection(productName) {
if (productMap[productName]) {
const productExternalId = productMap[productName]["productExternalId"];
return (
<span className="font-weight-medium">
{productExternalId} - {productName}
</span>
);
} else {
return "";
}
}
function getSelectedMeta() {
if (productMap[selectedProduct]) {
return (
<span className="font-weight-medium">
(
<span className="uppercase">
production id: {productMap[selectedProduct]["productionExternalId"]}
</span>
<span style={{ marginLeft: "0.75rem" }}>
Batch Size: {productMap[selectedProduct]["quantity"]}
</span>
)
</span>
);
} else {
return "";
}
}
function customizedOption({
productName,
productExternalId,
productionExternalId,
quantity
}) {
return (
<Option
className="product-select-box-item"
key={productName}
value={productName}
>
<div className="d-flex flex-column">
<div className="d-flex" style={{ marginBottom: "0.2rem" }}>
<div className="mr-auto-1 font-weight-bold">{productName}</div>
<div className="uppercase">{productionExternalId}</div>
</div>
<div className="d-flex" style={{ marginBottom: "0.01rem" }}>
<div className="mr-auto-1 uppercase">{productExternalId}</div>
<div>{quantity}</div>
</div>
</div>
</Option>
);
}
return (
<div className="d-flex flex-row">
<Select
className="product-select-box auto-flex"
size="large"
value={customizedDisplayOnSelection(selectedProduct)}
onSelect={onSelect}
>
{details.map(product => customizedOption(product))}
</Select>
<div className="d-flex align-items-center auto-flex">
{getSelectedMeta()}
</div>
</div>
);
});
ReactDOM.render(
<div>
<ProductSelectBox details={data} defaultSelected="" />
</div>,
document.getElementById("container")
);

How do I prevent drop in originating container in react js and react-beautiful-dnd?

I am using React and react-beautiful-dnd.
This has had me stumped
My use case is to drag items from container1 to container2.
Items in Container1 cannot be dropped in Container 1 only in Container 2.
If an item in container 1 is dragging the remaining items in Container 1 should not move to allow a drop.
I created a sample fiddle to isolate the use case - https://codesandbox.io/s/34z92ny69p
Any help is appreciated
G
If I'm understanding what you want:
Allow Container 1 items to be moved into Container 2.
Do not allow Container 1 to be empty.
Do not allow any items to be moved back into Container 1.
Optional: I also set it up so that you can disable dragging by passing down an isDragDisabled prop in the DroppableContainer, for example:
<DroppableContainer
droppableId="Container2"
title="Container 2"
data={this.state.container2Data}
isDragDisabled
/>
Working example: https://codesandbox.io/s/moy02o60yx
components/Workspace.js
import React, { Component } from "react";
import { DragDropContext } from "react-beautiful-dnd";
import { Grid, Row } from "react-bootstrap";
import DroppableContainer from "./DroppableContainer";
const testData = {
container1Data: [
{ id: 1, name: "item 1" },
{ id: 2, name: "item 2" },
{ id: 3, name: "item 3" },
{ id: 4, name: "item 4" },
{ id: 5, name: "item 5" },
{ id: 6, name: "item 6" }
],
container2Data: [
{ id: 101, name: "other item 1" },
{ id: 102, name: "other item 2" }
]
};
export default class Workspace extends Component {
state = {
container1Data: testData.container1Data,
container2Data: testData.container2Data
};
onDragEnd = ({ destination, source }) => {
if (
!destination ||
destination.droppableId !== "Container2" ||
(destination.droppableId === source.droppableId &&
destination.index === source.index)
) {
return;
}
this.setState(prevState => {
const addItem = prevState.container1Data.splice(source.index, 1);
prevState.container2Data.splice(destination.index, 0, ...addItem);
return {
container1Data: [...prevState.container1Data],
container2Data: [...prevState.container2Data]
};
});
};
render = () => (
<DragDropContext onDragEnd={this.onDragEnd}>
<Grid bsClass="box-container">
<Row>
<DroppableContainer
droppableId="Container1"
title="Container 1"
data={this.state.container1Data}
dropDisabled
/>
<DroppableContainer
droppableId="Container2"
title="Container 2"
data={this.state.container2Data}
/>
</Row>
</Grid>
</DragDropContext>
);
}
components/DroppableContainer.js
import React, { PureComponent } from "react";
import { Droppable } from "react-beautiful-dnd";
import { Col } from "react-bootstrap";
import styled from "styled-components";
import DraggableItem from "./DraggableItem";
const StyledDiv = styled.div`
border: 1px solid #000080;
padding: 15px;
`;
export default class DroppableContainer extends PureComponent {
renderDraggableItems = () =>
this.props.data.map((item, i) => (
<DraggableItem
key={i}
item={item}
index={i}
isDragDisabled={
this.props.isDragDisabled || this.props.data.length === 1
}
/>
));
render = () => (
<Col sm={4}>
<Droppable
droppableId={this.props.droppableId}
isDropDisabled={this.props.dropDisabled || false}
>
{provided => (
<StyledDiv
className={`container ${this.props.data.length === 1 ? "disabled" : null }`}
ref={provided.innerRef}
{...provided.droppableProps}
>
<div className="row">
<div className="col">{this.props.title}</div>
</div>
{this.renderDraggableItems()}
{provided.placeholder}
</StyledDiv>
)}
</Droppable>
</Col>
);
}
components/DraggableItem.js
import React from "react";
import { Draggable } from "react-beautiful-dnd";
import { Col } from "react-bootstrap";
import styled from "styled-components";
const DragItemStyled = styled.span`
text-transform: uppercase;
outline: none;
border: 0;
background-color: ${props => (props.isDragDisabled ? "#d8d8d8" : "#bec7bd")};
line-height: 32px;
color: ${props => (props.isDragDisabled ? "#a9a9a9" : "#000080")};
display: inline-block;
font-family: Karla, Verdana, sans-serif;
font-size: 14px;
padding-left: 15px;
padding-right: 10px;
cursor: ${props => (props.isDragDisabled ? "no-drop" : "grab")};
border-radius: 5px;
margin-bottom: 5px;
width: 150px;
`;
const DraggableItem = ({ item, index, isDragDisabled }) => (
<Draggable
key={item.id}
draggableId={JSON.stringify({
nodeId: item.id,
type: "DragItem"
})}
index={index}
isDragDisabled={isDragDisabled}
>
{provided => (
<div
className="row"
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<Col md={4}>
<DragItemStyled isDragDisabled={isDragDisabled}>
{item.name}
</DragItemStyled>
</Col>
</div>
)}
</Draggable>
);
export default DraggableItem;

Categories