Drag drop in React.js - javascript

I'm trying to make a drag-drop functionality on a webpage but unsuccessful so far. I want to make the A, B, C cards in the pink box stay where they are after we drag a copy of them to the black box (like a menu box, link for a demo of this behavior). I don't know what element or code that makes the box stay where they are like a menu bar despite many attempts of finding, so any help would be appreciated! Thank you!
So far, since I'm a beginner in React, my problem is that the online examples I refer to are written in a single file using 'class App', but what I'm trying to do is to have separate components, and I don't know how to convert the code properly.
My app includes index.js, App.js, and the components:\
Card.js
import React from "react";
function Card(props) {
const dragStart = (e) => {
const target = e.target;
e.dataTransfer.dropEffect = "move";
e.dataTransfer.setData("card_id", target.id);
setTimeout(() => {
target.style.display = "none";
}, 0);
};
const dragOver = (e) => {
e.stopPropagation();
// e.dataTransfer.dropEffect = "copy";
};
return (
<div
id={props.id}
className={props.className}
draggable={props.draggable}
onDragStart={dragStart}
onDragOver={dragOver}
>
{props.children}
</div>
);
}
export default Card;
Board.js (the black box)
import React from "react";
function Board(props) {
const drop = (e) => {
e.preventDefault();
const card_id = e.dataTransfer.getData("card_id");
const card = document.getElementById(card_id);
card.style.display = "block";
e.target.appendChild(card);
};
const dragOver = (e) => {
e.preventDefault();
};
return (
<div
id={props.id}
className={props.className}
onDrop={drop}
onDragOver={dragOver}
>
{props.children}
</div>
);
}
export default Board;
Menu.js (the pink box)
import React from "react";
import Card from "./Card";
function Menu(props) {
const dragOver = (e) => {
e.preventDefault();
};
return (
<div id={props.id} className={props.className} onDragOver={dragOver}>
{props.children}
</div>
);
}
export default Menu;
My App.js is currently like this:
import React, { useState } from "react";
import Board from "./components/Board";
import Card from "./components/Card";
import Menu from "./components/Menu";
function App() {
const [card_list, setList] = useState([
{ id: "card-1", value: "A" },
{ id: "card-2", value: "B" },
{ id: "card-3", value: "C" },
]);
return (
<div className="flexbox">
<Menu id="menu-1" className="menu">
{card_list.map((item) => {
return (
<Card
key={item.id}
value={item.value}
id={item.id}
className="card"
draggable="true"
>
<p> {item.value}</p>
</Card>
);
})}
</Menu>
<Board id="board-2" className="board">
<Card id="card-other" className="card">
<p> Card two </p>
</Card>
</Board>
</div>
);
}
export default App;
And index.js is nothing but:
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);

You can implement dragging and dropping using react-grid-layout easily.
Here is react-grid-layout example.
https://codesandbox.io/examples/package/react-grid-layout
In addition to it, you can use react-dnd module for dragging and dropping.

Related

I'm attempting to use boolean props: when the user clicks on the Selected button, the Select text should be hidden

and when the user clicks "Selected," show the Select,Please take a look at my code and tell me where I'm going wrong.
This is the App.js file I'm working on. 
import "./styles.css";
import MainShow from "./Show";
import { useState } from "react";
export default function App() {
var [isDone, setDone] = useState(false);
const Delected = () => {
setDone(!isDone);
console.log("set");
};
const Selected = () => {
setDone(!isDone);
console.log("Del");
};
return (
<div className="App">
<h1>Hello ,Problem Solver</h1>
<MainShow DoneMain={isDone} /> //imported
<button onClick={Delected}>Delected</button> //Delected button
<button onClick={Selected}>Selected</button> //Selected button
</div>
);
}
This MainShow file has the Main function.Look at my code on CodeSandbox
import React, { useState } from "react";
const Main = (props) => {
const [isDone] = useState(props.DoneMain);
console.log(isDone);
return (
<div>
<div>
<div
className="container"
style={{ display: isDone ? "block" : "none" }}
>
<p> Select</p>
</div>
<p>Hello</p>
</div>
</div>
);
};
export default Main;
When the user clicks on the "Deleted" button, I want "Select" to be hidden, and when the user clicks on "Selected," I want "Select" to be displayed. 
Check for state value changes and show/hide
const Delected = () => {
setDone(false);
console.log("set");
};
const Selected = () => {
setDone(true);
console.log("Del");
};
{isDone && <MainShow DoneMain={isDone} />}
Demo

react testing library: how to test component in which user press button after should open modal in react portal

I am testing this component Sidebar
const Sidebar = () => {
const { modalVisible, modalVisibleHandler } = useModalVisible();
return (
<div className="sidebar">
<Profile />
<Menu />
<WorkSpace all={true} />
<SidebarButton openModal={modalVisibleHandler} />
{modalVisible && (
<Portal>
<TaskCreate />
</Portal>
)}
</div>
);
};
My Portal component
import { useEffect } from "react";
import ReactDOM from "react-dom";
const modalRoot = document.getElementById("modal-root");
export default function Portal({ children }: { children: any }) {
const el = document.createElement("div");
useEffect(() => {
if (modalRoot) {
modalRoot.appendChild(el);
}
return () => {
if (modalRoot) modalRoot.removeChild(el);
};
}, [el]);
return ReactDOM.createPortal(children, el);
}
and TaskCreate component
const TaskCreate = () => (
<div
className="task-create task-view"
id="task-create"
>
this component will open when user click button in Sidebar component
</div>
);
I have in TaskCreate component id="task-create" so before user click on "open modal" in Sidebar, TaskCreate component with id "task-create" I need to test that this id not.toBeInTheDocument() and after user click toBeInTheDocument()
The problem is that Portal is connect with id "modal-root" which is in the index.html and my test can't get this bind

React - How to apply multiple values from an object using context

I use a color theme in my project for this, I used a React context to pass the value in several components, everything worked fine for me until I decided to add another property inside the object in order to apply different colors to different components, for example a ThemeBackground property that takes green as a value it will be applied to the RoutesPage component and a side property that takes an orange color it will be applied to the SideBar component. The problem is that I cannot apply the side property for the SideBar component, I tried several options, but I did not succeed right now, I will show you everything in more detail in the pictures so that you clearly understand the problem and then I will provide you with the code
Notice the ThemesBackground property is successfully applied to the content but the problem is that I want to apply the side property to my sidebar at the moment I imported the ThemeBackground property for my sidebar so my sidebar applies red color but I think you already understood the problem in short property ThemeBackground should be applied to content and side property to sidebar
LessonThemes.jsx
import React, { useState, useEffect, createContext } from "react";
import SideBar from "./SideBar";
import RoutesPage from "../pages/Routes";
export const CounterContext = createContext(["color"]);
export default function LessonThemes(props) {
const [BackgroundTheme, SetBackgroundTheme] = useState(localStorage.getItem("color"));
const [themes, setThemes] = useState([
{ name: "G", ThemeBackground: "maroon", side: "orange" },
{ name: "R", ThemeBackground: "red", side: "aqua" },
{ name: "B", ThemeBackground: "blue", side: "pink" },
])
useEffect(() => {
localStorage.setItem("color", BackgroundTheme);
})
const SideBarPageContent = (SideBarPageContentBackground) => {
localStorage.setItem('color', SideBarPageContentBackground);
SetBackgroundTheme(SideBarPageContentBackground);
}
const list = themes.map((theme, index) => {
return (
<label key={index}>
<input
onChange={() => SideBarPageContent(theme.ThemeBackground)}
type="radio"
name="background"
/>{theme.name}</label>
);
})
return (
<CounterContext.Provider value={[BackgroundTheme, SetBackgroundTheme]}>
<SideBar list={list} {...props} />
<RoutesPage path={props.match} />
</CounterContext.Provider>
);
}
SideBar.jsx
import React from 'react';
import {CounterContext} from "./LessonThemes";
import SideBarMenu from "./SideBarMenu";
import '../css/Sidebar.css'
export default function SideBar(props) {
const [BackgroundTheme, SetBackgroundTheme] = React.useContext(CounterContext);
return (
<div className="wrappers">
<nav id="sidebar" className="sidebar-wrapper modal">
<div style={{background: BackgroundTheme}} className={"sidebar-page-content"}>
<div className="sidebar-brand">
<div className="sidebar-brand-container">
<div>
{props.list}
</div>
<div>
<span href="#">Theme</span>
</div>
</div>
</div>
<div className="sidebar-menu">
<SideBarMenu path={props.match.path}/>
</div>
...
</div>
</nav>
</div>
);
}
I don't know if it will be useful to you or not, but in addition I want to demonstrate the RoutesPage component, although everything is in order there
RoutesPage.jsx
import React from "react";
import {Route, Switch} from "react-router-dom";
import '../css/Sidebar.css'
import {CounterContext} from "../components/LessonThemes";
function RoutesPage(props) {
const {path} = props.path;
const routes = [
{
path: `${path}`,
exact: true,
component: () => <h2>Home</h2>
},
{
path: `${path}/Calendar`,
component: () => <h2>Test123</h2>
},
{
path: `${path}/Guardian`,
component: () => <h2>Shoelaces</h2>
}
];
const [BackgroundTheme, SetBackgroundTheme] = React.useContext(CounterContext);
return (
<>
<main style={{background: BackgroundTheme}} className="page-content">
<div className="page-container">
<h2>Pro Sidebar</h2>
<hr/>
<div className="tabs">
<Switch>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</div>
</div>
</main>
</>
);
}
export default RoutesPage;
Is it possible that in the SideBar.jsx, the div style needs to be set to BackgroundTheme not SideBarBackgroundTheme? Also wouldn't it be passing in the object so you would still need to key into it for specific colors? Like BackgroundTheme.side?
The problem is that the side property is never being stored to the value of the context. Your context value is still just a string. The complete theme object exists is the local state of the LessonThemes component only.
In the onChange handler of your input you call SideBarPageContent which in turn calls SetBackgroundTheme, which updates the BackgroundTheme property which you pass to the context provider. The argument that you are passing to this function call is theme.ThemeBackground -- which is the background color only and not the entire object.
You likely want to refactor your code so that the context contains the whole object.
Within the SideBar component, it's unclear where you think that the variable SideBarBackgroundTheme is coming from, but that variable doesn't exist.
LessonThemes.jsx
import React, {useState, useEffect, createContext} from "react";
import SideBar from "./SideBar";
import RoutesPage from "../pages/Routes";
export const CounterContext = createContext([]);
export default function LessonThemes(props) {
const [SideBarTheme, SetSideBarTheme] = useState(localStorage.getItem("SideBarKey"));
const [PageContentTheme, SetPageContentTheme] = useState(localStorage.getItem("PageContentKey"));
const [themes, setThemes] = useState([
{
name: "G",
SideBar: "maroon",
PageContent: "blue",
},
{
name: "R",
SideBar: "gray",
PageContent: "green",
},
])
useEffect(() => {
localStorage.setItem("SideBarKey", SideBarTheme, "PageContentKey", PageContentTheme);
})
const SideBarPageContent = (PageContent, SideBar) => {
localStorage.setItem('PageContentKey', PageContent, 'SideBarKey', SideBar);
SetPageContentTheme(PageContent);
SetSideBarTheme(SideBar);
}
const list = themes.map((theme, index) => {
return (
<label key={index}>
<input
onChange={() => SideBarPageContent(theme.PageContent, theme.SideBar)}
type="radio"
name="background"
/>{theme.name}</label>
);
})
return (
<CounterContext.Provider value={{
SideBarValue: [SideBarTheme, SetSideBarTheme],
PageContentValue: [PageContentTheme, SetPageContentTheme]
}}>
<SideBar list={list} {...props} />
<RoutesPage path={props.match}/>
</CounterContext.Provider>
);
}
SideBar.jsx
export default function SideBar(props) {
const { SideBarValue } = React.useContext(CounterContext);
const [SideBarTheme, SetSideBarTheme] = SideBarValue;
return (
<div className="wrappers">
<nav id="sidebar" className="sidebar-wrapper modal">
<div style={{background: SideBarTheme}} className={"sidebar-page-content"}>
<div className="sidebar-brand">
<div className="sidebar-brand-container">
<div>
{props.list}
</div>
<div>
<span href="#">Theme</span>
</div>
</div>
...
RoutesPage.jsx
import React from "react";
import {Route, Switch} from "react-router-dom";
import '../css/Sidebar.css'
import {CounterContext} from "../components/LessonThemes";
function RoutesPage(props) {
const {path} = props.path;
const routes = [
{
path: `${path}`,
exact: true,
component: () => <h2>Home</h2>
},
{
path: `${path}/Calendar`,
component: () => <h2>Test123</h2>
},
{
path: `${path}/Guardian`,
component: () => <h2>Shoelaces</h2>
}
];
const { PageContentValue } = React.useContext(CounterContext);
const [PageContentTheme, SetPageContentTheme] = PageContentValue;
return (
<>
<main style={{background: PageContentTheme}} className="page-content">
...

React Context API + hook

I am fairly new to Context API. So bassicaly, I want when I press on the Button in the ButtonComponent everything in ButtonComponent disapears as well in ImageComponent but when I click on the Button nothing happens. I am kind of stuck with this I would be very grateful if I got someone to help me if possible. Thanks in Advance!
//HiddenContext
import React from "react";
export const HiddenContext = React.createContext(false);
function HiddenProvider({ children }) {
const [hideButton, setHideButton] = React.useState(false);
function handleClick() {
setHideButton(true);
}
return (
<HiddenContext.Provider value={{ hideButton, handleClick }}>
{children}
</HiddenContext.Provider>
);
}
// App Component/Parent
import React, { useState } from 'react';
import './App.css';
export const HiddenContext = React.createContext(false);
function HiddenProvider({ children }) {
const [hideButton, setHideButton] = React.useState(false);
function handleClick() {
setHideButton(true);
}
return (
<HiddenContext.Provider value={{ hideButton, handleClick }}>
{children}
</HiddenContext.Provider>
);
}
function App() {
const { hideButton } = React.useContext(HiddenContext);
return (
<HiddenProvider>
<div className="App">
<ImageComponent hideButton={hideButton} />
</div>
</HiddenProvider>
);
}
//ButtonComponent
import React, {useState,ReactFragment} from 'react'
import { HiddenContext, } from './HiddenContext';
function ButtonComponent() {
const { hideButton, handleClick } = React.useContext(HiddenContext);
return (
<React.Fragment>
{!hideButton && (
<li>
<img className="image" src="./icons" alt="" />
<Button
onClick={handleClick}
variant="outlined"
className="button__rightpage"
>
Hide
</Button>
<caption className="text"> Hide</caption>
</li>
)}
</React.Fragment>
);
}
// ImageComponent
import React, {useState, ReactFragment} from 'react'
import { HiddenContext, } from './HiddenContext';
const ImageComponent = () => {
const { hideButton } = React.useContext(HiddenContext);
return (
<div>
{!hideButton && (
<React.Fragment>
<img src="icons/icon.png" alt="" />
<caption>Image </caption>
</React.Fragment>
)}
</div>
);
};
We created here 2 context - instead of 1
I make a codesendbox for us to see the fix.
https://codesandbox.io/s/focused-night-i95fr
We should create context only one time, to wrap the App component with the provider, and we can use this context like you did wherever we want
And relative to the beginner, it seems from your code that you understand what you are doing
about your comment - attached a pic
You are trying to access the context value outside the provider (in App).
Try to remove this from App, like so:
function App() {
return (
<HiddenProvider>
<div className="App">
<ImageComponent />
</div>
</HiddenProvider>
);
}

How to create a simple list maker app in React.JS?

I'm working on a simple list maker, to do list app using create-react-app and I'm having some trouble puzzling out the functionality. What I'm trying to accomplish with this app:
I want to be able to enter text into an input, push the button or press enter, and whatever text will be listed on the body of the app.
I want to be able to create a button that will delete the list items once the task or objective is complete
My code is broken up into these components so far:
App,
ListInput,
ItemList,
Item
The code for App is
import React, { Component } from 'react';
import './App.css';
import Navigation from './components/Navigation';
import ListInput from './components/ListInput';
import ListName from './components/ListName';
import Item from './components/Item';
import ItemList from './components/ItemList';
class App extends Component {
constructor() {
super();
this.state = {
input: '',
items: []
};
}
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState({
items: [this.state.items, inputValue]
})
}
})
}
onButtonEnter = () => {
this.addItem();
}
render() {
return (
<div className="App">
<Navigation />
<ListName />
<ListInput addItem={this.addItem}
onButtonEnter={this.onButtonEnter} />
<Item />
<ItemList />
</div>
);
}
}
export default App;
The code for ListInput is :
import React from 'react';
import './ListInput.css';
const ListInput = ({ addItem, onButtonEnter }) => {
return (
<div>
<p className='center f2'>
{'Enter List Item'}
</p>
<div className='center'>
<div className='center f3 br-6 shadow-5 pa3 '>
<input type='text'
className='f4 pa2 w-70 center'
placeholder='Enter Here'
/>
<button className='w-30 grow f4 link ph3 pv2 dib white bg-black'
onClick={onButtonEnter}
onSubmit={addItem} >
{'Enter'}
</button>
</div>
</div>
</div>
);
}
export default ListInput;
The code for Item is:
import React from 'react';
const Item = ({text}) =>{
return (
<div>
<ul>{text}</ul>
</div>
)}
export default Item;
And the code for ItemList is :
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{item.map(items => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
In my react app I am returning an error of 'item' is not defined and I'm confused why.
In your App.js you need to pass items as a prop to ItemList component like
<ItemList items={this.state.items} />
Also in addItem function pushing inputValue to items array isn’t correct do something like below
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState(prevState => ({
items: [...prevState.items, inputValue]
}));
}
})
}
And in ItemList.js do conditional check before doing .map also some typo errors in .map
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{items && items.map(item => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
Try with above changes This would work
Please excuse me if there are any typo errors because I am answering from my mobile
Your ItemList was not correct. Take a look at corrected snippet below you need to map on items and not item (hence the error item is not defined). Also, you need to items as a prop to ItemList in your app.js
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{items.map(item => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
In app.js add following line. Also, I don't see what is doing in your app.js remove it.
<ItemList items={this.state.items}/>
Seems like you have a typo in ItemList.
It receives items (plural) as prop but you are using item.
const ItemList = ({ items }) => {
return (
<div>
{items.map(items => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
And don't forget to actually pass the items prop to 'ItemList':
<ItemList items={this.state.items} />

Categories

Resources