Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
hasithaishere committed Oct 4, 2021
0 parents commit 87f1696
Show file tree
Hide file tree
Showing 26 changed files with 20,229 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Email Template Builder Widget

![Email Template Builder Preview](https://i.ibb.co/VV3LGYM/Screenshot-from-2021-09-09-15-19-46.png)


This project is maintained for building an embeddable React widget that can be inserted into a host website using a single `<script>` tag. This renders email template viewer. It supports JSX, CSS styles, and is compiled using Webpack into a single .js file which can be static-hosted.

Both synchronous and asynchronous loading is supported.

# Overview
1. The widget is instantiated when the .js package is loaded
2. The host page supplies a **name** and a **targetElementId**
3. The widget registers a global object with the name supplied by the host page
4. The widget renders the React component at the element specified by the host page
5. The host page can communicate with the widget via the global object


## Usage Example #1: Synchronous
This method uses simple `<script>` tag reference as shown below:

```html
<div id="root"></div>

<script src="http://somehost/widget.js"
id="Simple-Widget-Script"
data-config="{'name': 'w1', 'config': {'targetElementId': 'root'}}" ></script>
```

The data-config attribute passes in the name **w1** for the widget's global object as well as the target element id **root** where the widget should be rendered.

The host page can then communicate with the widget via the global object like this:

```html
<div><button onclick="w1('message', 'Hello world!');">Send Message</button></div>
```

In this code, we send the **message** call to the widget and pass a string as the parameter.

## Usage Example #2: Asynchronous
We can load the widget asynchronously. Using this method we create a *temporary* object that holds any calls to the widget in a queue and when the widget loads, it will then process those calls.

```html
<div id="root">Loading...</div>
<script>
(function (w, d, s, o, f, js, fjs) {
w['Simple-Widget'] = o; w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
}(window, document, 'script', 'w1', 'http://somehost/widget.js'));
w1('init', { targetElementId: 'root' });
</script>
```

This code follows the pattern used by Google Analytics. The function is called with the desired name of the global object (**w1**) and the url to the script. The function then records the desired name and, using that name, creates a placeholder global object that receives and queues any calls made to the widget before the asynchronous loading finishes.

Then it creates a script tag and injects it into the DOM.

The host then issues the 'init' call to the widget passing in any initialization values:

```html
w1('init', { targetElementId: 'root' });
```

# Running the Project
## Install dependencies
```
$ npm install
```
## Run the development server
```
$ ./node_modules/.bin/webpack-dev-server --open
```
## Build the package
```
$ ./node_modules/.bin/webpack --config webpack.config.js
```
## Run Tests
```
$ Jest
```

# References
Helpful guidance in this project from the following sites:

https://blog.jenyay.com/building-javascript-widget/

https://github.com/seriousben/embeddable-react-widget
1 change: 1 addition & 0 deletions _config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
theme: jekyll-theme-slate
31 changes: 31 additions & 0 deletions demo/demo_async.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<html>

<head>
<title>Demo page of the widget</title>
</head>

<body>
<h1>Lorem ipsum dolor sit amet augue</h1>
<h2>Fusce gravida vehicula vestibulum tortor pulvinar</h2>
<p>Orci purus ante excepteur nunc nascetur rhoncus taciti ut. Ornare vitae sed. Amet suspendisse integer rutrum
aliquet
venenatis. Nec sed nullam mauris magna vel pretium non tristique. Porta justo ac dictumst aliquip blandit.
Libero
nec eu dolor eget volutpat. Magna nulla neque. Fames tempus lorem duis orci ipsum.</p>

<div id="root">Loading...</div>
<script>
(function (w, d, s, o, f, js, fjs) {
w['Simple-Widget'] = o; w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
}(window, document, 'script', 'w1', './widget.js'));
w1('init', { targetElementId: 'root', postUrl: 'https://etbw-email-temp.free.beeceptor.com/api/save-email-template', dataUrl: 'https://api.npoint.io/6f3c943608efd90ab2f3' });

</script>

</body>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

</html>
23 changes: 23 additions & 0 deletions demo/demo_sync.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>

<head>
<title>Demo page of the widget</title>
</head>

<body>
<h1>Lorem ipsum dolor sit amet augue</h1>
<h2>Fusce gravida vehicula vestibulum tortor pulvinar</h2>
<p>Orci purus ante excepteur nunc nascetur rhoncus taciti ut. Ornare vitae sed. Amet suspendisse integer rutrum
aliquet
venenatis. Nec sed nullam mauris magna vel pretium non tristique. Porta justo ac dictumst aliquip blandit.
Libero
nec eu dolor eget volutpat. Magna nulla neque. Fames tempus lorem duis orci ipsum.</p>


<div id="root"></div>

<script src="./widget.js" id="Simple-Widget-Script" data-config="{'name': 'w1', 'config': {'targetElementId': 'root', 'postUrl': 'https://etbw-email-temp.free.beeceptor.com/api/save-email-template', 'dataUrl': 'https://api.npoint.io/6f3c943608efd90ab2f3' }}" ></script>

</body>

</html>
13 changes: 13 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<html>

<head>
<title>Simple Embeddable React Widget</title>
</head>

<body>
<h1>Simple Embeddable React Widget</h1>
<p>Demo #1: <a href="demo_sync.html">synchronous loading</a></p>
<p>Demo #2: <a href="demo_async.html">asynchronous loading</a></p>
</body>

</html>
31 changes: 31 additions & 0 deletions dist/demo_async.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<html>

<head>
<title>Demo page of the widget</title>
</head>

<body>
<h1>Lorem ipsum dolor sit amet augue</h1>
<h2>Fusce gravida vehicula vestibulum tortor pulvinar</h2>
<p>Orci purus ante excepteur nunc nascetur rhoncus taciti ut. Ornare vitae sed. Amet suspendisse integer rutrum
aliquet
venenatis. Nec sed nullam mauris magna vel pretium non tristique. Porta justo ac dictumst aliquip blandit.
Libero
nec eu dolor eget volutpat. Magna nulla neque. Fames tempus lorem duis orci ipsum.</p>

<div id="root">Loading...</div>
<script>
(function (w, d, s, o, f, js, fjs) {
w['Simple-Widget'] = o; w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
}(window, document, 'script', 'w1', './widget.js'));
w1('init', { targetElementId: 'root', postUrl: 'https://etbw-email-temp.free.beeceptor.com/api/save-email-template', dataUrl: 'https://api.npoint.io/6f3c943608efd90ab2f3' });

</script>

</body>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

</html>
23 changes: 23 additions & 0 deletions dist/demo_sync.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>

<head>
<title>Demo page of the widget</title>
</head>

<body>
<h1>Lorem ipsum dolor sit amet augue</h1>
<h2>Fusce gravida vehicula vestibulum tortor pulvinar</h2>
<p>Orci purus ante excepteur nunc nascetur rhoncus taciti ut. Ornare vitae sed. Amet suspendisse integer rutrum
aliquet
venenatis. Nec sed nullam mauris magna vel pretium non tristique. Porta justo ac dictumst aliquip blandit.
Libero
nec eu dolor eget volutpat. Magna nulla neque. Fames tempus lorem duis orci ipsum.</p>


<div id="root"></div>

<script src="./widget.js" id="Simple-Widget-Script" data-config="{'name': 'w1', 'config': {'targetElementId': 'root', 'postUrl': 'https://etbw-email-temp.free.beeceptor.com/api/save-email-template', 'dataUrl': 'https://api.npoint.io/6f3c943608efd90ab2f3' }}" ></script>

</body>

</html>
31 changes: 31 additions & 0 deletions dist/dist/demo_async.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<html>

<head>
<title>Demo page of the widget</title>
</head>

<body>
<h1>Lorem ipsum dolor sit amet augue</h1>
<h2>Fusce gravida vehicula vestibulum tortor pulvinar</h2>
<p>Orci purus ante excepteur nunc nascetur rhoncus taciti ut. Ornare vitae sed. Amet suspendisse integer rutrum
aliquet
venenatis. Nec sed nullam mauris magna vel pretium non tristique. Porta justo ac dictumst aliquip blandit.
Libero
nec eu dolor eget volutpat. Magna nulla neque. Fames tempus lorem duis orci ipsum.</p>

<div id="root">Loading...</div>
<script>
(function (w, d, s, o, f, js, fjs) {
w['Simple-Widget'] = o; w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
}(window, document, 'script', 'w1', './widget.js'));
w1('init', { targetElementId: 'root' });

</script>

<div><button onclick="w1('message', 'Hello world!');">Send Message</button></div>

</body>

</html>
24 changes: 24 additions & 0 deletions dist/dist/demo_sync.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<html>

<head>
<title>Demo page of the widget</title>
</head>

<body>
<h1>Lorem ipsum dolor sit amet augue</h1>
<h2>Fusce gravida vehicula vestibulum tortor pulvinar</h2>
<p>Orci purus ante excepteur nunc nascetur rhoncus taciti ut. Ornare vitae sed. Amet suspendisse integer rutrum
aliquet
venenatis. Nec sed nullam mauris magna vel pretium non tristique. Porta justo ac dictumst aliquip blandit.
Libero
nec eu dolor eget volutpat. Magna nulla neque. Fames tempus lorem duis orci ipsum.</p>


<div id="root"></div>

<script src="./widget.js" id="Simple-Widget-Script" data-config="{'name': 'w1', 'config': {'targetElementId': 'root'}}" ></script>

<div><button onclick="w1('message', 'Hello world!');">Send Message</button></div>
</body>

</html>
13 changes: 13 additions & 0 deletions dist/dist/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<html>

<head>
<title>Simple Embeddable React Widget</title>
</head>

<body>
<h1>Simple Embeddable React Widget</h1>
<p>Demo #1: <a href="demo_sync.html">synchronous loading</a></p>
<p>Demo #2: <a href="demo_async.html">asynchronous loading</a></p>
</body>

</html>
13 changes: 13 additions & 0 deletions dist/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<html>

<head>
<title>Simple Embeddable React Widget</title>
</head>

<body>
<h1>Simple Embeddable React Widget</h1>
<p>Demo #1: <a href="demo_sync.html">synchronous loading</a></p>
<p>Demo #2: <a href="demo_async.html">asynchronous loading</a></p>
</body>

</html>
2 changes: 2 additions & 0 deletions dist/widget.js

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions dist/widget.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/

/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v16.14.0
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
8 changes: 8 additions & 0 deletions jest/staticHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
process() {
return 'module.exports = {};';
},
getCacheKey() {
return 'staticHandler';
},
};
Loading

0 comments on commit 87f1696

Please sign in to comment.