<select> inside react-popper inside react-bootstrap modal not working - modal-dialog

I use a DatePicker with a select to switch the year and month. I use react-popper to show the DatePicker. But when I use the DatePicker in a react-bootstrap Modal the options of the select will popup for a second when I click it but then disappear again. I created a sandbox here to highlight the problem
import React, {useState} from 'react';
import ReactDOM from 'react-dom';
import FocusTrap from 'focus-trap-react';
import { usePopper } from 'react-popper';
export default function Tooltip() {
const [referenceElement, setReferenceElement] = useState(null);
const [popperElement, setPopperElement] = useState(null);
const [showPicker, setShowPicker] = useState(false);
const popper = usePopper(referenceElement, popperElement, {
placement: 'bottom-start'
});
const openPopper = () => {
setShowPicker(true);
};
const closePopper = () => {
setShowPicker(false);
};
return <div className='datepicker-container'>
<a ref={setReferenceElement} className="btn btn-dark" onClick={openPopper}>Click to open select in Popper</a>
{showPicker && ReactDOM.createPortal(
<FocusTrap
active
focusTrapOptions={{
initialFocus: false,
allowOutsideClick: true,
clickOutsideDeactivates: true,
onDeactivate: closePopper
}}>
<div
tabIndex={-1}
style={popper.styles.popper}
className="dialog-sheet"
{...popper.attributes.popper}
ref={setPopperElement}
role="dialog">
<div>
<select>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
</div>
</FocusTrap>,
document.querySelector('#popup-container')
)}
</div>
}
The CSS looks like this:
.datepicker-container {
position: relative;
z-index: 100;
}
.dialog-sheet {
z-index: 2003;
background-color: #EEE;
}
Has anyone an idea how could I fix this. Is there something I'm missing in the css to fix this? Or has it maybe to do with the focus trap?

The problem was the portal I was using. Without ReactDOM.createPortal and just rendering it in place, works fine.

Related

Antd Modal+Carousel+Form shudders for 10ms

During opening antd modal I can see for 10ms that my modal is increased and displays not the first page of the carousel inside.
Here I have attached a Json file, which you can play in your Chrome DevTools (F12 -> Performance -> Load profile). Described problem appears on 1150ms. Here is the link to the codesanbox where you can play with my code. I found that the modal increases because my form layout equal to "vertical", but I still didn't get why the last page of my carousel appears first. I am thinking of some hooks in React like useLayoutEffect to render it correctly, however it seems to my as a antd bug, or my mistake somewhere and I don't want to overload my code with useless hooks.
Here is the code for that who won't open codesanbox:
import "./styles.css";
import React, { useState, useRef } from "react";
import { Modal, Carousel, Form, Button, Input, Image } from "antd";
export default function App() {
const [modal, setModal] = useState<boolean>(false);
const [pageOneForm] = Form.useForm();
const [pageTwoForm] = Form.useForm();
return (
<>
<Button onClick={() => setModal(true)}> open </Button>
<Modal
width={500}
centered
closable={false}
title={"Title of my modal"}
open={modal}
onCancel={() => setModal(false)}
okButtonProps={{ style: { display: "none" } }}
>
<Carousel dotPosition="top" swipeToSlide draggable arrows>
<div>
<img alt="card1" src="card1.png" className="card" />
<Form
form={pageOneForm}
layout="vertical" //it makes my modal huge for few ms
>
<Form.Item
label="Some input for 1 page"
name="carteVitale"
rules={[{ required: true, message: "" }]}
>
<Input />
</Form.Item>
</Form>
</div>
<div>
<img alt="card2" src="card2.png" className="card" />
<Form form={pageTwoForm} layout="vertical">
<Form.Item
label="Some input for 2 page"
rules={[{ required: true, message: "" }]}
>
<Input />
</Form.Item>
</Form>
</div>
</Carousel>
</Modal>
</>
);
}
I expect to render this modal without unplanned modal shakes
UPDATE:
Size of modal can be fixed by adding two keys to Carousel component:
<Carousel
adaptiveHeight={false}
lazyLoad="progressive"
...
or by playing with css:
<Carousel
initialSlide={0}
lazyLoad="ondemand"
className="cards-container"
...
<Form.Item
className="no-wrap"
...
and css will looks like:
.cards-container {
animation: fadein 0.5s ease;
position: relative;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.no-wrap label {
white-space: nowrap;
overflow: hidden;
}

How to make Post request with Axios (MERN Stack)

I am new to REACT and the MERN Stack and try to understand everything. But sometimes, it seems that the simplest things do not want to get into my head.
I hope that, one day, I will understand it all. Until then it still seems a long way away. Anyway. I am starting with a simple MERN app. All users should be displayed on the start page. On a separate "page" there should be a create users form. For now, the users are displayed on the home screen but when I switch back from the "create users page" they dissappeared. Furthermore the input form do not work (validation error). When checking get and posts requests from my backend with Thunder Client everthing works, so I suppose, this might be something to do with the frontend. Sorry for my wording. I am a programming newbie.
I hope it is somewhat understandable. What am I doing wrong? I would be so happy, if anyone could help. Thank you!
client/src/App.js
import React from "react";
import { Routes, Route } from "react-router-dom";
import AllUsers from "./pages/AllUsers";
import Navigation from "./components/Navigation";
import CreateUser from "./pages/CreateUser";
import "./App.css";
function App() {
return (
<div className="App">
<Navigation />
<Routes>
<Route path="/" element={<AllUsers />} />
<Route path="create-User" element={<CreateUser />} />
</Routes>
</div>
);
}
export default App;
client/src/components/Navigation.js
import React from "react";
import { Link } from "react-router-dom";
const Navigation = () => {
return (
<header className="bg-background border-t-0 shadow-none">
<nav className="bg-navigation bg-opacity-40 rounded-t-xs flex justify-around h-12 p-3 ">
<Link to="/create-user">Create User</Link>
<Link to="/">
<img id="workshop-icon" src="../assets/home.svg" alt="home button" />
</Link>
</nav>
</header>
);
};
export default Navigation;
client/src/AllUsers.js
import React from "react";
import { useState, useEffect } from "react";
import Axios from "axios";
const AllUsers = () => {
const [listOfUsers, setListOfUsers] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/getUsers").then((response) => {
setListOfUsers(response.data);
});
}, []);
return (
<div className="App">
<div className="usersDisplay">
{listOfUsers.map((user) => {
return (
<div key={user._id}>
<h1>Name: {user.name}</h1>
<h1>Age: {user.age}</h1>
<h1>Username: {user.username}</h1>
</div>
);
})}
</div>
</div>
);
};
export default AllUsers;
client/src/createUser.js
import React from "react";
import { useState } from "react";
import Axios from "axios";
const CreateUser = () => {
const [listOfUsers, setListOfUsers] = useState([]);
const [name, setName] = useState("");
const [age, setAge] = useState(0);
const [username, setUsername] = useState("");
Axios.post("http://localhost:3001/createUser", {
name,
age,
username,
}).then((response) => {
setListOfUsers([
...listOfUsers,
{
name,
age,
username,
},
]);
});
return (
<div className="input">
<div>
<input
type="text"
placeholder="Name..."
onChange={(event) => {
setName(event.target.value);
}}
/>
<input
type="number"
placeholder="Age..."
onChange={(event) => {
setAge(event.target.value);
}}
/>
<input
type="text"
placeholder="Username..."
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<button onClick={CreateUser}> Create User </button>
</div>
</div>
);
};
export default CreateUser;

I cannot querySelect input of ion-searchbar in Jasmine tests

I'm running a simple test - enter a value into an Ionic search bar.
I cannot get a reference to the search bar input field in my jasmine tests.
I've set up a simple vanilla Ionic 4 app with a single test page.
The following tests all fail.
const el: HTMLElement = fixture.nativeElement;
const input = el.querySelector('input');
expect(input).not.toBeNull(); // FAILS
const bar = el.querySelector('ion-searchbar');
expect(bar.innerHTML).not.toEqual(''); // FAILS
expect(bar.shadowRoot).not.toBeNull(); // FAILS
I don't understand why?
I've written other tests successfully on Ionic lists to read values.
I tried putting in timers to give the component time to render the html.
If I put a simple HTML input field on the page it is picked up OK.
I thought the shadow DOM might be relevant here even though when I inspect the input field in the browser it doesn't seem to be in the shadow DOM, and the standard document.querySelector works just fine.
HTML Page
// test.page.html
<ion-header>
<ion-toolbar>
<ion-title>
Test Page
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-searchbar></ion-searchbar>
</ion-content>
Component Class
// test.page.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-test',
templateUrl: './test.page.html',
styleUrls: ['./test.page.scss'],
})
export class TestPage {
constructor() { }
}
Test Spec
// test.page.spec.ts
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { TestPage } from './test.page';
describe('TestPage', () => {
let component: TestPage;
let fixture: ComponentFixture<TestPage>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestPage ],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestPage);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should have a searchbar', () => {
const el: HTMLElement = fixture.nativeElement;
const bar = el.querySelector('ion-searchbar');
fixture.detectChanges();
expect(bar).not.toBeNull(); // PASSES
// adding some checks to see what is going on
expect(bar.children.length).toBeGreaterThan(0); // FAILS
expect(bar.innerHTML).not.toEqual(''); // FAILS
expect(bar.shadowRoot).not.toBeNull(); // FAILS
});
it('should have an input field', () => {
const el: HTMLElement = fixture.nativeElement;
const input = el.querySelector('input');
expect(input).not.toBeNull(); // FAILS
});
});
This is over a year late, but I think it's worth answering for others who might need it. For what it's worth, I'm running #ionic-native/core 4.15 with #angular/core 5.2.11 and ionic-angular 3.9.2.
I don't know why const input = el.querySelector('input'); isn't working for you. The following code works for me:
const toolbar = fixture.nativeElement.querySelector('ion-toolbar')
const searchBar = toolbar.querySelector('ion-searchbar');
const input = searchBar.querySelector('input');
Alternatively, you can make your query for the input more specific:
const input = searchBar.querySelector('div.searchbar-input-container > input.searchbar-input')
I like to look at the structure of HTMLElements and their component instances by console.loging them in the test. When I add this to my test:
console.log(searchBar);
The console in the captured test browser will output:
<ion-searchbar class="searchbar searchbar-md searchbar-left-aligned" ng-reflect-placeholder="Besmirch">
<div class="searchbar-input-container">
<button class="searchbar-md-cancel button button-md button-clear button-clear-md button-clear-md-dark" clear="" color="dark" ion-button="" mode="md" type="button" ng-reflect-color="dark" ng-reflect-mode="md" ng-reflect-clear=""><span class="button-inner"><ion-icon name="md-arrow-back" role="img" class="icon icon-md" aria-label="arrow back" ng-reflect-name="md-arrow-back"></ion-icon></span>
<div class="button-effect"></div>
</button>
<div class="searchbar-search-icon"></div>
<input class="searchbar-input" dir="auto" placeholder="Besmirch" type="search" autocomplete="off" autocorrect="off" spellcheck="false">
<button class="searchbar-clear-icon button button-md button-clear button-clear-md" clear="" ion-button="" type="button" ng-reflect-mode="md" ng-reflect-clear=""><span class="button-inner"></span>
<div class="button-effect"></div>
</button>
</div>
<button class="searchbar-ios-cancel button button-ios button-clear button-clear-ios" clear="" ion-button="" mode="ios" type="button" ng-reflect-mode="ios" ng-reflect-clear="" tabindex="-1"><span class="button-inner">Cancel</span>
<div class="button-effect"></div>
</button>
</ion-searchbar>
In this structure, we can see the input is in the .searchbar-input-container div.

how to use material-ui Dialog PaperProps

I'm using v1.1.0 of material-ui in React 16.3.2. I'm trying to create a landing page similar to Showcase - Local Insights
where the dialog has opacity (Find foreclosures). I'm trying to use PaperProps for Dialog component described here Dialog doc
Here's a component I've created to try to do this.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogTitle from '#material-ui/core/DialogTitle';
import ForwardIcon from '#material-ui/icons/Forward';
import Input from '#material-ui/core/Input';
import FormControl from '#material-ui/core/FormControl';
import Slide from '#material-ui/core/Slide';
const styles = theme => ({
dialogPaper: {
opacity: 0.5,
border: '#FF0000 1px solid',
},
button: {
margin: '30px'
}
});
function Transition(props) {
return <Slide direction="up" {...props} />;
}
class SignInDialog extends React.Component {
state = {
open: false,
username: ''
};
handleClickOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
render() {
const { classes } = this.props;
return (
<div>
<Button variant="fab" color="primary" aria-label="add" className={classes.button} onClick={this.handleClickOpen}>
<ForwardIcon />
</Button>
<Dialog
PaperProps={styles.dialogPaper}
open={this.state.open}
TransitionComponent={Transition}
onClose={this.handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">WELCOME</DialogTitle>
<DialogContent>
<p>SIGN IN</p>
<FormControl className={classes.formControl}>
<Input
value={this.state.searchString}
onChange={this.handleChange('search')}
id="siginin-input"
placeholder="Enter your username"
/>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleClose} color="primary">
Continue
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
SignInDialog.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignInDialog);
I haven't been able to figure out how to get the Dialog to take the styles. What is needed to get PaperProps to work?
If you want to use PaperProps you have to specify the props of the Paperfor which you are applying style.
<Dialog
PaperProps={{ classes: {root: classes.dialogPaper } }}
/>
You can also use classes property and override the style
<Dialog
classes={{paper:classes.dialogPaper}}
/>
The correct way to overide paper props is by using classNames
<Dialog
PaperProps={{ className: classNames(classes.dialogPaper) }}/>
<Dialog
PaperProps={{ classes: {root: classes.dialogPaper } }}
/>

How to render Map in nested div View

I'm trying to work out how to render a map inside a nested div. I have my component Home.js:
import React, {Component} from "react";
import {Map, TileLayer} from "react-leaflet"
class Home extends Component {
constructor( )
{
super();
this.state = {
latlng: {
lat: 51.5074,
lng: 0.1277,
},
}
}
render()
{
return (
<div id="main-wrap">
<div id="sidebar">
...
</div>
<div id="content-wrap" style={{height:'700px'}}>
<Map
center={this.state.latlng}
length={4}
zoom={13}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</Map>
</div>
</div>
);
}
}
export default Home;
But that doesn't render anything although I can see the component in the page if I use the React developer tools.
If I just have one div containing the map then it works fine. e.g.
import React, {Component} from "react";
import {Map, TileLayer} from "react-leaflet"
class Home extends Component {
constructor()
{
super();
this.state = {
latlng: {
lat: 51.5074,
lng: 0.1277,
},
}
}
render()
{
return (
<div id="main-wrap">
<Map
center={this.state.latlng}
length={4}
zoom={13}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</Map>
</div>
);
}
}
export default Home;
But I'd quite like to include other stuff on the page as well, hence the multiple divs.
Thanks in advance if you can help.
Mark
The problem was that I hadn't set this CSS:
.leaflet-container {
height: 100%;
}
All works now!