User Interface Development Documentation

HTML

User interface components in IRIDA should be developed using ReactJS and the Ant Design component library (for more information see below).

IRIDA depends upon the Thymeleaf Template Engine for internationalization and server root URLs.

React JS

The IRIDA user interface is developed using the React JS Framework, although there are legacy pages that still contain jQuery and AngularJS. All new development for IRIDA is expected to be done in React.

Ant Design

IRIDA uses the design system Ant Design, a React JS based component library.

Webpack

Build Process:

Production Mode: ./gradlew buildWebapp

Development Mode: ./gradlew startWebapp

JavaScript

IRIDA uses the babel-loader to transpile the latest versions of JavaScript to a version supported by browsers.

Current babel plugins for UI development:

CSS

IRIDA uses PostCSS to transform CSS:

IRIDA uses webpacks’ MiniCSSExtractPlugin to extract found in JS Files ito its own css file.

Full Front End Build with Webpack and Thymeleaf

An “entry” is used by webpack to indicate the root JavaScript file for a page in IRIDA. All entires for IRIDA are listed in src/main/webapp/entries.js. This is a simple JavaScript exported object that contains key va pairs (key is the entry name, value is the path to the root file).

Example:

module.exports = {
  dashboard: "./resources/js/pages/dashboard/Dashboard.jsx",
}

When webpack compiles all the assets found for that entry (CSS and JavaScript) it will output them in optimized chunks for faster loading and shared code between different entry points (e.g. code for displaying a modal window). This allows for faster loading between entries since this file would not need to be re-downloaded.

These compiled files change during development so it would become a futile effort to keep maintaining all the new links statically on their respective HTML pages. To get around this, we have created a system connecting Webpack and Thymeleaf to allow the dynamic addition of these links to their HTML page at runtime.

Custom HTML Tags

Do not add CSS link and JavaScript script tags for any Webpack entry or dependencies

Due to webpack optimizing and chunking CSS and JavaScript files for faster loading, the IRIDA team created a Thymeleaf extension that parses a webpack manifest file to handle adding the chunks onto the HTML entry page at runtime.

For entries:

How it works

Webpack

This is an excerpt from the assets-manifest.json file:

Excerpt from assets-manifest.json

Thymeleaf

At compile time:

  1. Parses the assets-manifest.json file to create an in memory object of the assets required for each entry point.

At runtime, Thymeleaf will intercept the HTML page request:

  1. See if there is a cached version of this page (for the requested internationalization)
    • If cached, it will return cached page, else continue
  2. Scan the page for the webpacker tags
    • For CSS (<webpacker:css entry="cart" />), it will create and add the required link tags for all CSS chunks to the HTML template
    • For JavaScript (<webpacker:js entry="cart" />), it will:
      • create and add the required script tags for all JavaScript chunks to the HTML template
      • see if there are HTML (internationalization) assets, and if there are it will inject the HTML fragment found on the asset path (in this case the markup found in i18n/cart.html) will be added directly before the script tag for that entry

Example:

Template
<!DOCTYPE html>
<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
>
  <head>
    <meta charset="UTF-8" />
    <title th:text="#{ProjectRemoteSettings.title}">Title</title>
    <webpacker:css entry="cart"/>
  </head>
  <body>
    <div id="cart">LOADING ...</div>
    <webpacker:js entry="cart"/>
  </body>
</html>
After Thymeleaf Webpacker
<!DOCTYPE html>
<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
>
  <head>
    <meta charset="UTF-8" />
    <title th:text="#{ProjectRemoteSettings.title}">Title</title>
      <link rel="stylesheet" href="/dist/css/cart.bundle.css" />
      <link rel="stylesheet" href="/dist/css/268.bundle.css" />
  </head>
  <body>
    <div id="cart">LOADING ...</div>
    <script id="cart-translations" th:inline="javascript" th:fragment="i18n">
        window.translations = window.translations || [];
        window.translations.unshift({
            "SampleDetailsViewer.removeFromCart": /*[[#{SampleDetailsViewer.removeFromCart}]]*/ "",
            "SampleDetails.metadata": /*[[#{SampleDetails.metadata}]]*/ "",
            "SampleDetails.files": /*[[#{SampleDetails.files}]]*/ "",
            "SampleFiles.singles": /*[[#{SampleFiles.singles}]]*/ "",
            "SampleFiles.paired": /*[[#{SampleFiles.paired}]]*/ ""
        });
    </script>
    <script src="/dist/js/cart.bundle.js"></script>
    <script src="/dist/js/144-e0f48175ce0ec34d860f.js"></script>
    <script src="/dist/js/245-f59a9ead959e5e722bf0.js"></script>
  </body>
</html>
After Thymeleaf Internationalization (cached template)
<!DOCTYPE html>
<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
>
  <head>
    <meta charset="UTF-8" />
    <title th:text="#{ProjectRemoteSettings.title}">Title</title>
      <link rel="stylesheet" href="/dist/css/cart.bundle.css" />
      <link rel="stylesheet" href="/dist/css/268.bundle.css" />
  </head>
  <body>
    <div id="cart">LOADING ...</div>
    <script id="cart-translations">
        window.translations = window.translations || [];
        window.translations.unshift({
            "SampleDetailsViewer.removeFromCart": "Remove From Cart",
            "SampleDetails.metadata": "Metadata",
            "SampleDetails.files": "Files",
            "SampleFiles.singles": "Single End Data",
            "SampleFiles.paired": "Paired End Date"
        });
    </script>
    <script src="/dist/js/cart.bundle.js"></script>
    <script src="/dist/js/144-e0f48175ce0ec34d860f.js"></script>
    <script src="/dist/js/245-f59a9ead959e5e722bf0.js"></script>
  </body>
</html>