Without a framework
EF elements are Web Component. They can be used with JavaScript frameworks or without a framework (vanilla JavaScript). This guideline provides how you can use EF elements without a framework, either with bundling tool or without bundling tool.
Without bundling
It's the simplest and the quickest way for creating a quick standalone demo and getting started with EF. However, without any extra tools it will only support browsers that natively support JavaScript module syntax. [see list].
EF elements are shipped as ES6 module. To use EF elements without a JavaScript framework, simply include a script with type=module
into your HTML template.
The following tutorial will guide you through creating a simple login page using EF.
Initialize your project
Create a new project folder.
mkdir project-demo
cd project-demo
Use npm init
command to initialize the project. It will create package.json
file which contains application information and its dependencies.
npm init
Install EF dependencies
Install element and theme packages using npm.
npm i @refinitiv-ui/elements
npm i @refinitiv-ui/halo-theme
Project setup
Initialize HTML template
Create file index.html
with a typical HTML template.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>A Simple HTML</title>
</head>
<body></body>
</html>
Import component and theme modules
Create a script type="module"
tag that imports EF dependencies, then include it to index.html
.
<script type="module">
import '@refinitiv-ui/elements/lib/button';
import '@refinitiv-ui/elements/lib/panel';
import '@refinitiv-ui/elements/lib/text-field';
import '@refinitiv-ui/elements/lib/password-field';
import '@refinitiv-ui/halo-theme/dark/imports/native-elements';
import '@refinitiv-ui/elements/lib/button/themes/halo/dark';
import '@refinitiv-ui/elements/lib/panel/themes/halo/dark';
import '@refinitiv-ui/elements/lib/text-field/themes/halo/dark';
import '@refinitiv-ui/elements/lib/password-field/themes/halo/dark';
</script>
Create a login page
Now, we can start adding elements, styling and logics into index.html
.
<style>
ef-panel {
display: flex;
flex-direction: column;
align-items: center;
width: 450px;
height: 100%;
margin: 40px auto;
border: solid 1px #e1e1e1;
}
h1 {
margin-top: 40px;
}
</style>
<ef-panel spacing>
<h1>Hello!</h1>
<ef-text-field placeholder="Username"></ef-text-field>
<ef-password-field placeholder="Password"></ef-password-field>
<div>
<ef-button disabled>Login</ef-button>
<ef-button>Cancel</ef-button>
</div>
</ef-panel>
<script>
const username = document.querySelector("ef-text-field");
const password = document.querySelector("ef-password-field");
const loginButton = document.querySelector("ef-button");
const onTextChanges = function () {
if (username.value.length === 0 || password.value.length === 0) {
loginButton.disabled = true;
} else {
loginButton.disabled = false;
}
};
username.addEventListener("value-changed", onTextChanges);
password.addEventListener("value-changed", onTextChanges);
loginButton.addEventListener("tap", function () {
document.querySelector("h1").textContent = "Done!";
});
</script>
Serving application
In order to correctly serve bare modules, we need a tool that rewrites the path. For the simplicity, we are going to use es-dev-server
In the browser, import must get either a relative or absolute URL. Modules without any path are called “bare” modules.
At root directory in your sample application, run this command to serve the app.
npx es-dev-server --node-resolve --watch
<ef-panel spacing> <h1>Hello!</h1> <ef-text-field placeholder="Username"></ef-text-field> <ef-password-field placeholder="Password"></ef-password-field> <div> <ef-button disabled>Login</ef-button> <ef-button>Cancel</ef-button> </div> </ef-panel>
const username = document.querySelector('ef-text-field'); const password = document.querySelector('ef-password-field'); const loginButton = document.querySelector('ef-button'); const onTextChanges = function() { if (username.value.length === 0 || password.value.length === 0) { loginButton.disabled = true; } else { loginButton.disabled = false; } }; username.addEventListener('value-changed', onTextChanges); password.addEventListener('value-changed', onTextChanges); loginButton.addEventListener('tap', function() { document.querySelector('h1').textContent = 'Done!'; });
ef-panel { display: flex; flex-direction: column; align-items: center; width: 450px; height: 100%; margin: 40px auto; } ef-button { margin: 15px 5px; } h1 { margin-top: 40px; }
With bundling
On this part of tutorial, you are going to take a step further and look into setting up bundle configuration. In this example, you will build your application to support legacy browsers which are not neither support ES6 nor Web Component.
We are going to use webpack
as it is one of the most popular bundling tool. However, setup of the other bundling tools would be fundamentally similar.
Installing the tools
Webpack
webpack
is going to resolve modules import path, process and pack them into production-ready files. While webpack-dev-server
will serves a webpack app and updates the browser on changes.
npm i -D webpack@4 webpack-cli webpack-dev-server
Babel
babel
will transpile modern JavaScript syntax (es6
or newer) into es5
which also works on older browsers. However, the bundle size will be larger and so it will have significant impact on the performance. babel
relies on @babel/preset-env
which will read a provided list of target browsers and compile accordingly.
@babel/plugin-transform-runtime
is a plugin that allows the re-use of Babel's injected helper code to optimize bundle size. This is used along with @babel/runtime
, a runtime dependency. more
npm install -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install @babel/runtime
core-js
core-js
a module that provides polyfills for es6 and newer. It's required to use with babel
.
npm i core-js@3
Project setup
In src
folder, create index.js
and then import EF elements, themes and polyfills.
// Polyfills
import '@refinitiv-ui/polyfills/minimal';
// Components
import '@refinitiv-ui/elements/lib/button';
import '@refinitiv-ui/elements/lib/panel';
import '@refinitiv-ui/elements/lib/text-field';
import '@refinitiv-ui/elements/lib/password-field';
// Themes
import '@refinitiv-ui/halo-theme/dark/imports/native-elements';
import '@refinitiv-ui/elements/lib/button/themes/halo/dark';
import '@refinitiv-ui/elements/lib/panel/themes/halo/dark';
import '@refinitiv-ui/elements/lib/text-field/themes/halo/dark';
import '@refinitiv-ui/elements/lib/password-field/themes/halo/dark';
Then at the root directory of the project, create index.html
with following content.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Webpack sample</title>
</head>
<body>
<script src="./dist/index.js"></script>
<ef-panel class="panel" spacing>
<h1>Hello!</h1>
<ef-text-field class="input" placeholder="Username"></ef-text-field>
<ef-password-field
class="input"
placeholder="Password"
></ef-password-field>
<div class="btn-container">
<ef-button cta disabled>Login</ef-button>
<ef-button>Cancel</ef-button>
</div>
</ef-panel>
<script>
const username = document.querySelector("ef-text-field");
const password = document.querySelector("ef-password-field");
const loginButton = document.querySelector("ef-button");
const onTextChanges = function () {
if (username.value.length === 0 || password.value.length === 0) {
loginButton.disabled = true;
} else {
loginButton.disabled = false;
}
};
username.addEventListener("value-changed", onTextChanges);
password.addEventListener("value-changed", onTextChanges);
loginButton.addEventListener("tap", function () {
document.querySelector("h1").textContent = "Done!";
});
</script>
<style>
.panel {
display: flex;
flex-direction: column;
align-items: center;
width: 400px;
height: 300px;
margin: 10em auto;
}
.input {
margin-top: 8px;
}
.btn-container {
display: flex;
margin: 12px 0;
}
.btn-container ef-button {
margin: 0 8px;
}
</style>
</body>
</html>
Configurations
Create a file name webpack.config.js
at the project root and paste the following configuration.
What this configuration does is setting webpack
to bundle and transpile a ./src/index.js
and output it to ./dist/index.js
. It will also look for files with extension of .mjs
and .js
in the folders that specified in include
section to transpile it down to ES5 by using babel (by default, babel will not transpile any folders in node_modules).
Note that you may need to update list of folders in the include
section as some dependencies might has started to ship the module with ES6 instead of ES5.
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.m?js$/,
include: [
path.resolve(__dirname, 'node_modules/@refinitiv-ui'),
path.resolve(__dirname, 'node_modules/@webcomponents'),
path.resolve(__dirname, 'node_modules/lit-element'),
path.resolve(__dirname, 'node_modules/lit-html'),
path.resolve(__dirname, 'node_modules/d3-interpolate')
],
use: {
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
corejs: 3,
useBuiltIns: "usage",
},
],
],
plugins: ["@babel/plugin-transform-runtime"],
},
},
},
],
},
};
Create a file name .browserslistrc
with the following content at the application root directory. This is the list of target browsers that we want our application to work on, it will be read by babel
.
ie >= 11
Firefox >= 63
Chrome >= 61
Safari >= 11
Add scripts to package.json
.
"scripts": {
"start": "webpack-cli serve --compress --host 0.0.0.0 --mode production",
"build": "webpack --config webpack.config.js --mode production"
}
Finally, run npm run build
and then npm start
.
You should be able to access the application at http://localhost:8080/