UptickEngineering

From the blog

Introducing React Interactive Tutorials

TL;DR? Check out the demo

Learning any new web app can be intimidating to new users, especially those who don’t work with online tools on a day-to-day basis. To ease the transition from newbie to professional on aBAS, we decided to explore the possibility of building tutorials into the system.

Having interacted with similar onboarding material on other software-as-a-service platforms, we knew that when it comes to learning an interface the tutorials would have to be:

  • Interactive, rather than a video or slideshow

    Although a video format would be appropriate for core concepts, it can be difficult and frustrating to follow a video guide on how to use software tools. Additionally, it becomes a burden to keep videos up to date when changes are made to the app.

  • Loosely coupled with the actual interface

    We don’t want to make drastic changes to our existing interface, especially if it demands adding extra content or logic that will be invisible 99% of the time.

  • Compatible with tutorials spanning different pages of the app

    Only some components of our app are single page app driven, so the tutorials need to be able to track state and display content across page loads.


So, knowing what we need, what’s out there?

Having a look at the open source javascript packages available, there were some promising tools we considered. However, they were all problematic for at least one of a few reasons:

  • The tutorials weren’t interactive, they were more of an annotation slideshow

    This is the approach taken by many introductory frameworks; highlighting a sequence of components in the app without taking any user input. Although easy to implement, this format would not work well for the complicated user interface interactions we want to teach.

    In some cases the tutorials weren’t embedded either, they were maintained in a clone of the app. With the rapid iteration of our product, we would struggle to keep two versions of the interface up to date.

  • Lack of annotations, or absolute positioning of injected annotations

    Another common theme was a lack of inline annotations, opting for text within a separate tutorial overlay interface. Those that did allow annotations worked by injecting them inline into the DOM (which is problematic with React as discussed later), or by fixed positioning the annotations manually based on the target’s bounding rectangle. For best cross-browser compatibility and performance, we wanted to avoid using fixed positions.

  • They made too many assumptions about how the tutorials would be used

    Among the interactive options, we found them to become either frustratingly restrictive or fragile when the complexity of the training material increased.

    It gives a very bad impression when the user does something slightly unexpected and the tutorial breaks, and the concepts we want to explain can take multiple paths (otherwise they would not warrant a tutorial). It’s a similarly frustrating experience when you have to manually consent to progress the tutorial after following its instructions.

For these reasons, we decided to create our own tutorial framework built with React.


How do React Interactive Tutorials work?

Each tutorial is structured as a JSON blob containing some top-level information like the tutorial name and description, along with a series of steps. The steps define the highlighted regions and annotations at any given point of the tutorial.

Rather than a traditional sequentially ordered checklist of requirements to complete a tutorial, each tutorial step itself has a set of conditions that determine whether or not that step is “active”. The tutorial lands on the currently active step by evaluating every step in reverse until all its conditions are met.

Because of this behaviour, we get a lot of flexibility and logic for free. For example if the tutorial involves two steps to open a dropdown and then follow a contained link, but the user knows a shortcut to navigate directly to the end link without opening the dropdown, the tutorial will jump ahead as soon as they land on the destination page.

At this point in time, the available step conditions are:

  • HTML form input is equal, or not equal, to a given value
  • The page URL matches a given regular expression
  • Bootstrap dropdown state (open / closed)
  • Another step has been completed

and all conditions can be chained to any depth of AND / OR blocks for complicated comparisons.

activeWhen: [
  {
    compare: 'url',
    url: /\/service-quotes\/create\/$/,
  },
  {
    compare: 'either',
    when: [
      {
        compare: 'inputNotVal',
        selector: '#property_object',
        value: null,
      },
      {
        compare: 'all',
        when: [
          {
            compare: 'inputNotVal',
            selector: '#property_address',
            value: '',
          },
          {
            compare: 'inputNotVal',
            selector: '#property_name',
            value: '',
          },
        ],
      },
    ],
  },
],

With our app, we find it is best practice to have a catch-all step at the top that should be able to recover to a known state from any page on our site (via a header etc. that is present on every page).

To adapt to user input in real time, and allow a smooth transition to the next step of the tutorial when the user completes the required action whilst the annotation is open, the tutorial framework listens to the following events:

  • Browser loads to a different page (initial page load)
  • User changes the value on an HTML form input
  • Bootstrap dropdown state changes
  • Bootstrap modal is opened

to automatically re-evaluate which step is active. To avoid interrupting users while they are typing into form fields, any form input changes are debounced until the user has stopped making changes for a brief period of time.

The help overlay can be toggled on or off by the user at any point any time, and only re-appears without user input when the tutorial step progresses through user actions.


How are annotations and highlights handled?

Originally, the tutorials worked by injecting classes onto highlights (to raise the z-index above a backdrop) and appending span elements with annotation content where desired.

However we quickly realised that this approach was at odds with our use of React throughout our core product. Although we can annotate around wrappers of React components, we could never annotate or highlight individual pieces of anything powered by React; doing so would mutate it’s rendered DOM and thus break or be broken by React’s optimised render functions.

To solve this, React-Interactive-Tutorials now works entirely through css by rendering its own <style> tag. The styles adjust the z-index of highlighted elements and create annotations with :after pseudo-elements, fortunately modern browsers are clever enough to adapt in real time to the style element being re-written and the behaviour is unchanged.

The only downside to this approach is that there can no longer be interactive content positioned relative to the annotations. To solve this, the existing “skip” button after annotations has been incorporated into the interface (bottom right corner), which overall is a more robust solution.


Future of the Project

We’ve put together a couple of basic tutorials and have been using the training program in production for a few months now. More varied and complicated tutorials will demand additional logic, so there will be more condition options in future versions.

Some cool directions the project could take:

  • Support for tutorials contained within React components

    Although the annotations are theoretically supported within React components, there is no logic to progress a tutorial based on the state of your React app besides conventional input events.

  • Better vision-impaired support

    At the moment the tutorials are highly visual, depending on the layering of z-indices and positioning of text to be useful. This is likely not very usable for users who have complex styling disabled or depend upon a screen reader, especially as the annotations are represented as pseudo-elements.

  • Some kind of tutorial config building interface

    The JSON tutorials aren’t difficult to build if you’re familiar with programming condition syntax and css selectors, but anything that would make creating tutorials easier or more accessible would greatly improve the overall package. Given the complexity of the step conditions, this would be an ambitious project.

  • Better mobile device scaling of tutorial overlay, and support for device size based conditions

    The current tutorial interface is not designed around mobile devices, but with a few tweaks and @media queries it could be greatly improved. Similarly, the annotations and highlights would benefit from adapting to screen widths to better support responsive user interfaces.


If you’d like to contribute, please check out the github project: https://github.com/uptick/react-interactive-tutorials


For an interactive sample of the current stable version, check out the demo page at:

https://uptick.github.io/react-interactive-tutorials/

Note: The training module is currently not intended for mobile devices.


For instructions on how to add react-interactive-tutorials to your project, head to the readme page:

https://github.com/uptick/react-interactive-tutorials/blob/master/README.md