I'm trying to load the Jira helpdesk widget after user makes an action by loading the script Jira provides dynamically in code like this
let script = document.createElement('script');
script.setAttribute('data-jsd-embedded', '');
script.setAttribute('data-key', 'key value');
script.setAttribute('data-base-url', 'https://jsd-widget.atlassian.com');
script.setAttribute('src', 'https://jsd-widget.atlassian.com/assets/embed.js');
document.head.appendChild(script);
However while it shows in Sources tab in Chrome that it loaded the script, the associated iframe that contains the widget doesn't get created as it should happen normally, and I don't see any error messages in the console.
When I just use a script tag in the HTML file like this
<head>
...
<script data-jsd-embedded data-key="key value" data-base-url="https://jsd-widget.atlassian.com" src="https://jsd-widget.atlassian.com/assets/embed.js"></script>
</head>
Everything works perfectly and there are more additional resources/scripts fetched besides embed.js.
Why doesn't it work the same with creating a script tag programmatically in JS code? How do I make it work?
As far as I can tell the end result is the same - you get the same script tag.
EDIT I seem to have located the non-minified version of the embed.js and will include below
import MutationObserverHandler from "embeddable/widget/mutation-observer-handler";
import ReactDevTools from "embeddable/widget/util/react-dev-tools";
import * as Sentry from "@sentry/browser";
const jsdWidgetID = "jsd-widget";
const jsdWidgetName = "JSD widget";
class Iframe {
el: HTMLElement;
document: Document;
constructor(iframeScriptSrc: string, embeddableData: {}) {
this.createAndInsertIframe(document.body);
this.applyCss();
this.writeDocument(iframeScriptSrc, embeddableData);
new MutationObserverHandler(this.document, this.css.bind(this));
}
createAndInsertIframe(parent: HTMLElement) {
const iframe = document.createElement("iframe");
iframe.scrolling = "no";
iframe.name = jsdWidgetName;
iframe.id = jsdWidgetID;
parent.appendChild(iframe);
this.el = iframe;
const contentWindow = iframe.contentWindow;
// check for null, which according to typescript dom lib >=2.9 can happen (wasn't the case with 2.6)
if (!contentWindow) {
Sentry.captureMessage("iframe.contentWindow is null", Sentry.Severity.Error);
throw new Error("Can't setup jsd-widget iframe");
}
this.document = contentWindow.document;
}
applyCss() {
this.css("height", "1px");
this.css("width", "1px");
this.css("position", "fixed");
this.css("border", "0");
this.css("bottom", "0");
this.css("right", "0");
this.css("z-index", "9999999999999");
}
writeDocument(iframeScriptSrc: string, embeddableData: EmbeddableData) {
const scriptTag = `<script type="application/javascript" src="${iframeScriptSrc}"></script>`;
const embeddableDataJson = JSON.stringify(embeddableData);
const baseURL = JSON.parse(embeddableDataJson).baseUrl; // added baseURl because lazy loading was not working
this.document.open();
this.document.write(
`<html>
<head><base href='${baseURL}' /></head>
<body data-embeddable=${embeddableDataJson}>
<div id="react-root"></div>
${embeddableData.reactDevtools !== undefined ? ReactDevTools.scriptTag : ""}
${scriptTag}
</body>
</html>`
);
this.document.close();
}
css(property: string, value: string) {
this.el.style[property] = value;
}
}
document.addEventListener("DOMContentLoaded", () => {
const script = document.currentScript || document.querySelector("script[data-jsd-embedded]");
const dataAttributes = script && script instanceof HTMLScriptElement && script.dataset;
const isJsmWidgetRendered = document.querySelector(`iframe#${jsdWidgetID}`);
if (isJsmWidgetRendered) {
// tslint:disable-next-line no-console
console.error("JSM Widget: could not render more than one widget on a single page");
return;
}
if (!script) {
// tslint:disable-next-line no-console
console.error("JSM Widget: could not find jsd-embedded script");
return;
}
if (!dataAttributes) {
// tslint:disable-next-line no-console
console.warn(
"JSM Widget: legacy unsupported browser, jsd-embedded script.dataset was undefined"
);
return;
}
const scriptSrc = script.getAttribute("src");
if (!scriptSrc) {
return;
}
const iframeScriptSrc = `${scriptSrc.substring(0, scriptSrc.lastIndexOf("/"))}/iframe.js`;
new Iframe(iframeScriptSrc, dataAttributes);
});
interface EmbeddableData {
reactDevtools?: string;
}