|
| 1 | +import { Meta, Canvas } from "@storybook/blocks"; |
| 2 | +import * as HeaderBarStories from "./ApplicationHeaderBar.stories"; |
| 3 | + |
| 4 | +<Meta title="Patterns/Application Header Bar/Documentation" /> |
| 5 | + |
| 6 | +# Application header bar |
| 7 | + |
| 8 | +A persistent context bar shown below the GOV.UK header on every planning application page. It gives case officers instant access to the key application facts — reference, address, and proposal description — without that information taking up space inside the task form. |
| 9 | + |
| 10 | +<Canvas of={HeaderBarStories.Default} /> |
| 11 | + |
| 12 | +<hr /> |
| 13 | + |
| 14 | +## Why this pattern exists |
| 15 | + |
| 16 | +BOPS originally showed the full application details — reference, address, description, and more — at the top of every individual task page. This created two problems: |
| 17 | + |
| 18 | +1. **Officers had to scroll past the details to reach the task.** On every page, they saw the same block of information before they could do anything useful. |
| 19 | + |
| 20 | +2. **The details were duplicated everywhere.** Each task page maintained its own copy of the same information, making it harder to maintain consistency. |
| 21 | + |
| 22 | +The team extracted this information into a single, dedicated bar that sits persistently above the task content. Officers always know which application they're working on without losing any task space. |
| 23 | + |
| 24 | +<hr /> |
| 25 | + |
| 26 | +## When to use this pattern |
| 27 | + |
| 28 | +Use the application header bar when: |
| 29 | + |
| 30 | +- you are displaying a **task or form that belongs to a specific planning application** |
| 31 | +- officers will be moving between multiple tasks within the same application and need **persistent context** about which case they're on |
| 32 | +- the page layout already has a GOV.UK header at the top and you need an **application-specific second tier** below it |
| 33 | + |
| 34 | +## When not to use this pattern |
| 35 | + |
| 36 | +Do not use the application header bar when: |
| 37 | + |
| 38 | +- the page is **not associated with a specific application** (for example, a dashboard, search results, or admin pages) |
| 39 | +- you are showing a **public-facing** page — this pattern is designed for back-office case officers, not applicants |
| 40 | +- the application context is already prominent elsewhere on the page (for example, a full application detail page) |
| 41 | + |
| 42 | +<hr /> |
| 43 | + |
| 44 | +## How it works |
| 45 | + |
| 46 | +### Three zones |
| 47 | + |
| 48 | +The bar is divided into three horizontal zones: |
| 49 | + |
| 50 | +1. **Reference and address (left)** — the application reference number, shown in bold and linked to the application overview, followed by the full site address. These are always visible and cannot be hidden. |
| 51 | + |
| 52 | +2. **Description toggle (centre)** — a "Show proposal description" button that expands a panel below the bar revealing the full proposal description. This lets officers check the description text at any point without navigating away. The button toggles between "Show proposal description" and "Hide proposal description". |
| 53 | + |
| 54 | +3. **Application information link (right)** — a link to the full Application Information page, which opens in a new tab. This link can be hidden in specific contexts where it would be redundant or inappropriate (for example, when the officer is already on the application information page). |
| 55 | + |
| 56 | +### Description collapsed (default) |
| 57 | + |
| 58 | +This is the state officers see on first load. The proposal description is hidden; the bar shows only the reference, address, toggle button, and link. |
| 59 | + |
| 60 | +<Canvas of={HeaderBarStories.Default} /> |
| 61 | + |
| 62 | +### Description expanded |
| 63 | + |
| 64 | +When an officer clicks "Show proposal description", a panel drops below the bar with the full proposal text. This is rendered as a separate story since the Stimulus.js toggle controller is not active in Storybook. |
| 65 | + |
| 66 | +<Canvas of={HeaderBarStories.DescriptionExpanded} /> |
| 67 | + |
| 68 | +### Without application information link |
| 69 | + |
| 70 | +In some contexts the application information link is suppressed — for example, when the officer is already viewing the application information page. The right zone is empty and the bar remains functional. |
| 71 | + |
| 72 | +<Canvas of={HeaderBarStories.WithoutApplicationInfoLink} /> |
| 73 | + |
| 74 | +### In context |
| 75 | + |
| 76 | +The bar sits directly below the black GOV.UK header, forming a two-tier header stack above the task content. Both tiers are sticky — they remain pinned at the top of the viewport as the officer scrolls through long task forms. |
| 77 | + |
| 78 | +<Canvas of={HeaderBarStories.InContext} /> |
| 79 | + |
| 80 | +### Sticky behaviour |
| 81 | + |
| 82 | +The header bar uses `.bops-headers--sticky` on its wrapper element, which applies `position: sticky; top: 0; z-index: 2000`. This keeps both the GOV.UK header and the application bar visible at all times. The sticky behaviour is disabled when the sidebar navigation pattern is active (they don't coexist). |
| 83 | + |
| 84 | +### Responsive behaviour |
| 85 | + |
| 86 | +On screens narrower than 768px, the bar stacks vertically — each item appears on its own line. The vertical dividers between items are hidden on mobile. The primary use case is desktop. |
| 87 | + |
| 88 | +<hr /> |
| 89 | + |
| 90 | +## Implementation |
| 91 | + |
| 92 | +### HTML structure |
| 93 | + |
| 94 | +```html |
| 95 | +<div class="bops-header-bar" |
| 96 | + data-controller="toggle" |
| 97 | + data-toggle-class-name-value="govuk-!-display-none" |
| 98 | + data-toggle-condensed-text-value="Show proposal description" |
| 99 | + data-toggle-expanded-text-value="Hide proposal description"> |
| 100 | + |
| 101 | + <ul class="bops-header-bar__list"> |
| 102 | + <!-- Left: reference and address --> |
| 103 | + <li class="bops-header-bar__item"> |
| 104 | + <strong><a href="/planning_applications/123" class="govuk-link govuk-link--no-visited-state">BPS-24-00345-HAPP</a></strong> |
| 105 | + </li> |
| 106 | + <li class="bops-header-bar__item">12 Elm Grove, London, SE15 5DE</li> |
| 107 | + |
| 108 | + <!-- Centre: description toggle button --> |
| 109 | + <li class="bops-header-bar__item--toggle"> |
| 110 | + <button |
| 111 | + type="button" |
| 112 | + class="button-as-link govuk-link govuk-link--no-visited-state" |
| 113 | + data-toggle-target="button" |
| 114 | + data-action="toggle#click" |
| 115 | + aria-expanded="false"> |
| 116 | + Show proposal description |
| 117 | + </button> |
| 118 | + </li> |
| 119 | + |
| 120 | + <!-- Right: application information link --> |
| 121 | + <div class="bops-header-bar__list--right"> |
| 122 | + <li class="bops-header-bar__item"> |
| 123 | + <a href="/planning_applications/123/information" class="govuk-link govuk-link--no-visited-state" target="_blank" rel="noopener noreferrer"> |
| 124 | + Application information |
| 125 | + </a> |
| 126 | + </li> |
| 127 | + </div> |
| 128 | + </ul> |
| 129 | + |
| 130 | + <!-- Description panel (hidden by default) --> |
| 131 | + <div class="bops-header-bar__toggle-panel govuk-!-display-none" |
| 132 | + data-toggle-target="content" |
| 133 | + aria-live="polite"> |
| 134 | + <p class="govuk-!-margin-0">Proposed two-storey rear extension...</p> |
| 135 | + </div> |
| 136 | +</div> |
| 137 | +``` |
| 138 | + |
| 139 | +### CSS classes |
| 140 | + |
| 141 | +| Class | Purpose | |
| 142 | +|-------|---------| |
| 143 | +| `.bops-header-bar` | Bar wrapper — light blue background, bottom border, flex layout | |
| 144 | +| `.bops-header-bar__list` | Flex row containing all left items and the toggle button | |
| 145 | +| `.bops-header-bar__item` | Individual item — inline flex, padded, with a left border separator | |
| 146 | +| `.bops-header-bar__item--toggle` | Wrapper for the description toggle button | |
| 147 | +| `.bops-header-bar__list--right` | Right-aligned group for the application information link | |
| 148 | +| `.bops-header-bar__toggle-panel` | Collapsible description panel below the bar | |
| 149 | +| `.bops-headers--sticky` | Applied to the outer wrapper to pin both headers at the top of the viewport | |
| 150 | + |
| 151 | +### Stimulus controller |
| 152 | + |
| 153 | +The description toggle is powered by the `toggle` Stimulus controller. The controller reads three data attributes from the container element: |
| 154 | + |
| 155 | +| Attribute | Value | Purpose | |
| 156 | +|-----------|-------|---------| |
| 157 | +| `data-controller` | `"toggle"` | Activates the controller on this element | |
| 158 | +| `data-toggle-class-name-value` | `"govuk-!-display-none"` | CSS class added/removed to show/hide the panel | |
| 159 | +| `data-toggle-condensed-text-value` | `"Show proposal description"` | Button label when panel is hidden | |
| 160 | +| `data-toggle-expanded-text-value` | `"Hide proposal description"` | Button label when panel is visible | |
| 161 | + |
| 162 | +The button uses `data-toggle-target="button"` and the panel uses `data-toggle-target="content"`. |
| 163 | + |
| 164 | +### Ruby (ViewComponent) |
| 165 | + |
| 166 | +```ruby |
| 167 | +render BopsCore::HeaderBarComponent.new( |
| 168 | + left: [ |
| 169 | + { text: tag.strong(govuk_link_to(planning_application.reference, planning_application_path(planning_application), no_visited_state: true)) }, |
| 170 | + { text: planning_application.full_address } |
| 171 | + ], |
| 172 | + right: [ |
| 173 | + { label: "Application information", href: planning_application_information_path(planning_application) } |
| 174 | + ], |
| 175 | + toggle: { |
| 176 | + condensed_text: "Show proposal description", |
| 177 | + expanded_text: "Hide proposal description", |
| 178 | + content: content_tag(:p, planning_application.description, class: "govuk-!-margin-0") |
| 179 | + } |
| 180 | +) |
| 181 | +``` |
0 commit comments