|
| 1 | +--- |
| 2 | +title: Finishing touches for forms |
| 3 | +date: 2025-01-05 |
| 4 | +description: Some tips on making sure forms written in Ruby on Rails are accessible |
| 5 | +author: |
| 6 | + name: Peter Yates |
| 7 | + url: https://github.com/peteryates |
| 8 | +image: |
| 9 | + src: /assets/posts/finishing-touches-for-forms/illustration.png |
| 10 | + alt: Illustration of somebody straightening a picture frame on a wall which says ‘form sweet form’. |
| 11 | + opengraphImage: true |
| 12 | +--- |
| 13 | + |
| 14 | +The [GOV.UK formbuilder](https://govuk-form-builder.netlify.app) makes it easy for Ruby on Rails developers to build forms that follow the rules set out in the [GOV.UK Design System](https://design-system.service.gov.uk/). |
| 15 | + |
| 16 | +It generates the correct HTML, provides a nice API for customising the fields to meet the needs of your service and takes care of clearly displaying any error messages. |
| 17 | + |
| 18 | +There are, however, some suggestions in the Design System guidance that go beyond the remit of the form builder. |
| 19 | + |
| 20 | +They are often forgotten, we'll go over some simple ways to solve them here. |
| 21 | + |
| 22 | +## Positioning the error summary at the top of the page |
| 23 | + |
| 24 | +In addition to making it clear that something is wrong, we also need to tell the user exactly what happened and how to fix it. |
| 25 | + |
| 26 | +[The guidance says](https://design-system.service.gov.uk/components/error-summary#where-to-put-the-error-summary): |
| 27 | + |
| 28 | +> Put the error summary at the top of the main container. If your page includes breadcrumbs or a back link, place it below these, but above the `<h1>`. |
| 29 | +
|
| 30 | +This is problematic because the form often isn't the first thing on the page but the form builder needs to be responsible for rendering the error summary to ensure the links and targets are consistent. |
| 31 | + |
| 32 | +We could solve the problem by wrapping the whole page in a `<form>` element, which would allow us to place the error summary wherever we like, but it's a hack. It isn't semantically correct and is likely to lead to other problems. |
| 33 | + |
| 34 | +Thankfully, Rails has our backs. We can use [`content_for`](https://guides.rubyonrails.org/layouts_and_rendering.html#using-the-content-for-method) to insert content into a named block in our layout. If we add a named block called `top_of_main` at the top of our `<main>` element, like this: |
| 35 | + |
| 36 | +```html |
| 37 | +<div class="govuk-width-container"> |
| 38 | + <main class="govuk-main-wrapper" id="main-content"> |
| 39 | + <%= yield :top_of_main %> |
| 40 | + <h1 class="govuk-heading-xl">Default page template</h1> |
| 41 | + |
| 42 | + <!-- the rest of our page --> |
| 43 | +``` |
| 44 | + |
| 45 | +We can send our error summary from the form to it, like this: |
| 46 | + |
| 47 | +```html |
| 48 | +<%= form_with(model: @object, url: some_path) do |form| %> |
| 49 | + <%= content_for(:top_of_main) { form.govuk_error_summary } %> |
| 50 | + |
| 51 | + <%= form.govuk_text_field(:how_many_juggling_items) %> |
| 52 | +<% end %> |
| 53 | +``` |
| 54 | + |
| 55 | +And now our error summary is in the right spot. |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +## Prefixing the page title with 'Error:' |
| 60 | + |
| 61 | +When a form submission results in a validation failure we need to make it clear to the user that something is wrong. |
| 62 | + |
| 63 | +[The guidance says](https://design-system.service.gov.uk/patterns/validation/#how-to-tell-the-user-about-validation-errors): |
| 64 | + |
| 65 | +> Add ‘Error: ’ to the beginning of the page `<title>` so screen readers read it out as soon as possible |
| 66 | +
|
| 67 | +This is a little tricky because we need some logic that can determine whether there's an error on the page, and we set the `<title>` in the document `<head>` before we've rendered the form in the `<body>`. |
| 68 | + |
| 69 | +We can use `content_for` here too to place the title where we need it. |
| 70 | + |
| 71 | +```html |
| 72 | +<head> |
| 73 | + <%= tag.title(yield(:page_title)) %> |
| 74 | + <!-- other head content --> |
| 75 | +</head> |
| 76 | +``` |
| 77 | + |
| 78 | +We can use the [GOV.UK Components](https://govuk-components.netlify.app/) [title with error prefix helper](https://govuk-components.netlify.app/helpers/title-with-error-prefix/) to add the 'Error:' prefix whenever `@object.errors.any?` is `true`, and pass the resulting string into the `page_title` content: |
| 79 | + |
| 80 | +```ruby |
| 81 | +<% |
| 82 | + content_for(:page_title) do |
| 83 | + title_with_error_prefix( |
| 84 | + "How many implements can you juggle with?", |
| 85 | + error: @object.errors.any? |
| 86 | + ) |
| 87 | + end |
| 88 | +%> |
| 89 | +``` |
| 90 | +
|
| 91 | +## Making sure error links to checkboxes and radios work |
| 92 | +
|
| 93 | +The form builder comes with two ways to build lists of check boxes and radio buttons. |
| 94 | +
|
| 95 | +The simplest is to use `#govuk_collection_check_boxes` and `#govuk_collection_radio_buttons`, which mimic the behaviour of [their Rails counterparts](https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-collection_checkboxes). |
| 96 | +
|
| 97 | +You just pass in the list of options and the form builder will render them. This works for simple lists. |
| 98 | +
|
| 99 | +Sometimes, however, the list needs to be customised a little further. For example we might need [a conditionally revealed questions](https://design-system.service.gov.uk/components/checkboxes#conditionally-revealing-a-related-question) or [a divider](https://design-system.service.gov.uk/components/checkboxes/#add-an-option-for-none). |
| 100 | +
|
| 101 | +To support this the form builder comes with the more powerful `#govuk_check_boxes_fieldset` and `#govuk_radio_buttons_fieldset` methods which let the developer build the form field by field. |
| 102 | +
|
| 103 | +This power comes at a cost. For example you could write something like this. Here the `delete` will only be shown to admins: |
| 104 | +
|
| 105 | +```ruby |
| 106 | +f.govuk_radio_buttons_fieldset(:close_ticket)) do |
| 107 | +
|
| 108 | + if @user.admin? |
| 109 | + f.govuk_radio_button( |
| 110 | + :close_ticket, |
| 111 | + 'delete' |
| 112 | + ) |
| 113 | + end |
| 114 | +
|
| 115 | + f.govuk_radio_button( |
| 116 | + :close_ticket, |
| 117 | + 'archive' |
| 118 | + ) |
| 119 | +
|
| 120 | + # the rest of the options |
| 121 | +end |
| 122 | +``` |
| 123 | +
|
| 124 | +This would make it impossible for the error summary to accurately link to the first check box or radio button without repeating the logic --- an approach that's going to lead to bugs when it's updated in one place but not the other. |
| 125 | +
|
| 126 | +Instead, we have to help the error summary by 'marking' the field we want the error summary to link to with `link_errors: true`. This overrides the ID generation so the link in the error summary will match it. |
| 127 | +
|
| 128 | +```ruby |
| 129 | +f.govuk_radio_buttons_fieldset(:close_ticket)) do |
| 130 | +
|
| 131 | + if @user.admin? |
| 132 | + f.govuk_radio_button( |
| 133 | + :close_ticket, |
| 134 | + 'delete', |
| 135 | + link_errors: true |
| 136 | + ) |
| 137 | + end |
| 138 | +
|
| 139 | + f.govuk_radio_button( |
| 140 | + :close_ticket, |
| 141 | + 'archive', |
| 142 | + link_errors: @user.regular_user? |
| 143 | + ) |
| 144 | +
|
| 145 | + # the rest of the options |
| 146 | +end |
| 147 | +``` |
| 148 | +
|
| 149 | +Now when there's a validation error, regardless of whether the user is an admin or not the error summary will link to the first option. It's a good idea to write some tests to ensure future changes don't affect it. |
| 150 | +
|
| 151 | +--- |
| 152 | +
|
| 153 | +These finishing touches will help make sure your forms are usable and accessible. |
| 154 | +
|
| 155 | +If you have any feedback or suggestions of other form features commonly missed, [let us know](https://github.com/x-govuk/govuk-form-builder/issues/new). |
0 commit comments