I'm having some issues with the basic rendering tests from create-react-app and the Polyline component from react-leaflet. Whenever I try to add the Polyline as a child to my Map, I get the test error message: TypeError: Cannot read property '_layerAdd' of null. The map and polyline both still show in the browser just fine, but this test error is getting a little annoying.
Has anyone faced the same issue? If so, how did you resolve it?
I've seen a few similar questions asked, but no real answer yet for when using react-leaflet. I've tried tying the map rendering to componentDidMount, but without luck.
Here's my render for my map:
render() {
return (
<Map
animate={this.state.animate}
center={this.state.latlng}
length={4}
onClick={this.handleClick}
zoom={13}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.se/hydda/full/{z}/{x}/{y}.png"
/>
<Marker position={pathPoints[0]} />
<Marker position={pathPoints[pathPoints.length - 1]} />
<Polyline color="red" positions={pathPoints} />
</Map>
);
}
}
Thanks!
In fact, it has nothing to do with react-leaflet library. Leaflet Polyline uses SVG renderer (by default) but JSDOM which comes with Jest test runner for Create React App, does not support SVG to a full extent (in particular createSVGRect is not supported). That's basically the reason why the specified error occurs.
How to configure Create React App to pass tests?
It rather needs to be considered as a workaround but the idea is to extend JSDOM SVGSVGElement by introducing createSVGRect as an empty function. This technique will allow to emulate SVG support in JSDOM in order to pass Jest tests for vector overlays such as Polygon
Under src directory create a file named setupTests.js and provide the following code:
var createElementNSOrig = global.document.createElementNS
global.document.createElementNS = function(namespaceURI, qualifiedName) {
if (namespaceURI==='http://www.w3.org/2000/svg' && qualifiedName==='svg'){
var element = createElementNSOrig.apply(this,arguments)
element.createSVGRect = function(){};
return element;
}
return createElementNSOrig.apply(this,arguments)
}
Related
I have a SVG which includes text elements. I can get the text of the elements in my cypress test, but when I try to call getbbox() I get the error 'TypeError: text.getBBox is not a function'. If I understand correctly cypress is returning text as a htmlELement, and I need an SVGElement to call getbbox() on, but whats best practice to do that in the context of a cypress test?
cy.get('svg#osmdSvgPage1').get("text").each(textElement => {
var text = textElement.text(); //successful
//do some stuff
var bbox = textElement.getbbox(); //throws 'TypeError: text.getBBox is not a function'
//do more stuff
});
WRT to getBBox(), it must be called on the native SVGGraphicsElement, but you have called it on the jQuery object passed into .each().
Just add an index like this to unwrap the jQuery object:
var bbox = textElement[0].getBBox()
WRT cy.get('svg#osmdSvgPage1').get("text") I think you want this instead
cy.get('svg#osmdSvgPage1').find("text")
assuming the text elements are within the svg.
If there's only one svg, you can just use
cy.get("text")
but if there are several svg on the page cy.get('svg#osmdSvgPage1').get("text") will return all the text from all svg, because .get("text") scans the whole page by default.
I have a Drawer Component anchored at the bottom but I would still like to interact with the page above the drawer but either I can click out of if but the drawer closes so I tried the variants persistent and permanent both didn't work they actually made it so nothing at all happens when I click out of if. I think it has something to do with the spacing or padding above, but if anyone knows how to disable that, it would be greatly appreciated.
I solved it slightly differently, by removing the "inset" CSS property of the .MuiDrawer-modal div:
.MuiDrawer-modal {
inset: unset !important;
}
Figured out my problem, I ended us having to do some height changes to the Paper component and it seemed to work the way I wanted. You can overrided the css with makeStyles method in the #material-ui/core/styles directory. I used the classes property example
// Outside the component
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
drawer: {
css here ...
}
})
// Inside Component
const classes = useStyles() // the make styles returns a function and calling the useStyles returns an object with the css.
// Inside return
<Drawer
anchor="bottom"
classes={{ paper: classes.drawer }}
>
Content...
</Drawer>
I have an SVG loading like this:
<object id="svg-object" type="image/svg+xml" width="1400px" height="900px" data="media/1.svg?"></object>
I then have a function that works calling out one element in this svg and apply a style to it just fine. Here is the onload event that is working for getting me the element properly:
window.onload=function() {
var svgObject = document.getElementById('svg-object').contentDocument;
var element = svgObject.getElementById('sprite1');
};
But how do I set a .hover even in for this same element? I've tried:
$('#${element}').hover(function(e) { }
But no luck.
Also, how can I apply the svgObject variable to a whole class like path or polygon? I use this on a local inline SVG and it works fine:
$("polygon, path").hover(function(e) { }
I would like this to work on the object embedded in the svg also.
Sorry, I am not able to put an external svg in snippet (or at least I don't know how) as external URL will not load in an object. And it needs to load as an object for you to see the issue.
Any help?
Also, here is code that works defining element color from script but mouseover not working either. (tried instead of hover)
window.onload=function() {
var svgObject = document.getElementById('svgEmb').contentDocument;
var element = svgObject.getElementById('left');
element.style.fill = "blue";
element.style.stroke ="blue";
};
element.addEventListener("mouseover", function() {
element.style.fill = "red";
element.style.stroke ="red";
});
I am embedding an SVG object in HTML as follows:
<object id='mapObject' type="image/svg+xml" data="assets/maps/drawing.svg">
</object>
Then, I want to reach it from my typescript classes (from Ionic 2, but that should be indifferent):
let map = document.getElementById("mapObject");
let svgDoc = map.contentDocument; // Get the inner SVG DOM
This works on browser, but typescript complains about it:
typescript Property 'contentDocument' does not exist on type 'HTMLElement'
And worse, it doesn't work in the device, because the compiler refuses to generate code for it.
My temporary hack/solution is casting as follows:
let svgDoc = (<HTMLIFrameElement>map).contentDocument; // Get the inner SVG DOM
This works in the device, because HTMLIFrameElement has the contentDocument property, but it seems to me that typescript should have an specific type for this. However I've been unable to find it.
Anyone?
You can cast/assert it to HTMLObjectElement which has the contentDocument property:
let map = document.getElementById("mapObject") as HTMLObjectElement;
let svgDoc = map.contentDocument; // should be fine
I have no problem using SVGweb when page is simply loaded (opened).
How is it possible to reinitialize SVGweb in order to redraw all SVG on the page?
Anotherwords I need SVGweb to rescan and rerender everything on the page.
source (from this):
<script type="image/svg+xml">
<svg>
...
</svg>
</script>
to this (like SVGweb si doing that when simply open the page):
<svg>
...
</svg>
I need this because I change the SVG graphics using ajax and need to rerender it on the page.
I needed the same capability and figured out how to do this properly without modifying the svgweb source or calling the _onDOMContentLoaded() handler manually. In fact, it is supported natively.
The trick is to (re)attach your SVG elements to the DOM using window.svgweb.appendChild() which causes the node to be processed by svgweb, as is documented within the svgweb manual.
Example, using jQuery:
// Iterate over all script elements whose type attribute has a value of "image/svg+xml".
jQuery('body').find('script[type="image/svg+xml"]').each(function () {
// Wrap "this" (script DOM node) in a jQuery object.
var $this = jQuery(this);
// Now we use svgweb's appendChild method. The first argument is our new SVG element
// we create with jQuery from the inner text of the script element. The second
// argument is the parent node we are attaching to -- in this case we want to attach
// to the script element's parent, making it a sibling.
window.svgweb.appendChild(jQuery($this.text())[0], $this.parent()[0]);
// Now we can remove the script element from the DOM and destroy it.
$this.remove();
});
For this to work properly I suggest wrapping all SVG script tags with a dedicated div, so that when attaching the SVG element it is attached to a parent element containing no other nodes. This removes the possibility of inadvertently reordering nodes during the process.
After the DOM is changed with a new SVGweb code (through Ajax)
<script type="image/svg+xml">
<svg>
...
</svg>
</script>
need to execute this:
svgweb._onDOMContentLoaded();
But before need to comment a line in the core source of SVGweb svg-uncompressed.js or svg.js
svg-uncompressed.js
from
if (arguments.callee.done) {
return;
}
to
if (arguments.callee.done) {
//return;
}
svg.js: find and delete this:
arguments.callee.done=true;
or replace with
arguments.callee.done=false;
EDIT:
One more fix to work for IE9:
for svg.js
from
var a=document.getElementById("__ie__svg__onload");if(a){a.parentNode.removeChild(a);a.onreadystatechange=null}
to
var IEv=parseFloat(navigator.appVersion.split("MSIE")[1]);if(IEv<9){var a=document.getElementById("__ie__svg__onload");if(a){a.parentNode.removeChild(a);a.onreadystatechange=null;a=null;}}
for svg-uncompressed.js
from
// cleanup onDOMContentLoaded handler to prevent memory leaks on IE
var listener = document.getElementById('__ie__svg__onload');
if (listener) {
listener.parentNode.removeChild(listener);
listener.onreadystatechange = null;
listener = null;
}
to
// cleanup onDOMContentLoaded handler to prevent memory leaks on IE
var IEv=parseFloat(navigator.appVersion.split("MSIE")[1]);
if (IEv<9) {
var listener = document.getElementById('__ie__svg__onload');
if (listener) {
listener.parentNode.removeChild(listener);
listener.onreadystatechange = null;
listener = null;
}
}