html2canvas dynamically rendered div - reactjs

I am trying to use html2canvas (http://html2canvas.hertzen.com) to take a screenshot of my page that contains a google maps view, but I am running into an issue where the resulting screenshot is white, except for the marker. What (possibly) complicates the issue is that I am (trying to) creating a React application.
I use this code to take the screenshot:
takeScreenshot = () => {
let googleMapsView = document.querySelector('.google-map');
html2canvas(googleMapsView).then((canvas) => {
let imgData = canvas.toDataURL('image/png');
console.log(imgData);
});
}
This is the page I'm trying to take a screenshot of:
The code above generates the following image:
As you can see, it generates the screenshot and captures the marker and the bottom "Map Data ©2018 Google" however, the resulting screenshot is still blank.
In the documentation on their Github, I see an option to preload here: https://github.com/niklasvh/html2canvas/wiki/Documentation#preload-content
However, this seems to have been removed from the library since the 0.5.0 beta version - they're now in 1.0.0 alpha, so going back that far to such an outdated library is not really an option.
How can I tell html2canvas to wait for the GMaps component to be rendered, before taking the screenshot?

You need to add param useCORS to use it with google maps, in your case:
takeScreenshot = () => {
let googleMapsView = document.querySelector('.google-map');
html2canvas(googleMapsView, {useCORS: true}).then((canvas) => {
let imgData = canvas.toDataURL('image/png');
console.log(imgData);
});
}

Have you tried to put your code inside the google map listener
google.maps.event.addListenerOnce(map, 'idle', function(){
// do something only the first time the map is loaded
takeScreenshot = () => {
let googleMapsView = document.querySelector('.google-map');
html2canvas(googleMapsView).then((canvas) => {
let imgData = canvas.toDataURL('image/png');
console.log(imgData);
});
}
});
See also the events section in the Google Maps Reference.

Related

html2canvas failing to capture Google static map

I'm creating a booking reference for the user to download after successfully making a booking. It looks like this in the browser:
However, after using html2canvas to get a screenshot of the component in React, I get this:
I suspect the issue has to do with the fact that the static map's use is protected by an API key, causing issues to render the image in an external view.
This is the callback that fires on hitting the share button, which for now just downloads the png (which is then converted to pdf using jsPDF):
const share = () => {
const input = document.getElementById("trip-summary");
if (input === null) return;
html2canvas(input!).then((canvas) => {
var image = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
const pdf = new jsPDF();
// #ts-ignore
pdf.addImage(image, "PNG", 0, 0);
pdf.save("download.pdf");
});
};
It looks like an external source of content. You should set the allowTaint option to true.
html2canvas(input, { allowTaint: true }).then(...);
Just wanted to add to the answer above.
It achieves exactly what I want, but you just need to set useCORS to true in the options as well
html2canvas(input!, { allowTaint: true, useCORS: true }).then(...)
, otherwise you'll get
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

UserEvent doesn't wait for dialog to be loaded when it is lazy load

After updating testinglibrary/userEvent from version 13 to 14, it is not waiting for dynamically rendered.
My dialog is lazily loaded as well as the content inside the dialog.
An example code is below.
it('updates the channel information after edit channel request succeeds', async () => {
render();
await userEvent.click(await screen.findByTestId('TestId'));
const myDialog = await screen.findByRole('dialog');
// This is problematic.
const nameField = within(myDialog).getByLabelText(/name/i);
})
Dialog shows spinner until it finishes fully loading the content.
And the content will be rendered as long as query waits. But it doesn't wait for the content to be rendered but quits waiting as soon as it finds the spinner, saying it couldn't find the content but only spinner.
What I tried
Using find query instead of get, some tests are resolved only doing this but others aren't.
Using screen instead of within(dialog).findBy. This resolves some breaking test as well.
I looked over the document and changelog if there were effective change that possibly breaks the test code, but had no luck :(
What should I do with it?
This might be because you haven't ran setup yet. userEvent's API have changed in 14, and now, per the documentation:
We recommend invoking userEvent.setup() before the component is rendered.
So in your case, you need to try something like this.
it('updates the channel information after edit channel request succeeds', async () => {
const user = userEvent.setup()
render();
await user.click(await screen.findByTestId('TestId'));
const myDialog = await screen.findByRole('dialog');
// This is problematic.
const nameField = within(myDialog).getByLabelText(/name/i);
})

Async Clipboard API "ClipboardItem is not defined" - Reactjs copy image to Clipboard

I'm working on React js, I created my app with create-react-app using npm. I was trying to build a button that takes an image and writes it to the clipboard. Fourtunately I found this npm library that seems to work fine! But keeps me thinking why I couldn't use the ¿built-in? Asynchronous Clipboard API to copy the image (the text copy works fine). I read a really enlightening guide here, and kept reading other great guide here, so I tried all the codes suggested, there and in other pages (despite they don't seem to really change the functionality, I got to try). I came with the same error in every try that impedes to compile: "'ClipboardItem' is not defined no-undef". One code for example was this one:
const response = await fetch('valid img url of a png image');
const blob = await response.blob();
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob})]);
It seems to be simple, easy to follow. The problem is when you need to put the data in a form the Clipboard can read it, make it a blob, because I need the ClipboardItem constructor, and my app seems to be unable to recognize it as such. Keeps returning ClipboardItem is not defined or, if I somehow define it, says it's not a constructor, of course. I tried with other constructors like Blob(), but had the same problem. The last thing kept me thinking that, since I'm new in the programming world, if there is something kinda basic I don't know of the interaction of Web Apis like this one with node or Reactjs, and if there is a solution, of course! Thanks in advance, you guys are great!
Edit: adding the whole component code as requested:
import React from "react";
function TestingClipAPI () {
async function handleScreenshot () {
const response = await fetch('https://i.postimg.cc/d0hR8HfP/telefono.png');
const blob = await response.blob();
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob})]);
};
return (
<div>
<button onClick={handleScreenshot} id="buttonID">test</button>
</div>
)
};
export default TestingClipAPI;
Possible issue: This might be because of CRA (Create-React-App) config - similar issue. Something like the library linked can be done, create a canvas and copy the image from there.
Solution or a way to make it work anyway: make a call this way before using ClipboardItem:
const { ClipboardItem } = window;
Note: this also works with other constructors like toBlob and HTMLCanvasElement that had the same issue.
Things to look for:
Browser support Clipboard
Secure origin on HTTPS or localhost. See this post.
How the function is being called - in the OP's case - onClick & asynchronous.
The issue is that onClick are not asynchronous by default and you are not awaiting the response and you also have a typo in navigator.clipboard.
const handleScreenshot = async () => {
try {
const response = await fetch(
"https://i.postimg.cc/d0hR8HfP/telefono.png"
);
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({ "image/png": blob }),
]);
} catch (err) {
console.error(err);
}
}
return (
<button onClick={async () => await handleScreenshot()} id="buttonID">
test
</button>
);
There are tradeoff between inline function and below are alternatives. I'd personally use the latter method.
function handleScreenshot() {
async function screenShot() {
try {
const response = await fetch(
"https://i.postimg.cc/d0hR8HfP/telefono.png"
);
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({ "image/png": blob }),
]);
} catch (err) {
console.error(err);
}
}
screenShot();
}
return (
<button onClick={handleScreenshot} id="buttonID">
test
</button>
);
Lastly, you can return a chained promise.
Simply add window in front of ClipboardItem like the following
window.ClipboardItem(...)
Unfortunately, as of the time of this answer, ClipboardItem isn't supported in Firefox. (Support can be enabled via an about:config setting; but of course, most Internet users will not have done this.)
Source: https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem#browser_compatibility

Get Stream IO React Native UnFollowing option not available

I am using Get Stream Io react native in my project https://github.com/GetStream/react-native-activity-feed
I see there is a functionality in the library of following a particular user, whereas the opposite is not available i.e. how to unfollow a user.
Please point me in the right direction on how to achieve this in react native
Our library doesn't include the logic for following/unfollowing. It has a button that allows you to set it up yourself though. Which is the FollowButton you're talking about. You could do something like this:
<FollowButton
followed={async () => {
// check if you're following the user
const following = await your_timeline_feed.following({filter: ['user:user_42'] })
if (following) {
return true;
}
return false;
}} // renders the button as "following"
clicked={async () => {
// your logic for following/unfollowing
await your_timeline_feed.follow('user', 'user_42');
}}
/>
Read more here:
https://getstream.github.io/react-native-activity-feed/#!/FollowButton
https://getstream.io/docs/following/?language=js

Uploading an image to Azure Blob Storage using React

I want to upload an image to Azure Blob Storage using React.
I've tried a lot of examples and none of them work.
The one that seemed the best was this one but still didn't manage to get it working on React.
What I'm trying right now is to use the createContainerIfNotExists method just to test and the error is Cannot read property createBlobServiceWithSas of undefined
My code is the following:
import AzureStorage from 'azure-storage';
const account = {
name: 'x',
sas: 'x',
};
const blobUri = `https://${account.name}.blob.core.windows.net`;
const blobService = AzureStorage.Blob.createBlobServiceWithSas(blobUri, account.sas);
export const createContainer = () => {
blobService.createContainerIfNotExists('test', (error, container) => {
if (error) {
// Handle create container error
} else {
console.log(container.name);
}
});
};
export default createContainer;
According to my research, because you develop A React application, we can not use the createBlockBlobFromBrowserFile method. We just can use the method in the browser. For more details, please refer to the document.
According to the situation, I suggest you use the other method(such as uploadStreamToBlockBlob) to upload image with V10 sdk. For more details, please refer to https://learn.microsoft.com/en-us/javascript/api/#azure/storage-blob/?view=azure-node-latest

Resources