My problem started when I wanted to use a component as a widget in an external website. First of all I created a route with react router to print only that component, so I can use it in an iframe, but the problem was that the component has dropdowns so when they were opened you had to scroll in order to access them.
So I did a research on internet to how to make a component "widgeable", but I could not find anything, or at least anything clear.
And here comes my question, is there a way to export my component, with styles and everything, separately from the whole project to use it as a widget for external websites? Is there an standard way to do it?
Also, is there a way to compile with webpack a component? If it is possible, how can I use it after being compiled in an external website?
In order to give you more information, my component its a form, that form has a dropdown calendar and popups, and with the react-router I've created a route on /widgets/form where prints only the form component.
Thanks for reading me!
Update 1
I would like to avoid adding react to the external website, I do want them to simply add an snippet of JS to their body, like this:
((d, s, u) => {
const h = d.getElementsByTagName(s)[0],
j = d.createElement(s);
j.async = true;
j.src = 'http://localhost:3000/files/widgets/ibb-form.min.js';
j.onload = () => {
console.log('Widget: ibblue form loaded!');
start({ url: u });
};
h.parentNode.insertBefore(j, h);
})(document, 'script', 'http://localhost:3000/widgets/form');
function start({ url }) {
new window.IBBForm({ url });
}
and the JS that I use in the script that snippet adds it's the following one:
(() => {
const IBBForm = class {
constructor({ id, url }) {
this._elementID = id || '#ibblue_form';
this._target = document.querySelector(this._elementID);
if (!url) throw new Error('No URL specified');
this._url = url;
this._iframe = this._createIframe();
this._target.append(this._iframe);
}
_createIframe() {
const iframe = document.createElement('iframe');
iframe.id = 'ibblue_form_iframe_' + new Date().getTime();
iframe.src = this._url;
iframe.style =
'width: 100%; height: 600px; border: medium none; background-color: transparent; display: initial;';
iframe.allowTransparency = 'true';
return iframe;
}
};
window['IBBForm'] = IBBForm;
})();
Update 2
I edited the script downloaded to the following one:
import BookingSearch from './../../src/components/sections/BookingSearch';
(() => {
const IBBForm = class {
constructor({ id, url }) {
this._elementID = id || '#ibblue_form';
this._target = document.querySelector(this._elementID);
if (!url) throw new Error('No URL specified');
this._url = url;
this._addRequirements().then(() => {
this._element = this._createForm();
});
}
async _addRequirements() {
await this._addScript('https://unpkg.com/react@17/umd/react.production.min.js');
await this._addScript('https://unpkg.com/react-dom@17/umd/react-dom.production.min.js');
}
async _addScript(url) {
return new Promise((resolve, reject) => {
const h = document.getElementsByTagName(s)[0],
j = document.createElement(s);
j.async = true;
j.type = 'module';
j.src = url;
j.crossorigin = '';
j.onload = resolve();
h.parentNode.insertBefore(j, h);
});
}
_createForm() {
return ReactDOM.render(<BookingSearch widget={true} dataTheme={{}} />, this._target);
}
};
window['IBBForm'] = IBBForm;
})();
When I execute it, console throws some errors:
Uncaught SyntaxError: expected expression, got '<'
Uncaught TypeError: window.IBBForm is not a constructor