Skip to main content

Tour Module Part 2: Creating a Tour for your module

Drupal 8 comes with a new guided help API in the form of the Tour module. PreviousNext put a lot of effort into getting this module into Drupal 8 core prior to feature freeze and are excited by the possibilities it presents to ease the learning curve for new Drupal site-builders.

In part 1 of this post - we told the tale of how this module came to fruition. In this post, part 2, we provide examples of how you can add tour module support to your modules and talk about some of the advanced features of the API.

by lee.rowlands /

What is Tour module?

In case you haven't yet read part 1 of this series on tour.module, tour module provides a context-sensitive guided tour of the main parts of a given Drupal user interface. It is powered by the JQuery Joyride plugin and allows site builders and module-developers the ability to provide focussed help to target elements on the page. If all of that sounds a bit wordy, here's a screencast of it in action.

Writing Tour module integration for your module.

So, how do you go about writing tour integration for your core or contrib module? Well it is as simple as creating a yml file in your module's config folder.

Let's consider an example from the 'Write tour integration for forum module' issue.

The yaml for the add/edit forum pages is named tour.tour.forum-containern.yml, which follows the module.type.id.yml pattern. So for example if you wanted to provide a tip for a 'configure pants' form in your module, you might name your file tour.tour.configure-pants.yml.

The forum add/edit yml looks like this:

id: forum-container
label: Add or Edit a forum container
langcode: en
paths:
  - admin/structure/forum/add/container
  - admin/structure/forum/edit/container/*
tips:
  introduction:
    id: introduction
    plugin: text
    label: Adding or Editing a container
    body: <p>This form can be used to edit an existing container or add a new container to your site.</p><p>Containers are used to group forums together. For example if you ran a Drupal forum you might create a 'Support' container and include forums for 'Installing Drupal' and 'Getting started' inside the 'Support' Container</p>
    weight: "1"
  container-name:
    id: container-name
    plugin: text
    label: Container name
    body: Enter a name to identify this container. Eg 'Support'
    weight: "2"
    attributes:
      data-id: edit-name
  container-description:
    id: container-description
    plugin: text
    label: Container description
    body: Give your container a description to help identify the purpose of the container and the types of forum it will contain. You can also use the container description to provide guidelines for other site administrators to help them decide which container a new forum might belong in.
    weight: "3"
    attributes:
      data-id: edit-description
  container-parent:
    id: container-parent
    plugin: text
    label: Container parent
    body: If you wish to nest your containers, select the parent container here. If you don't require nesting, choose <root>.
    weight: "4"
    attributes:
      data-id: edit-parent-0
  container-weight:
    id: container-weight
    plugin: text
    label: Container weight
    body: <p>Use the weight field to alter the order in which containers are displayed. Use a lower number to move a container to the top of the list.</p><p>E.g. if you have two containers, one with weight of -5 and one with weight of 5, the one with the -5 weight will be displayed first.</p><p>Items with the same weight are sorted alphabetically.</p>
    weight: "5"
    attributes:
      data-id: edit-weight
  container-save:
    id: container-save
    plugin: text
    label: Save
    body: When you have finished completing the form, click 'Save' to create the new container or save the changes to an existing container.
    weight: "6"
    attributes:
      data-id: edit-submit
  container-delete:
    id: container-delete
    plugin: text
    label: Delete
    body: Use this button to delete your container. You will be required to confirm you wish to delete the container before it is actually deleted.
    weight: "7"
    attributes:
      data-id: edit-delete

The yaml consists of a few key properties as follows

  • id - the id of the tour
  • langcode - the language of the tour. We have configuration schemas now so config entities will eventually be translatable.
  • label - a name for the tour - these aren't used in core but will be leveraged by the Tour UI module which is under active development (read more below).
  • paths - an array of paths for which the tour is active. Specify these the same way you would specify block visibility - ie you can use * for wildcards
  • tips - an array of tip plugin configuration to comprise the tour

Tip plugins

Tip types leverage the core plugin system and implement a TipPluginInterface. Core ships with a 'text' plugin and there is an 'image' plugin used in tests. But as it is plugin-based, any module can create a new plugin that implements the API. So things like Youtube videos or other rich interactions can easily be achieved via the API.

So lets look at the text plugin configuration as seen in the forum yml above

container-delete:
    id: container-delete
    plugin: text
    label: Delete
    body: Use this button to delete your container. You will be required to confirm you wish to delete the container before it is actually deleted.
    weight: "7"
    attributes:
      data-id: edit-delete

This configuration defines a tip for the delete button on the forum edit page. The keys are fairly self-explanatory -

  • id is the tip id
  • plugin is the plugin type (only text is available in core)
  • label - is the heading for the tip - this is themed as a h3 in the tip
  • body - the body of the tip - markup is allowed
  • weight - tips within a tour are ordered by weight
  • attributes - these attributes are passed through to the render tip but there are few of special merit that control the placement of the tip as follows
    • data-id - use this to nominate the id of the dom element the tip targets - eg in the case of the delete button it targets the element with id 'edit-delete'. There is no need to include the leading #, internally the joyride plugin fetches the data-id attribute and passes it directly to document.getElementById() so this is a straight id field, not a jQuery selector
    • data-class - if the element you are targeting does not have an id, you can use data-class to target it. Note this does not include the leading . (period). This is parsed by the joyride plugin into a jquery selector - so you can use more complex selection rules - provided your selector starts with a classname without the leading . (period). Eg
        action-links a[href="/admin/structure/forum/add/forum"]
      
      which would be parsed by joyride into '.action-links a[href="/admin/structure/forum/add/forum"]'
    • If you omit both the .data-id and .data-class (and have this patch) - your tip will be shown as a modal instead of being targeted to an element. This is useful for a general introduction to the page/form etc.

Getting more advanced

If you find you need to do something more advanced than show a text tip, eg you need some additional parameters or logic, you will need to create tip plugin by implementing the TipPluginInterface. See the tour_test module for an example - it contains an image plugin that renders an image instead of text, taking a 'url' configuration key instead of a body.

Next steps

Tour UI

Nick and Tim Plunkett are already busy working on a Tour UI module so creating these yaml files is a simple site-building experience. This will also mean that new contributors can easily contribute tour yml files for core modules too. If you'd like to help, get in touch via the issue queue or on irc.

Writing Tour integration for core modules

We need your help to write tour integration for core modules before the code freeze. There is a meta issue. If you want to help please get in touch either via the issue queue or on irc.