I’m trying to use React’s new Portal function in order to open an AgGridReact component in a pop up window. However, the issue I am running into is when I open the component in a new window, the table functions do not work while the mouse cursor is in the pop up window. However, if I drag my mouse into the original window, the component starts to work, allowing me to change the size of the columns while my mouse is above the original window and not the pop up window.
Specifically, I am creating a new html, then calling
ReactDOM.createPortal(this.props.children, this.containerEl)
(
containerEl
being the created html element). I then attach the new element to the created external window.
I then pass
containerEl
as the
popupParent
to the AgGridReact component.
If anyone would like to see anymore code I can post as required. But any help or pointers would be greatly appreciated.
Where the AgGridReact is rendered
const [openedElement, setOpenedElement] = useState()
const updateOpenedElement = val => {
!openedElement && setOpenedElement(val)
}
<AgGridReact
...
popupParent={openedElement? openedElement: window.document.getElementById("root")}
...
>
</AgGridReact>)
And the code for the NewWindow is as follows
const copyStyles = (sourceDoc, targetDoc) => {...}
class NewWindow extends React.Component {
constructor(props) {
super(props)
this.setOpenedElement = props.setOpenedElement
this.containerEl = document.createElement('div')
this.externalWindow = null
}
render() {
this.setOpenedElement(this.containerEl)
return ReactDOM.createPortal(this.props.children, this.containerEl)
}
componentDidMount() {
this.externalWindow = window.open("", "",
"width=600, height=400, left=200, top=200")
copyStyles(document, this.externalWindow.document)
this.externalWindow.document.body.appendChild(this.containerEl)
}
componentWillUnmount() {
this.externalWindow.close()
}
}
setOpenedElement is called in a parent component, which I am using in order to create the element popupParent will use.
Related
I have a modal with a long form in my react application. So when I submit the form I am showing the validation messages from the server on top of the form. So the user has to scroll to the top to view the messages. So I want to automatically scroll to the top when the message appears. So I added the below code in the submit handler function. But it is not working.
setAddModalErrorMsg([{ msg: res.data.msg, type: "error" }])
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth"
});
The other answers showed how you can scroll the modal to the top, and that is the generally accepted way of achieving this, though, I want to show you how to scroll the "Message" into view, regardless of whether it's on the top or not.
You would also need to create a ref to where you display your message and use the scrollIntoView functionality to scroll the modal to your validation message.
import React, { useRef } from 'react';
const Modal = () => {
const validationMessageRef = useRef();
const setAddModalErrorMsg = () => {
// scrolls the validation message into view, and the block: 'nearest' ensures it scrolls the modal and not the window
validationMessageRef.current?.scrollIntoView({ block:'nearest' });
}
return (
<div>
<div ref={validationMessageRef}>
// your validation message is displayed here
</div>
// rest of your modal content here
</div>
)
}
to automatically scroll to the top we can use the below code :
constructor(props) {
super(props)
this.myRef = React.createRef() // Create a ref object
}
add the scrollTo function after setAddModalErrorMsg.
setAddModalErrorMsg([{ msg: res.data.msg, type: "error" }])
this.myRef.current.scrollTo(0, 0);
<div ref={this.myRef}></div>
attach the ref property to a top dom element
You're trying to scroll window, but chances are your window is already at the top, it's your modal element that needs to scroll up.
To do this, i'd create a reference to the modal element, then in your function scroll the modal element via the ref, so something along the lines of:
import React, {useRef} from 'react';
const Modal = (props) => {
// use the useRef hook to store a reference to the element
const modalRef = useRef();
const setAddModalErrorMsg = () => {
// check the ref exists (it should always exist, it's declared in the JSX below), and call a regular javascript scrollTo function on it
modalRef.current?.scrollTo({x: 0, y: 0, animated: false});
}
// see here we create a reference to the div that needs scrolled
return (
<div ref={modalRef}>
{ // your modal content }
</div>
)
}
I have a parent Component which sends a list of data to a List component which in turn sends it to a Label Component to display each data as a label.
I want to be able to focus on the label element when i click on it so that the appropriate style is applied ..
Below is the gist :-
class ContainerComp extends React.Component {
constructor(props) {
super(props);
this.state = {
group: [1, 2, 3]
};
clickHandler = (name, ref) = > {
// I am able to get the DIV as a html element here but calling .focus() on it dosent change the style where as when i explictly add focus using chrome debugger for the element it works.
ref.focus() // not working
}
render() {
return ( <
ListComp group = {
group
}
onClick = {
clickHandler
} >
)
}
}
function ListComp(props) {
const data = props.group.map(... < label onClick = {} > )
return ( <
Label.. >
)
}
function Label(props) {
let ref = createref();
// on focus style for the component is defined in this component
// i am making use of css modules
return ( <
div ref = {
ref
}
onClick = (name, ref) >
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
How can we achieve such a functionality without having to pass a selected prop to the label component ? By default i would select the first element and keep the focus .. or we can make it configurable.
Usually for this I would use Redux and fire off an action which therefore sets the property of the component that needs change, and make a listener that will listen for that specific prop and change style accordingly.
In this situation, id just pass down the event handler to the child component (remember to not call it when you pass it down, so do:
{() => {eventHandler()}}
and then in the child component do:
onClick={this.props.eventHandler(e)}
You will use the event to identify which element triggered it and then apply the class/style/prop to it.
There was some problem with the Ref , I am not quite sure why but i changed it to use the useRef() hook.
Label Component
const elementRef = useRef(null);
return (
<div className={[externalStyle, styles.container].join(' ')} onClick={() => onClickEvent(itemName, elementRef)} ref = {elementRef} tabIndex={1}> // added tabIndex and also changed to useRef
Container Component
clickHandler = (name: string, ref) => {
ref.current.focus(); // volla it worked
}
I tried using the old form of Ref and also useRef() without null previously (el) => (const = el).
It works if some one has some explanation where i went wrong i will be happy to listen as i am able to wrap my head around. may be a nights sleep helped fix it :P
I have created a Web Component which hosts Wiris. However when the component is rendered the Wiris editor is (very) badly formed:
You can see the issue live here.
The code is as follows:
class WirisComponent extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
// Create a shadow root
var shadow = this.attachShadow( { mode: 'open' } );
// Create a div to host the Wiris editor
var div = document.createElement('div');
div.id = 'editorContainer';
var wirisDefaultConfig = {
'language': 'en'
};
var editor = com.wiris.jsEditor.JsEditor.newInstance(wirisDefaultConfig);
// Insert the Wiris instance into the div
editor.insertInto(div);
// Append it to the shadow route
shadow.appendChild(div);
}
}
// Define the new element
customElements.define('wiris-component', WirisComponent);
and the HTML mark-up is:
<wiris-component></wiris-component>
Note that I've tried this in Chrome which does have full support for web components.
Any idea what the problem is? Is the problem related to the styling issue found in this issue?
Don't use a Shadow DOM: the styles imported with your library are not working with it.
class WirisComponent extends HTMLElement {
connectedCallback() {
var wirisDefaultConfig = {
'language': 'en'
};
var editor = com.wiris.jsEditor.JsEditor.newInstance(wirisDefaultConfig);
editor.insertInto(this);
}
}
// Define the new element
customElements.define('wiris-component', WirisComponent);
<script src="https://www.wiris.net/demo/editor/editor"></script>
<wiris-component></wiris-component>
I have created a simple portal following a tutorial on this site: How to create a React Modal(which is append to `<body>`) with transitions?. The code for the simple portal is:
var Portal = React.createClass({
render: () => null,
portalElement: null,
componentDidMount() {
var p = this.props.portalId && document.getElementById(this.props.portalId);
if (!p) {
var p = document.createElement('div');
p.id = this.props.portalId;
document.body.appendChild(p);
}
this.portalElement = p;
this.componentDidUpdate();
},
componentWillUnmount() {
document.body.removeChild(this.portalElement);
},
componentDidUpdate() {
React.render(<div {...this.props}>{this.props.children}</div>, this.portalElement);
}
});
However rather than rendering the div created above as the parent it renders a div with an undefined id as the parent. I am wondering why this is the case and how I can remove it. Thank you
I think you need to pass portalId as a prop to Portal component. Which is undefined right now.
I am developping a React Application and trying to get the computed style 'scrollHeight' of a DOM Element.
I put this code in the componentDidMount:
componentDidMount() {
// let _this = this;
// window.onload = function(){
let imgFigureDOM = findDOMNode(_this.refs.imgFigure0),
imgW = imgFigureDOM.scrollWidth,
imgH = imgFigureDOM.scrollHeight;
// }
}
But, I can't get the correct value of scrollHeight only in the chrome browser.It seems that the chrome is not enough fast to render the DOMNode completely when the findDOMNode execute.
The value is correct when I use window.onload as above, but Shouldn't the DOMNode was completely loaded when the componentDidMount execute?
Thank you for your patient answer!
componentDidMount() is called when your React component is rendered. React has rendered an <img> tag, that doesn't mean that the image is loaded.
Let's set up some basic definitions to distinguish rendered from loaded:
rendered: React has converted your virtual DOM elements (specified in the render method) into real DOM elements and attached them to the DOM.
loaded: Image data or other remote content has downloaded completely (or failed to download).
So just add the onLoad and onError event handlers to your React <img> tag and away you go.
image-events
Short Example:
import React from 'react';
class ImageWithStatusText extends React.Component {
constructor(props) {
super(props);
this.state = { imageStatus: null };
}
handleImageLoaded(e){
this.setState({ imageStatus: 'loaded' });
console.log(e.target.scrollHeight);
}
handleImageErrored() {
this.setState({ imageStatus: 'failed to load' });
}
render() {
return (
<div>
<img
src={this.props.imageUrl}
onLoad={this.handleImageLoaded.bind(this)}
onError={this.handleImageErrored.bind(this)}
/>
{this.state.imageStatus}
</div>
);
}
}
export default ImageWithStatusText;