Building a responsive client-side widget framework – Part 2

Share the joy
  •  
  •  
  •  
  •  

In this post I continue on from Part 1: Building a client-side responsive widget framework, to talk over the approach used to building JustGiving widgets.

Problem 4: Allowing widgets to be configurable

JustGiving widgets need to be configurable to some degree, to allow the user to select a specific layout choice, or to pass in required config values our REST APIs need to make the correct http requests. This config needs to be baked into the minimal widget snippet we supply to our end users. To handle this, a data-config attribute is added to the widget html snippet. This includes the user selected and required config as key-value pairs. The host JavaScript file reads the value of the data-config attribute and parses the string into a query string format that is appended to widget’s iframe URL. When it loads, the widget application can then read its own URL, and parse the query string parameters back into a config object that can be propagated into the application.

Problem 5: Cross-browser CORS XHR requests with promise support

Because our REST API endpoints are called using XHR and are served on a different domain to the widgets themselves the widgets must implement CORS (Cross origin resource sharing) fto make endpoints requests. We tested a set of AJAX micro libraries that claimed support for CORS and we decided on a library that offered full CORS support, even down to IE8. Using the library, a promisified fetchData method was written. This returns a promise (a ‘thenable’) which allows cleaner handling of async flows within the application, and handles the parsing of returned text data* back into JSON for consumption by the widget application.

*IE8 supports CORS but only so far as accepting requests for contentType text/plain but not application/json, where JSON is the usual currency of a REST endpoint. Where JSON data is required, the payload can be stringified by the REST API and sent back to the browser as text. It can then be parsed back into JSON by the consuming application.

Problem 6: Building small and fast

Our widgets will load asynchronously to prevent blocking or slowing of load times of our hosts’ pages. This means that they need to load and render as quickly as possible, so the user doesn’t scroll beyond them before they’re visible. So we made performance a key focus throughout development, particularly application weight and http request count. Native code was preferred over heavier vendor frameworks and libraries. This helped minimise load time, provide faster runtime operations and to speed up page rendering. Build steps such as minification, concatenation and icon data-uri generation were added to further reduce page weight and the number of http requests the widgets must make. Concurrent REST API requests were enabled through used of promise patterns such as Promise.all, and optimised CSS transitions were used to give a jank-free user-experience to interactive UI components.

Problem 7: A re-usable framework

Many of the widgets need to solve the same problems: fluid iFrames, CORS XHR requests, reading user configuration, firing off analytics data, client-side feature detection, API polling, and UI components such as carousels. The solution to this is to create a set of decoupled JavaScript, CSS and UI modules that are stored in a components library which is treated as a widget dependency and installed into each widget with front-end package manager Bower. By knitting this all together into a framework, the heavy-lifting of the widget application is already dealt with and new widgets can be produced quickly.

From a JavaScript perspective this kind of modularity can be achieved by creating classes and storing these and other pieces of functionality in modules. ES6 (also known as ES Harmony and ES 2015) was chosen as the default native JavaScript specification. It provides a clean class definition syntax that allows for easy inheritance and extension of functionality, as well as providing native JavaScript modules. Each widget is bootstrapped by creating a derived class that inherits from a base widget class that provides core widget functionality. Additional modules can then be pulled into this application class as needed and initialised at bootstrap. Because all XHR requests have been promisified we can fetch the required JSON input before passing the data to View class that handle the rendering of the data into the DOM using Handlebars templating.

For CSS, creating re-usable styles was equally important to allow for quick iteration of new widgets, as well as ensuring brand consistency. OOCSS architecture was chosen as it is well suited to building out a library of re-usable CSS ‘objects’ that can be applied, combined and re-used across many contexts.

Finally, to complete the framework, we wrote a custom Yeoman generator that bundles in the bower dependency and generates requisite boilerplate code and folder structure for scaffolding out a new widget project in a matter of seconds.

Problem 8: Pulling it all together with gulp

In a codebase made of many small parts, a task runner is essential to bringing them all together to generate deployable production code. We chose Gulp as the tool of choice. It handles many tasks including transpiling ES6, bundling JavaScript modules, linting and minifying code, compiling Less into CSS, running JavaScript unit tests and taking performance metrics. Each widget has its own Gulp file. This acts a bit like a build config file, where the assets the widget requires can be specified and bundled for build.

At the time of writing we are using this new framework to build out a set of beta widgets for our charity customers. The next steps are to create a widget builder application that allows end users to select and configure their widgets and delivers their code snippets in an easy-to-use format. In the meantime, look out for a JustGiving widget near you!


Share the joy
  •  
  •  
  •  
  •  

About the author

Leanne T

Senior Front-End Developer

View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *