Right way of implementing a modal in React - css

I would like to have a modal that will pop up when a table row is clicked. The modal is opening when I click a row in my table component. However I'm not getting the desired result with the css. I want it to overlap everything that is on the page when a row is clicked. Right now it's showing on top of the page and I cant see the content in it.
//Modal.js
import React from "react";
import Table from "react-bootstrap/Table";
export default function Modal() {
return (
<div className="modalContainer">
<Table responsive="true" size="sm" striped bordered hover>
<thead>
<tr>
<th>Own Product</th>
<th>Competitors Products</th>
</tr>
</thead>
<p>Brand</p>
<p>Category</p>
<p>In Stock</p>
<p>Name</p>
<p>Price</p>
<p>Product Code</p>
<p>Product Link</p>
</Table>
</div>
);
}
//Code from Table.js
render() {
let { isLoaded, products } = this.state; //instead of typing
this.state all the time
if (!isLoaded) {
return <Loading />;
} else {
return (
<div className="tableContainer">
{this.props.rows}
<Table responsive="true" size="sm" striped bordered hover>
<thead>
<tr>
<th>Product ID</th>
<th>Product Name</th>
<th>Match ID</th>
<th>Match Score</th>
<th>Match Name</th>
<th>Match Price</th>
<th>Match State</th>
</tr>
</thead>
<tbody>
{products.map(product => (
//use filter instead to show only the matched ones
<tr key={product.id} onClick={() => this.toggleModal()}>
<td>{product.id}</td>
<td>{product.name}</td>
<td>{product.matches[0].id}</td>
<td>{Math.round(product.matches[0].score)}</td>
<td>{product.matches[0].name}</td>
<td>{product.matches[0].price}</td>
<td>{product.matches[0].matchLabel}</td>
</tr>
))}
{this.state.modalOpen ? <Modal /> : null}
</tbody>
</Table>
</div>
);
}
}
//CSS
.tableContainer {
position: relative;
width: 100%;
height: 100%;
}
.modalContainer {
margin: -30% auto;
position: absolute;
width: 100%;
height: 100%;
justify-content: center;
border: 1px solid black;
z-index: 1;
left: 0;
top: 0;
overflow: auto;
background-color: rgba(219, 239, 250);
}

The issue is that your tableContainer is position:relative, which re-sets the positioning context for its children. So, your <Modal> is absolutely positioned with respect to the tableContainer instead of the browser window.
You can either change your css to so your Modal is e.g. position:fixed or move your modal out of your tableContainer like this:
return (
<>
{this.state.modalOpen ? <Modal /> : null}
<div className="tableContainer">
{this.props.rows}
<Table responsive="true" size="sm" striped bordered hover>
//....//
</Table>
</div>
</>

State for modal
state = {
axiosStatus: {
status: '',
title: '',
details: '',
},
modal: false,
}
modal handler
modalHandler = ()=> {
this.setState({modal: !this.state.modal});
};
modal content hander
axiosStatusHandler = (status, title, details)=>{
let oldState = this.state.axiosStatus;
oldState.status = status;
oldState.title = title;
oldState.details = details;
this.setState({axiosStatus: oldState});
};
Jsx for modal
<Modal show={this.state.modal} modalClosed={this.modalHandler}>
<ModalContent
status = {this.state.axiosStatus.status}
title = {this.state.axiosStatus.title}
details = {this.state.axiosStatus.details}
/>
</Modal>
Modal Component
import React from 'react';
import './Modal.css';
import Aux from '../../../hoc/Auxi';
import Backdrop from '../Backdrop/Backdrop';
const Modal = ( props ) => (
<Aux>
<Backdrop show={props.show} clicked={props.modalClosed} />
<div
className={"Modal"}
style={{
transform: props.show ? 'translateY(0)' : 'translateY(-100vh)',
opacity: props.show ? '1' : '0'
}}>
{props.children}
</div>
</Aux>
);
export default Modal;
Backdrop Component
import React from 'react';
import './Backdrop.css';
const backdrop = (props) => (
props.show ? <div className={"Backdrop"} onClick={props.clicked}></div> : null
);
export default backdrop;
Backdrop css
.Backdrop {
width: 100%;
height: 100%;
position: fixed;
z-index: 100;
left: 0;
top: 0;
background-color: rgba(0, 0, 0, 0.5);
}
ModalContenet Component
import React from 'react';
const ModalContent = (props)=>{
return (
<div style={{textAlign: 'center'}}>
{/* <h3 style={{color: '#FF0000'}}>Failed</h3>*/}
<b><h2 style={{color: '#FF0000'}}>{props.title}</h2></b>
<h2 style={{color: '#FF0000'}}>{props.details}</h2>
</div>
)
};
export default ModalContent;

Related

How to change the CSS of a targeted div

I am creating an interactive rating card with react. I have 5 divs which represent the number you are rating. When I click on one number I want to change the background of the targeted div to white.
import './App.css';
import React, { useState } from 'react';
function App() {
const [color, setColor] = useState('blue');
const changeColor = () => {
setColor('white');
};
return (
<div className="card">
<div className="container">
<p class="question">How did we do?</p>
<p>
Please let us know how we did with your support request. All feedback
is appreciated to help us improve our offering!
</p>
<div id="numbers">
<div
className="circle"
onClick={setColor}
style={{ backgroundColor: color }}
>
1
</div>
<div
className="circle"
onClick={changeColor}
style={{ backgroundColor: color }}
>
2
</div>
<div className="circle">3</div>
<div className="circle">4</div>
<div className="circle">5</div>
</div>
<button className="btn"> Submit </button>
</div>
</div>
);
}
export default App;
So far I tried to work with an useState hook. I read in some other sources to use the e.target.value or to give every div a special key value. I tried it with both but didn't manage to solve it. At the moment div 1 and div 2 change the color if I click on one of them.
const App = () => {
const [selectedStar, setSelectedStar] = React.useState();
return (
<div>
<div
className={`circle ${selectedStar === 1 && "active"}`}
onClick={() => setSelectedStar(1)}
>
1
</div>
<div
className={`circle ${selectedStar === 2 && "active"}`}
onClick={() => setSelectedStar(2)}
>
2
</div>
<div
className={`circle ${selectedStar === 3 && "active"}`}
onClick={() => setSelectedStar(3)}
>
3
</div>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.circle {
height: 20px;
width: 20px;
background: blue;
text-align: center;
color: white;
border-radius: 50%;
}
.circle.active {
background: white;
color: black;
}
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="root"></div>

Reactjs sidebar doesn't collapse and dropdown doesn't open

I am trying to achieve two things:
(1) each time I click on the red arrow icon in the sidebar, I want the sidebar to collapse or open. From the below video, you'd see that the active and inactive states are already there. However, the sidebar doesn't collapse on inactive.
(2) each time I click on the Content menu, which is a drowndown menu, it doesn't open the submenu. Also, from the below video, you'd notice that the active and inactive states are already there. However, the dropdown still doesn't open on active.
Below is the video that clearly shows the error:
https://www.loom.com/share/6e0488101cee4c5b9bac7ded782b8807
Docs.js Page
import React from "react";
import { Helmet } from "react-helmet";
import SideMenu from "../docs/SideMenu";
const Docs = () => {
return (
<div className="">
<Helmet>
<title>Docs :: MyApp</title>
<meta name="description" content="MyApp" />
</Helmet>
<SideMenu />
</div >
)
};
export default Docs
SideMenu.js Component
import React, { useState } from "react";
import { Helmet } from "react-helmet";
import * as Icon from "react-bootstrap-icons";
import MenuItems from "./MenuItems";
const SideMenu = () => {
const [inActive, setInActive] = useState(false)
return (
<div className="">
<div className={`side-menu ${inActive ? "inActive" : ""}`}>
<Helmet>
<title>Docs :: MyApp</title>
<meta name="description" content="MyApp" />
</Helmet>
<div className="top-section">
<div className="logo">
<img src="/assets/media/logos/naked.png" alt="MyApp" />
</div>
<div onClick={() => setInActive(!inActive)} className="toggle-back">
{inActive ? (<Icon.ArrowLeftSquareFill />) : (<Icon.ArrowRightSquareFill />)}
</div>
</div>
<div className="search-bar">
<button className="search-bar-btn">
<Icon.Search />
</button>
<input type="text" placeholder="search" />
</div>
<div className="divider"></div>
<div className="main-menu">
<ul>
{menuItems.map((menuItem, index) => (
<MenuItems
key={index}
name={menuItem.name}
to={menuItem.to}
subMenu={menuItem.subMenu || []} />
))}
{/*<li>
<a className="menu-item">
<Icon.ArrowRightSquareFill className="menu-icon" />
<span>Dashboard</span>
</a>
</li>
<MenuItems
name={"Content"}
subMenu={[
{ name: 'Courses' },
{ name: 'Videos' },
]}
/>
<li>
<a className="menu-item">
<Icon.ArrowRightSquareFill className="menu-icon" />
<span>Support</span>
</a>
</li>*/}
</ul>
</div>
<div className="side-menu-footer">
<div className="avatar">
<img src="/assets/media/avatars/aa/brooks_lloyd.png" alt="MyApp" />
</div>
<div className="user-info">
<div className="font-size-h6">Title</div>
<div className="font-size-sm">Subtitle</div>
</div>
</div>
</div>
</div>
);
};
export default SideMenu
const menuItems = [
{ name: "Dashboard", to: "/" },
{ name: "Content", to: "/", subMenu: [{ name: "Courses" }, { name: "Videos" }], },
{ name: "Design", to: "/" },
];
MenuItems.js Component
import React, { useState } from "react";
import * as Icon from "react-bootstrap-icons";
const MenuItems = (props) => {
const { name, subMenu } = props;
const [expand, setExpand] = useState(false);
return (
<div className="">
<li>
<a onClick={() => setExpand(!expand)} className="menu-item">
<Icon.ArrowRightSquareFill className="menu-icon" />
<span>{name}</span>
</a>
{
subMenu && subMenu.length > 0 ? (
<ul className={`sub-menu ${expand ? "active" : ""}`}>
{subMenu.map((menu, index) =>
<li key={index}>
<a className="sub-menu">
<Icon.ArrowRightSquareFill className="menu-icon" />
{menu.name}
</a>
</li>
)}
</ul>) : null}
</li>
</div>
);
};
export default MenuItems
Docs.css File that contains the suspected errors, which are the side-menu and sub-menu lines:
.side-menu {
position: fixed;
background: #000;
width: 300px;
height: 100%;
box-sizing: border-box;
padding: 30px 20px;
transition: width .2s ease-in;
}
.side-menu.inactive {
width: 80px;
}
.side-menu .main-menu .sub-menu {
color: #333;
margin-left: 20px;
border-left: 1px solid #666;
box-sizing: border-box;
padding-left: 30px;
max-height: 0;
overflow: hidden;
transition: max-height .2s ease-in;
}
.side-menu .main-menu .sub-menu.active {
max-height: 200px;
}

Cannot centralize div on bootstrap

I'm trying to centralize the div "Status" using css. Already tryied to use vertical-align: middle, display: flex, align-item: center and nothing works. Can someone help me? it seens like the height of the div remains the same so I can't centralize it since it's content fills it's exactly entire space.
import React from 'react'
import { Row, Col } from 'reactstrap'
import styled from 'styled-components'
import Status from './Status'
export default ({ temporadas = [], temporadaSelecionada = {}, onChange = () => {} }) => {
return (
<StyledContainer>
<Row>
<Col md={12}>
{temporadas.map(temporada => {
return (
<StyledCard selected={temporadaSelecionada.codigo === temporada.codigo}
onClick={() => onChange(temporada)}>
<Status className='pull-right' style={{ marginRight: '-8px' }} ativa={temporada.status === 'A'} />
{/* <StyledIcon className={temporadaSelecionada.codigo === temporada.codigo ? 'fa fa-fw fa-minus' : 'fa fa-fw fa-plus'} /> */}
<StyledText alt={temporada.descricao} title={temporada.descricao}>{temporada.descricao || '-'}</StyledText>
</StyledCard>
)
})}
</Col>
</Row>
</StyledContainer>
)
}
const StyledContainer = styled.div`
margin-bottom: 10px;
`
const StyledCard = styled.div`
cursor: pointer;
padding: 16px;
display: block;
border: 1px solid #DADFEA;
background-color: #F4F7FA;
font-size: 100%
&:not(:first-child) {
margin-top: -1px;
}
&:last-child {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
${({ selected }) => selected && `
border-left: 5px solid #C5CBD9;
padding-left: 12px;
`}
`
const StyledText = styled.span`
margin: 0;
`
const StyledIcon = styled.i`
font-size: 10px;
`
The div "Status" is imported from another file and it behaves like this:
import React from 'react'
import styled from 'styled-components'
export default ({ ativa, label, ...props }) => {
if (ativa) {
return (
<div {...props}>
<StyledText>
<StyledLabel>{label}</StyledLabel>
<i class='fa fa-fw fa-circle text-success' />
<span>Ativa</span>
</StyledText>
</div>
)
} else {
return (
<div {...props}>
<StyledText>
<StyledLabel>{label}</StyledLabel>
<i class='fa fa-fw fa-circle text-danger' />
<span>Inativa</span>
</StyledText>
</div>
)
}
}
const StyledText = styled.small`
text-transform: none;
`
const StyledLabel = styled.span`
color: #79919D;
`
I want to put that div in the vertical-center of the row it is contained in. Can someone help me?
You can try this way.
<div className=" centeredDiv">
<status></status>
<div>
.centeredDiv{
display:flex;
justify-content:center;
align-items:center;
}

How to handle z-index on React components on a table

I'm having some issues while displaying a modal on a table. For each expense data I render a expenseItem component inside of a tbody. When clicking on this components, it opens a lateral modal and a backdrop on the remaining screen.
Backdrop has a action that whenever is clicked, it closes modal. But, when I click inside of modal, it calls close modal action of backdrop on top of it.
I've tried to set z-index of backdrop to 100 and moda to 200. It didn't work.
return (
<tr className="expense-box" onClick={() => handleOpenModal()}>
<td className="expense-info">{description}</td>
<td>-</td>
<td>-</td>
<td className="expense-info">{date}</td>
<td className="expense-info">{debit || credit}</td>
<td>
<BackDrop isShowing={isModalOpen} closeModal={handleOpenModal} />
<EditExpenseModal isShowing={isModalOpen} closeModal={handleOpenModal} expense={props.expense} />
</td>
</tr>
Backdrop component:
function BackDrop({ isShowing, closeModal }) {
return (
<div onClick={closeModal}>
{isShowing ? <div className="backDrop" /> : null}
<style jsx>
{`
.backDrop {
position: fixed;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
z-index: 100;
top: 0;
left: 0;
}
`}
</style>
</div>
);
}
export default BackDrop;
Modal component:
import { useState, Fragment } from 'react';
import { editExpenses } from '../redux/actions/expenseActions';
import DropdownMenu from '../components/dropdownMenu';
import { connect } from 'react-redux';
import { createPortal } from 'react-dom';
const EditExpenseModal = ({ expense, isShowing, closeModal, editExpenses, savedCategories }) => {
const { id, description, date, credit, debit } = expense;
const [ expenseItem, setExpenseItem ] = useState({
date,
description,
category: '',
subcategory: '',
credit,
debit
});
function handleInputChange(e, targetName) {
const { name, value } = e.target;
(value && !targetName) || (!value && !targetName)
? setExpenseItem({ ...expenseItem, [name]: value })
: setExpenseItem({ ...expenseItem, [targetName]: e.currentTarget.innerText });
}
function onEditExpense(id, expense) {
editExpenses(id, expense);
closeModal();
}
return isShowing
? createPortal(
<Fragment>
<div>
<div className="form">
<form>
<ul>
<li className="form-inputs">
<label>Date</label>
<input
className="field-input"
type="text"
name="date"
defaultValue={date}
onChange={handleInputChange}
/>
</li>
<li className="form-inputs">
<label>Description</label>
<input
className="field-input"
type="text"
name="description"
defaultValue={description}
onChange={handleInputChange}
/>
</li>
<li className="form-inputs">
<label>Category</label>
<DropdownMenu
savedCategories={savedCategories}
handleInputChange={handleInputChange}
type={'category'}
category={expenseItem.category}
/>
</li>
<li className="form-inputs">
<label>Subcategory</label>
<DropdownMenu
savedCategories={savedCategories}
handleInputChange={handleInputChange}
type={'subcategory'}
parentCategory={expenseItem.category}
category={expenseItem.subcategory}
/>
</li>
<li className="form-inputs">
<label>Credit</label>
<input
className="field-input"
type="text"
name="credit"
defaultValue={credit}
onChange={handleInputChange}
/>
</li>
<li className="form-inputs">
<label>Debit</label>
<input
className="field-input"
type="text"
name="debit"
defaultValue={debit}
onChange={handleInputChange}
/>
</li>
</ul>
</form>
<button onClick={() => onEditExpense(id, expenseItem)}>save</button>
<button onClick={() => closeModal()}>close</button>
</div>
<style jsx>{`
.form {
position: fixed;
background: white;
box-shadow: 0px 20px 5px rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
height: 100%;
top: 0;
right: 0;
width: 40%;
z-index: 200;
overflow: scroll;
}
.form-inputs {
display: flex;
flex-direction: column;
list-style-type: none;
padding: 1rem 2rem;
}
.form-inputs label {
margin-left: 8px;
}
.field-input {
margin: 5px;
width: 330px;
height: 40px;
font-size: 15px;
outline: none;
}
`}</style>
</div>
</Fragment>,
document.body
)
: null;
};
const mapDispatchToProps = (dispatch) => ({
editExpenses: (id, expense) => dispatch(editExpenses(id, expense))
});
const mapStateToProps = (state) => state;
export default connect(mapStateToProps, mapDispatchToProps)(EditExpenseModal);
This problem started to happen when I've turned all structure to a table. Before I was using only <div> and it was working.

React tutorial - css not loading

I'm working through a tutorial on React/Spring Boot located here. All was going well, including the initial display of groups on React.
However, once the React piece was refactored to separate the group list into a separate module and a nav bar was added, I get a display without any css rendering.
Here is the code:
App.js
import React, { Component } from 'react';
import './App.css';
import Home from './Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import GroupList from './GroupList';
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route path='/' exact={true} component={Home}/>
<Route path='/groups' exact={true} component={GroupList}/>
</Switch>
</Router>
)
}
}
export default App;
AppNavbar.js
import React, { Component } from 'react';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
export default class AppNavbar extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return <Navbar color="dark" dark expand="md">
<NavbarBrand tag={Link} to="/">Home</NavbarBrand>
<NavbarToggler onClick={this.toggle}/>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink
href="https://twitter.com/oktadev">#oktadev</NavLink>
</NavItem>
<NavItem>
<NavLink href="https://github.com/oktadeveloper/okta-spring-boot-react-crud-example">GitHub</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>;
}
}
Home.js
import './App.css';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';
class Home extends Component {
render() {
return (
<div>
<AppNavbar/>
<Container fluid>
<Button color="link"><Link to="/groups">Manage JUG Tour</Link></Button>
</Container>
</div>
);
}
}
export default Home;
GroupList.js
import React, { Component } from 'react';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
class GroupList extends Component {
constructor(props) {
super(props);
this.state = {groups: [], isLoading: true};
this.remove = this.remove.bind(this);
}
componentDidMount() {
this.setState({isLoading: true});
fetch('api/groups')
.then(response => response.json())
.then(data => this.setState({groups: data, isLoading: false}));
}
async remove(id) {
await fetch(`/api/group/${id}`, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(() => {
let updatedGroups = [...this.state.groups].filter(i => i.id !== id);
this.setState({groups: updatedGroups});
});
}
render() {
const {groups, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
const groupList = groups.map(group => {
const address = `${group.address || ''} ${group.city || ''} ${group.stateOrProvince || ''}`;
return <tr key={group.id}>
<td style={{whiteSpace: 'nowrap'}}>{group.name}</td>
<td>{address}</td>
<td>{group.events.map(event => {
return <div key={event.id}>{new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: '2-digit'
}).format(new Date(event.date))}: {event.title}</div>
})}</td>
<td>
<ButtonGroup>
<Button size="sm" color="primary" tag={Link} to={"/groups/" + group.id}>Edit</Button>
<Button size="sm" color="danger" onClick={() => this.remove(group.id)}>Delete</Button>
</ButtonGroup>
</td>
</tr>
});
return (
<div>
<AppNavbar/>
<Container fluid>
<div className="float-right">
<Button color="success" tag={Link} to="/groups/new">Add Group</Button>
</div>
<h3>My JUG Tour</h3>
<Table className="mt-4">
<thead>
<tr>
<th width="20%">Name</th>
<th width="20%">Location</th>
<th>Events</th>
<th width="10%">Actions</th>
</tr>
</thead>
<tbody>
{groupList}
</tbody>
</Table>
</Container>
</div>
);
}
}
export default GroupList;
App.css
.App {
text-align: center;
}
.container, .container-fluid {
margin-top: 20px;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
#media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
#keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Any help much appreciated.
If I understand correctly, you are not using the classes you define in the App.css file.
To use the styles add the className property to the element where you need the styling.
For example, in your home.js:
<Button className="App-link" color="link"><Link to="/groups">Manage JUG Tour</Link></Button>
As in the example App.js you linked:
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div className="App-intro">
<h2>JUG List</h2>
{groups.map(group =>
<div key={group.id}>
{group.name}
</div>
)}
</div>
</header>
</div>
);
It was my fault, I just needed to add this into index.js, which was right in the instructions:
import 'bootstrap/dist/css/bootstrap.min.css';

Resources