name: Step by step navigation description: A series of expanding/collapsing steps designed to navigate a user through a process body: | Step by step navigations show a sequence of steps towards a specific goal, such as 'learning to drive'. Each step can contain one or more links to pages. User research suggested that each step should be collapsed by default so that users are not overwhelmed with information. If JavaScript is disabled the step by step navigation expands fully. All of the functionality (including the icons and aria attributes) are added using JavaScript. This component is based on the accordion component in collections, but has been altered. This is for two reasons. - step by step navigations are needed throughout GOV.UK and extending the accordion to be this component would require it to be moved out of that application, which would break integration testing - creating a new component allows further iteration without impacting the accordion Background information relating to the testing and research behind this component can be found on the [Modelling Services wiki](https://gov-uk.atlassian.net/wiki/spaces/MS/pages) in Q2 to Q4, 2017/18. accessibility_criteria: | The step by step navigation must: - indicate to users that each step can be expanded and collapsed - inform the user when a step has been expanded or collapsed - be usable with a keyboard - allow users to show or hide all steps at once - inform the user which step a link belongs to - inform the user which step the current page is in - be readable when only the text of the page is zoomed - achieved for the numbers and logic elements by their text being wrapped in several elements that give them a white background and ensure the text when zoomed expands to the left, not to the right, where it would obscure the step titles The show/hide all button only needs to list the first panel `id` in the `aria-controls` attribute, rather than all of them. Step headings must use a button element: - so that steps can be toggled with the space and enter keys - so that steps can't be opened in a new tab or window When JavaScript is unavailable the component must: - be fully expanded - not be marked as expandable shared_accessibility_criteria: - link examples: default: data: steps: [ { title: "Do a thing", contents: [ { type: 'paragraph', text: 'This is a paragraph of text.' }, { type: 'paragraph', text: 'This is also a paragraph of text that intentionally contains lots of words in order to fill the width of the page successfully to check layout and so forth.' } ] }, { title: "Do another thing", logic: "and", contents: [ { type: 'paragraph', text: 'Some more text.' }, ] }, { title: "Do something else", contents: [ { type: 'paragraph', text: 'This is yet another paragraph.' } ] } ] with_unique_tracking: description: | In order to identify the step by step navigation the component is part of, we need to track a unique ID of the navigation in Google Analytics. This ID should be the same across all pages that are linked from and are part of that navigation. Its value is included in any tracking events, specifically in dimension96. It refers to the ID of the step nav page. This includes show/hide all, show/hide step and any link clicks. data: tracking_id: 'this-is-the-tracking-id' steps: [ { title: 'Unique tracking id', contents: [ { type: 'list', contents: [ { href: '#', text: 'This is a link' } ] } ] } ] with_a_different_heading_level: description: Steps have a `h2` by default, but this can be changed. The heading level does not change any styling. data: heading_level: 3 steps: [ { title: 'This is a heading 3', contents: [ { type: 'paragraph', text: 'This is yet another paragraph.' } ] }, { title: 'This is also a heading 3', contents: [ { type: 'paragraph', text: 'This is yet another paragraph.' } ] } ] open_a_step_by_default: description: Pass the index of the step to open by default. This is the nth step in the list, regardless of the number shown next to the step. Note that in the example below, the third step is open by default, not the step numbered '3'. data: show_step: 3 steps: [ { title: 'Closed by default', contents: [ { type: 'paragraph', text: 'Well, open now, obviously.' } ] }, { title: 'Also closed by default', contents: [ { type: 'paragraph', text: 'Hello.' } ] }, { title: 'Open by default', logic: "and", contents: [ { type: 'paragraph', text: 'Hello!' } ] }, { title: 'And again closed by default', contents: [ { type: 'paragraph', text: 'Goodbye...' } ] } ] remember_last_opened_step: description: | If steps are opened their IDs are added to an array in `sessionStorage`, so that if the user returns to this page in the same session, those steps will be opened on page load. This option can technically be used together with the option to open a step by default, but this is not recommended as it could confuse users. data: remember_last_step: true steps: [ { title: 'Remember this', contents: [ { type: 'paragraph', text: 'Hello!' } ] }, { title: 'Or this', contents: [ { type: 'paragraph', text: 'Hello!' } ] } ] with_links: description: | Links can have the following attributes: - style, an attribute on the parent list that controls its appearance. Defaults to optional but can be set to 'choice' (adds indent and bullets to the list) - `context`, an optional text field to show some extra information after the link, usually a monetary value - `active`, whether to make the link highlighted (to indicate the current page) - `highlight_step`, a general option to highlight the step. Usually the active link would be in the active step, but in some circumstances there might be an active step but no highlighted link. data: highlight_step: 2 show_step: 2 steps: [ { title: 'Links and paragraphs', contents: [ { type: 'paragraph', text: 'These links represent tasks that are required.' }, { type: 'paragraph', text: 'Spacing between a paragraph and a list of links should be smaller than the spacing between two paragraphs, in order to visually connect the two.' }, { type: 'list', contents: [ { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Obtain a driving licence prior to driving a car' }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Acquire food before attempting to cook', context: '1p to £20' }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Maintain contact with the ground at all times' } ] }, { type: 'paragraph', text: 'These links represent a choice:' }, { type: 'list', style: 'choice', contents: [ { href: '/component-guide/step_by_step_navigation/', text: 'Leave school at sixteen' }, { href: '/component-guide/step_by_step_navigation/', text: 'Continue into higher education', context: '£9,500' } ] } ] }, { title: 'Active step and links', contents: [ { type: 'list', contents: [ { href: '/component-guide/step_by_step_navigation/', text: 'Check the reasons' }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Make the decisions based on available data and the reasonable assumptions that are possible at the time', context: '1p to £20', active: true }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Do the things' } ] }, ] } ] or_then: description: Some of the more complex things in a step by step navigation require an option for laying out links in a clear structure. If a link in a list is not given a href, only the text is displayed, allowing for structures like the one shown below. data: show_step: 1 steps: [ { title: "Get a court to decide your child arrangements", contents: [ { type: 'paragraph', text: "You can only apply for a court to make a decision if you've tried mediation or your family are at risk, for example domestic abuse." }, { type: 'list', contents: [ { href: 'http://solicitors.lawsociety.org.uk/', text: 'Hire a lawyer to represent you', context: '£110 to £410 per hour' }, { text: 'or', }, { href: 'http://localhost:3000/represent-yourself-in-court', text: 'Represent yourself in court' } ] }, { type: 'paragraph', text: 'then' }, { type: 'list', contents: [ { href: '/looking-after-children-divorce/apply-for-court-order', text: 'Apply for a court order', context: '£215' }, { href: 'https://courttribunalfinder.service.gov.uk/search/', text: 'Find the right court to send your application' }, { href: '/get-help-with-court-fees', text: 'Get help with court fees' } ] }, { type: 'paragraph', text: "The court will send you a date for your first hearing 4 to 6 weeks after your application. You'll be told when and where your hearing will take place." } ] } ] solve_the_double_dot_problem: description: | As shown, options can be passed to the component to highlight one step, expand one step by default, and highlight multiple links. These options should only be used when the component is in the sidebar. The step that is highlighted and expanded will be referred to as the active step (even though they are separate options, it is assumed that they will normally be applied to the same step). If a link is in a step by step navigation more than once and the user is on that page, the backend doesn't know which link to highlight, so it highlights both of them. JavaScript is included in the component to solve this. It uses `sessionStorage` to capture the data-position attribute of non-external links when clicked, and then uses this value to decide which link to highlight after the new page loads. It uses the value of the `tracking_id` option to uniquely identify the current step nav in the session (if this is not passed to the component this may result in other step navs having the wrong link highlighted). If a user has not clicked a link (i.e. has visited the page without first clicking on a step by step navigation) only the first duplicate link will be highlighted. If that link is not in the active step, the JavaScript makes the highlighted link's parent the active step. If there is no active step, the first active link will be highlighted (but there should always be an active step). The example below will show all non-external links highlighted if JavaScript is disabled. In the real world no more than two or three links are likely to be highlighted at once. Changes to the active step and highlighted link are also applied on click, if the user clicks one of the other links that is to the same page (they are amended by the component to be jump links to the top of the page). Open this example using the 'preview' link and try clicking on the 'internal' links to see how it behaves. data: highlight_step: 2 show_step: 2 tracking_id: "example-id" steps: [ { title: "The active step", contents: [ { type: 'list', contents: [ { href: 'http://google.com', text: 'External link not set to active' }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Internal link set to active', active: true }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Internal link set to active', active: true }, { href: 'http://google.com', text: 'External link not set to active' } ] } ] }, { title: "Not the active step", contents: [ { type: 'list', contents: [ { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Internal link set to active - in the active step, should be set by default until another link is clicked', active: true }, { href: '/component-guide/step_by_step_navigation/with_links/preview', text: 'Internal link set to active', active: true }, { href: 'http://google.com', text: 'External link not set to active' } ] } ] } ] tracking_the_user_journey_through_the_query_string: description: | When the step by step pattern was first introduced, one of the problems we noticed was that if a content page is in more than one step by step journey, it wasn't practical to expand all of the step by steps, as the sidebar became very long. We solved this problem in a few ways: 1. Only show a list of titles for the step by steps that the content page is part of. 2. Only display this list if the content item is part of fewer than 5 step by step journeys 3. Allow content designers to optionally hide a step by step journey on content pages. The consequence of this is that if users land on any of these pages, they lose sight of the journey they are on, and which step of the journey they have reached (the black dot). The solution is to track the step by step journey the user is on. This has been achieved by adding a query string to all of the internal links in the step by step that contains the `content_id` of the step by step. For example, if the step by step contains a link to [/check-uk-visa](/check-uk-visa), the component will render the link as [/check-uk-visa?step-by-step-nav=d8f3c2e0-d544-4664-9616-ab71323e4d18](/check-uk-visa?step-by-step-nav=d8f3c2e0-d544-4664-9616-ab71323e4d18) As long as the user follows the links in the step by step (and not the inline links in the content), the component will know which step by step journey the user is on. If the content item is part of multiple step by steps, the component fetches the `content_id` of the `active` step by step from the query string and expands that one. The other step by steps that the content item is part of are displayed as a list of titles under an "Also part of" heading. Additionally, if content designers have chosen to "hide" a step by step on a content page, but we can see that the user is following a step by step journey (the `content_id` of the step by step is in the querystring), the step by step will still be displayed and expanded in the sidebar, and the other step by steps that the content item is part of, but content designers have chosen to hide will also be displayed as a list of titles under an "Also part of heading". In both cases, we never show more than 5 step by steps in the list. If the user lands on the content page "cold", i.e. from Google, and not from the step by step sidebar (or overview page), the original rules for the displaying the step by step sidebar will apply. The rules are as follows: 1. Content item is part of one step by step Expand the step by step in the sidebar 2. Content item is part of multiple step by steps Show a list of step by steps under the "Part of" heading 3. Step by step is marked as "hidden" for the content page Do not display the step by step Changes to the rules when the user is on a step by step journey (the query string is in the url): 1. Content item is part of one step by step No change, expand the step by step in the sidebar 2. Content item is part of multiple step by steps Expand the step by step in the querystring Show a list of the other step by steps under an "Also part of" heading, if the content item is part of less than 5 step by step journeys 3. Step by step is marked as "hidden" for the content page Expand the step by step in the querystring Show a list of the other step by steps (including other step by steps that have been marked as "hidden") under an "Also part of" heading, if the content item is part of less than 5 step by step journeys Changes to the rules when there is a secondary step by step linked: 1. Content item is part of one step by step No change. Expand the step by step in the sidebar. 2. Content item is part of multiple step by steps and no secondary step by step No change. Will show a list of primary step by steps under the "Part of" heading. Secondary step by steps will not be shown. 3. User is on a step by step journey (the query string is in the url) No change. Active step by step will be expanded. 4. Content item is part of multiple primary step by steps and a single secondary step by step No change. Show "Part of" step by steps list. Secondary step by step won't be shown. 5. Content item is part of multiple step by steps and multiple secondary step by steps No change. Show "Part of" step by step list. Secondary step by step won't be shown. 6. Step by step is marked as "hidden" for the content page No change. "Also part of" component will only show when an active step by step is shown. 7. Content item is part of one secondary step by step and no other step by steps are linked. Show secondary step by step expanded. 8. Content item is part of multiple secondary step by steps and no other step by steps are linked. Show secondary step by step list in "Part of" component. data: tracking_id: "this-is-the-content-id" steps: [ { title: "With query string", contents: [ { type: 'list', contents: [ { href: '/component-guide/step_by_step_navigation/with_links/preview?step-by-step-nav=this-is-the-content-id', text: 'This is an internal link with a query string', }, { href: 'http://google.com', text: 'This is an external link without a query string' } ] } ] } ] with_optional_steps: description: | Steps can be optional. This is controlled by two parameters, 'optional' and 'logic'. - Optional steps used to have a dotted line down the side but this was removed as it confused users. In some cases we may still need to record if a step is optional, so if this parameter is applied, Google Analytics tracking events will have 'optional' appended to the end of the eventLabel. - Logic will change the number next to a step to the word 'and' or the word 'or'. If a step is optional this should be conveyed by the text within that step. data: steps: [ { title: "Drive to work", contents: [ { type: paragraph, text: 'If you do not have a car, you will need to choose an alternative.' } ] }, { title: "Walk to work", optional: true, logic: 'or', contents: [ { type: 'paragraph', text: 'Walking is healthy but may not be practical where large distances are concerned.' } ] }, { title: "Get public transport to work", optional: true, logic: 'or', contents: [ { type: 'paragraph', text: 'Public transport includes buses, trains and boats.' } ] }, { title: "Do work", contents: [ { type: 'paragraph', text: 'Once you have reached your destination you should be able to start work.' } ] }, { title: "Get paid", logic: 'and', contents: [ { type: 'paragraph', text: 'Your employer should pay you for hours worked.' } ] } ] test_all_the_things: description: This component is very complicated so here is an example containing as many of the options available as possible. data: step_nav_url: "/learn-to-setup-standup" highlight_step: 3 steps: [ { title: 'Get the TV ready', contents: [ { type: 'paragraph', text: 'Configure the television so it is ready for the standup. You will also need a laptop.' }, { type: 'list', contents: [ { href: 'https://en.wikipedia.org/wiki/HDMI', text: 'Remove the Chromebit from HDMI 1 on the TV' } ] } ] }, { title: 'Plug everything in', logic: "and", contents: [ { type: 'paragraph', text: 'Connect the relevant cables between the various devices.' }, { type: 'list', contents: [ { href: 'https://www.google.co.uk/', text: 'Run the HDMI - MINI DVI cable from the TV to the facilitators laptop' }, { href: 'https://www.jabra.co.uk/', text: 'Plug the Jabra into the facilitators laptop' } ] } ] }, { title: 'Configure the catchbox', contents: [ { type: 'paragraph', text: 'These steps are required.' }, { type: 'list', contents: [ { href: 'http://www.google.com', text: 'Ensure the catchbox transmitter is plugged in at the mains wall' }, { href: 'http://www.google.com', text: 'Turn on the transmitter and wait for the switch to blink green' }, { href: 'http://www.google.com', text: 'Plug the transmitter USB cable into the facilitators laptop', active: true }, { href: 'http://www.google.com', text: 'Twist and pull the black piece of foam out of the catchbox' }, { href: 'http://www.google.com', text: 'Turn on the catchbox and wait for the LED to turn green' }, { href: 'http://www.google.com', text: 'Wait for the transmitter light to turn solid green' } ] }, { type: 'substep', optional: true, contents: [ { type: 'heading', text: 'Optional steps' }, { type: 'paragraph', text: 'These steps are not required.' }, { type: 'list', style: 'choice', contents: [ { href: 'https://www.google.co.uk/', text: "Get annoyed when it doesn't work" }, { href: 'http://www.google.com', text: 'Try to find someone else who knows how to do it', context: '1 to 10 minutes' } ] } ] } ] }, { title: 'Join and configure the standup', show_help_link: true, contents: [ { type: 'paragraph', text: 'Join the hangout and present to those on it.' }, { type: 'list', contents: [ { href: 'http://www.google.com', text: 'Connect to standup hangout via the calendar invite' }, { text: 'or' }, { href: 'http://www.google.com', text: 'Connect to standup hangout via the link in the team slack' } ] }, { type: 'paragraph', text: 'then' }, { type: 'list', contents: [ { href: 'http://www.google.com', text: 'Click the three dots in the bottom hand corner to open settings' }, { href: 'http://www.google.com', text: 'Set speaker to "Jabra"' }, { href: 'http://www.google.com', text: 'Set microphone to "C Media USB"' }, { href: 'http://www.google.com', text: 'Click "present to meeting"' }, ] } ] }, { title: 'Clear up', contents: [ { type: 'paragraph', text: 'Disconnect from the hangout and disconnect any cables.' }, { type: 'paragraph', text: 'Most importantly, remember to switch off the catchbox to save the battery.' } ] }, { title: 'Get someone else to clear up', logic: 'or', optional: true, contents: [ { type: 'paragraph', text: 'Schedule another meeting for right after the standup and force someone else to sort everything out.' } ] } ] small: description: Designed to fit in the sidebar of a page. Note that the small version of the component should not become smaller on mobile, and the large version on mobile should be the same size as the small version. This example is a copy of the one above for comparison. data: small: true remember_last_step: true step_nav_url: "/learn-to-setup-standup" highlight_step: 3 steps: [ { title: 'Get the TV ready (small)', contents: [ { type: 'paragraph', text: 'Configure the television so it is ready for the standup. You will also need a laptop.' }, { type: 'list', contents: [ { href: 'https://en.wikipedia.org/wiki/HDMI', text: 'Remove the Chromebit from HDMI 1 on the TV' } ] } ] }, { title: 'Plug everything in (small)', logic: "and", contents: [ { type: 'paragraph', text: 'Connect the relevant cables between the various devices.' }, { type: 'list', contents: [ { href: 'https://www.google.co.uk/', text: 'Run the HDMI - MINI DVI cable from the TV to the facilitators laptop' }, { href: 'https://www.jabra.co.uk/', text: 'Plug the Jabra into the facilitators laptop' } ] } ] }, { title: 'Configure the catchbox (small)', contents: [ { type: 'paragraph', text: 'These steps are required.' }, { type: 'list', contents: [ { href: 'http://www.google.com', text: 'Ensure the catchbox transmitter is plugged in at the mains wall' }, { href: 'http://www.google.com', text: 'Turn on the transmitter and wait for the switch to blink green' }, { href: 'http://www.google.com', text: 'Plug the transmitter USB cable into the facilitators laptop', active: true }, { href: 'http://www.google.com', text: 'Twist and pull the black piece of foam out of the catchbox' }, { href: 'http://www.google.com', text: 'Turn on the catchbox and wait for the LED to turn green' }, { href: 'http://www.google.com', text: 'Wait for the transmitter light to turn solid green' } ] }, { type: 'substep', optional: true, contents: [ { type: 'heading', text: 'Optional steps' }, { type: 'paragraph', text: 'These steps are not required.' }, { type: 'list', style: 'choice', contents: [ { href: 'https://www.google.co.uk/', text: "Get annoyed when it doesn't work" }, { href: 'http://www.google.com', text: 'Try to find someone else who knows how to do it', context: '1 to 10 minutes' } ] } ] } ] }, { title: 'Join and configure the standup (small)', show_help_link: true, contents: [ { type: 'paragraph', text: 'Join the hangout and present to those on it.' }, { type: 'list', contents: [ { href: 'http://www.google.com', text: 'Connect to standup hangout via the calendar invite' }, { text: 'or' }, { href: 'http://www.google.com', text: 'Connect to standup hangout via the link in the team slack' } ] }, { type: 'paragraph', text: 'then' }, { type: 'list', contents: [ { href: 'http://www.google.com', text: 'Click the three dots in the bottom hand corner to open settings' }, { href: 'http://www.google.com', text: 'Set speaker to "Jabra"' }, { href: 'http://www.google.com', text: 'Set microphone to "C Media USB"' }, { href: 'http://www.google.com', text: 'Click "present to meeting"' }, ] } ] }, { title: 'Clear up (small)', contents: [ { type: 'paragraph', text: 'Disconnect from the hangout and disconnect any cables.' }, { type: 'paragraph', text: 'Most importantly, remember to switch off the catchbox to save the battery.' } ] }, { title: 'Get someone else to clear up (small)', logic: 'or', optional: true, contents: [ { type: 'paragraph', text: 'Schedule another meeting for right after the standup and force someone else to sort everything out.' } ] } ]