diff --git a/app/assets/javascripts/autofocus.js b/app/assets/javascripts/autofocus.js
index 002a676a2..4d3d94849 100644
--- a/app/assets/javascripts/autofocus.js
+++ b/app/assets/javascripts/autofocus.js
@@ -3,13 +3,26 @@
Modules.Autofocus = function() {
this.start = function(component) {
- var forceFocus = $(component).data('forceFocus');
+ var $component = $(component),
+ forceFocus = $component.data('forceFocus'),
+ labelText = $('label[for="' + $component.attr('id') + '"]').eq(0).text().trim(),
+ clearAriaLabel = evt => {
+ $component.removeAttr('aria-label');
+ $component.off('blur', clearAriaLabel);
+ };
// if the page loads with a scroll position, we can't assume the item to focus onload
// is still where users intend to start
if (($(window).scrollTop() > 0) && !forceFocus) { return; }
- $(component).filter('input, textarea, select').eq(0).trigger('focus');
+ // screenreaders announce the page title when a new page loads
+ // this will be lost when focus is moved to our form control so add it to the label instead
+ $component.attr('aria-label', document.title + ' - ' + labelText);
+
+ $component.filter('input, textarea, select').eq(0).trigger('focus');
+
+ // the page title prefix is only needed on page load so remove once focus has shifted
+ $component.on('blur', clearAriaLabel);
};
};
diff --git a/app/templates/components/live-search.html b/app/templates/components/live-search.html
index 4108ded7f..8c6641b60 100644
--- a/app/templates/components/live-search.html
+++ b/app/templates/components/live-search.html
@@ -1,21 +1,25 @@
-{% from "components/textbox.html" import textbox %}
-
{% macro live_search(
target_selector=None,
show=False,
form=None,
- label=None
+ label=None,
+ autofocus=False
) %}
{%- set search_label = label or form.search.label.text %}
+
+ {%- set param_extensions = {
+ "label": {"text": search_label},
+ "autocomplete": "off",
+ } %}
+
+ {% if autofocus %}
+ {% set x=param_extensions.__setitem__("attributes", {"data-module": "autofocus"}) %}
+ {% endif %}
+
{% if show %}
-
-
{% endif %}
{% endmacro %}
diff --git a/app/templates/views/broadcast/areas-with-sub-areas.html b/app/templates/views/broadcast/areas-with-sub-areas.html
index 2f1f02a7f..640f69772 100644
--- a/app/templates/views/broadcast/areas-with-sub-areas.html
+++ b/app/templates/views/broadcast/areas-with-sub-areas.html
@@ -14,7 +14,13 @@
back_link=url_for('.choose_broadcast_library', service_id=current_service.id, broadcast_message_id=broadcast_message.id),
)}}
- {{ live_search(target_selector='.file-list-item', show=show_search_form, form=search_form, label='Search by name') }}
+ {{ live_search(
+ target_selector='.file-list-item',
+ show=show_search_form,
+ form=search_form,
+ label='Search by name',
+ autofocus=True)
+ }}
{% for area in library|sort %}
diff --git a/app/templates/views/broadcast/areas.html b/app/templates/views/broadcast/areas.html
index 9ed0a38e8..f55c364cf 100644
--- a/app/templates/views/broadcast/areas.html
+++ b/app/templates/views/broadcast/areas.html
@@ -16,7 +16,13 @@
back_link=url_for('.choose_broadcast_library', service_id=current_service.id, broadcast_message_id=broadcast_message.id),
)}}
- {{ live_search(target_selector='.govuk-checkboxes__item', show=show_search_form, form=search_form, label='Search by name') }}
+ {{ live_search(
+ target_selector='.govuk-checkboxes__item',
+ show=show_search_form,
+ form=search_form,
+ label='Search by name',
+ autofocus=True)
+ }}
{% call form_wrapper() %}
{{ form.areas }}
diff --git a/app/templates/views/email-branding/select-branding.html b/app/templates/views/email-branding/select-branding.html
index eb6e8e3cc..bd584176c 100644
--- a/app/templates/views/email-branding/select-branding.html
+++ b/app/templates/views/email-branding/select-branding.html
@@ -3,14 +3,16 @@
{% from "components/live-search.html" import live_search %}
{% from "components/button/macro.njk" import govukButton %}
+{% set page_title = "Email branding" %}
+
{% block per_page_title %}
- Email branding
+ {{ page_title }}
{% endblock %}
{% block platform_admin_content %}
-
Email branding
- {{ live_search(target_selector='.email-brand', show=True, form=search_form) }}
+
{{ page_title }}
+ {{ live_search(target_selector='.email-brand', show=True, form=search_form, autofocus=True) }}
{% for brand in email_brandings %}
diff --git a/app/templates/views/letter-branding/select-letter-branding.html b/app/templates/views/letter-branding/select-letter-branding.html
index 7dfa52875..19611e4b9 100644
--- a/app/templates/views/letter-branding/select-letter-branding.html
+++ b/app/templates/views/letter-branding/select-letter-branding.html
@@ -3,14 +3,16 @@
{% from "components/live-search.html" import live_search %}
{% from "components/button/macro.njk" import govukButton %}
+{% set page_title = "Letter branding" %}
+
{% block per_page_title %}
- Letter branding
+ {{ page_title }}
{% endblock %}
{% block platform_admin_content %}
-
Letter branding
- {{ live_search(target_selector='.letter-brand', show=True, form=search_form) }}
+
{{ page_title }}
+ {{ live_search(target_selector='.letter-brand', show=True, form=search_form, autofocus=True) }}
{% for brand in letter_brandings %}
diff --git a/app/templates/views/organisations/add-nhs-local-organisation.html b/app/templates/views/organisations/add-nhs-local-organisation.html
index 7047a4660..7c53e73a4 100644
--- a/app/templates/views/organisations/add-nhs-local-organisation.html
+++ b/app/templates/views/organisations/add-nhs-local-organisation.html
@@ -6,20 +6,28 @@
{% from "components/live-search.html" import live_search %}
{% from "components/form.html" import form_wrapper %}
+{% set page_title = "Accept our data sharing and financial agreement" %}
+
{% block per_page_title %}
- Accept our data sharing and financial agreement
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
- 'Accept our data sharing and financial agreement',
+ page_title,
back_link=url_for('main.request_to_go_live', service_id=current_service.id)
) }}
{% call form_wrapper() %}
{{ form.organisations.label.text }}
- {{ live_search(target_selector='.multiple-choice', show=True, form=search_form, label='Search by name') }}
+ {{ live_search(
+ target_selector='.multiple-choice',
+ show=True,
+ form=search_form,
+ label='Search by name',
+ autofocus=True)
+ }}
{{ radios(form.organisations, hide_legend=True) }}
{{ sticky_page_footer('Continue') }}
{% endcall %}
diff --git a/app/templates/views/organisations/organisation/settings/set-email-branding.html b/app/templates/views/organisations/organisation/settings/set-email-branding.html
index f61b40fe6..6c9ef0995 100644
--- a/app/templates/views/organisations/organisation/settings/set-email-branding.html
+++ b/app/templates/views/organisations/organisation/settings/set-email-branding.html
@@ -5,14 +5,16 @@
{% from "components/live-search.html" import live_search %}
{% from "components/form.html" import form_wrapper %}
-{% block org_page_title %}
- Default email branding
+{% set page_title = "Default email branding" %}
+
+{% block per_page_title %}
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
- "Default email branding",
+ page_title,
back_link=url_for('.organisation_settings', org_id=current_org.id)
) }}
{% call form_wrapper(data_kwargs={'preview-type': 'email'}) %}
@@ -22,7 +24,13 @@
- {{ live_search(target_selector='.multiple-choice', show=True, form=search_form, label='Search branding styles by name') }}
+ {{ live_search(
+ target_selector='.multiple-choice',
+ show=True,
+ form=search_form,
+ label='Search branding styles by name',
+ autofocus=True
+ ) }}
{{ radios(form.branding_style) }}
diff --git a/app/templates/views/organisations/organisation/settings/set-letter-branding.html b/app/templates/views/organisations/organisation/settings/set-letter-branding.html
index f93673799..a612c9696 100644
--- a/app/templates/views/organisations/organisation/settings/set-letter-branding.html
+++ b/app/templates/views/organisations/organisation/settings/set-letter-branding.html
@@ -5,14 +5,16 @@
{% from "components/page-header.html" import page_header %}
{% from "components/form.html" import form_wrapper %}
-{% block org_page_title %}
- Default letter branding
+{% set page_title = "Default letter branding" %}
+
+{% block per_page_title %}
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
- "Default letter branding",
+ page_title,
back_link=url_for('.organisation_settings', org_id=current_org.id)
) }}
{% call form_wrapper(data_kwargs={'preview-type': 'letter'}) %}
@@ -22,7 +24,13 @@
- {{ live_search(target_selector='.multiple-choice', show=True, form=search_form, label='Search by name') }}
+ {{ live_search(
+ target_selector='.multiple-choice',
+ show=True,
+ form=search_form,
+ label='Search by name',
+ autofocus=True
+ ) }}
{{ radios(form.branding_style, hide_legend=True) }}
diff --git a/app/templates/views/service-settings/link-service-to-organisation.html b/app/templates/views/service-settings/link-service-to-organisation.html
index 975d1d4d1..2b50edee5 100644
--- a/app/templates/views/service-settings/link-service-to-organisation.html
+++ b/app/templates/views/service-settings/link-service-to-organisation.html
@@ -5,17 +5,24 @@
{% from "components/live-search.html" import live_search %}
{% from "components/form.html" import form_wrapper %}
+{% set page_title = "Link service to organisation" %}
+
{% block service_page_title %}
- Link service to organisation
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
- 'Link service to organisation',
+ page_title,
back_link=url_for('.service_settings', service_id=current_service.id)
) }}
- {{ live_search(target_selector='.multiple-choice', show=True, form=search_form, label='Search by name') }}
+ {{ live_search(
+ target_selector='.multiple-choice',
+ show=True, form=search_form,
+ label='Search by name',
+ autofocus=True
+ ) }}
{% call form_wrapper() %}
{% if has_organisations %}
{{ radios(form.organisations) }}
diff --git a/app/templates/views/service-settings/set-email-branding.html b/app/templates/views/service-settings/set-email-branding.html
index 4de59364d..e8baba461 100644
--- a/app/templates/views/service-settings/set-email-branding.html
+++ b/app/templates/views/service-settings/set-email-branding.html
@@ -5,14 +5,16 @@
{% from "components/live-search.html" import live_search %}
{% from "components/form.html" import form_wrapper %}
+{% set page_title = "Set email branding" %}
+
{% block service_page_title %}
- Set email branding
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
- 'Set email branding',
+ page_title,
back_link=url_for('.service_settings', service_id=current_service.id)
) }}
{% call form_wrapper(data_kwargs={'preview-type': 'email'}) %}
@@ -22,7 +24,13 @@
- {{ live_search(target_selector='.multiple-choice', show=True, form=search_form, label='Search branding styles by name') }}
+ {{ live_search(
+ target_selector='.multiple-choice',
+ show=True,
+ form=search_form,
+ label='Search branding styles by name',
+ autofocus=True
+ ) }}
{{ radios(form.branding_style) }}
diff --git a/app/templates/views/service-settings/set-letter-branding.html b/app/templates/views/service-settings/set-letter-branding.html
index 455dcad65..4cb919a81 100644
--- a/app/templates/views/service-settings/set-letter-branding.html
+++ b/app/templates/views/service-settings/set-letter-branding.html
@@ -5,14 +5,16 @@
{% from "components/page-footer.html" import page_footer %}
{% from "components/form.html" import form_wrapper %}
+{% set page_title = "Set letter branding" %}
+
{% block service_page_title %}
- Set letter branding
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
- 'Set letter branding',
+ page_title,
back_link=url_for('.service_settings', service_id=current_service.id)
) }}
{% call form_wrapper(data_kwargs={'preview-type': 'letter'}) %}
@@ -22,7 +24,13 @@
- {{ live_search(target_selector='.multiple-choice', show=True, form=search_form, label='Search by name') }}
+ {{ live_search(
+ target_selector='.multiple-choice',
+ show=True,
+ form=search_form,
+ label='Search by name',
+ autofocus=True
+ ) }}
{{ radios(form.branding_style, hide_legend=True) }}
diff --git a/app/templates/views/templates/copy.html b/app/templates/views/templates/copy.html
index 7aac461ec..6bd5f74ba 100644
--- a/app/templates/views/templates/copy.html
+++ b/app/templates/views/templates/copy.html
@@ -2,15 +2,16 @@
{% from "components/live-search.html" import live_search %}
{% extends "withnav_template.html" %}
+{% set page_title = "Copy an existing template" %}
{% block service_page_title %}
- Copy an existing template
+ {{ page_title }}
{% endblock %}
{% block maincolumn_content %}
-
Copy an existing template
+ {{ page_title }}
{{ copy_folder_path(template_folder_path, current_service.id, from_service, current_user) }}
{% if not services_templates_and_folders.templates_to_show %}
@@ -18,7 +19,12 @@
This folder is empty
{% else %}
- {{ live_search(target_selector='#template-list .template-list-item', show=True, form=search_form) }}
+ {{ live_search(
+ target_selector='#template-list .template-list-item',
+ show=True,
+ form=search_form,
+ autofocus=True
+ ) }}
{% for item in services_templates_and_folders %}
diff --git a/tests/javascripts/autofocus.test.js b/tests/javascripts/autofocus.test.js
index 52b516613..5ea5179a8 100644
--- a/tests/javascripts/autofocus.test.js
+++ b/tests/javascripts/autofocus.test.js
@@ -1,3 +1,5 @@
+const helpers = require('./support/helpers.js');
+
beforeAll(() => {
require('../../app/assets/javascripts/autofocus.js');
});
@@ -8,16 +10,19 @@ afterAll(() => {
describe('Autofocus', () => {
+ const labelText = 'Search by name';
let focusHandler;
let search;
beforeEach(() => {
+ document.title = 'Find services by name - GOV.UK Notify';
+
// set up DOM
document.body.innerHTML =
`
- Search by name
+ ${labelText}
`;
@@ -45,6 +50,28 @@ describe('Autofocus', () => {
});
+ test('has a label including the page title when the module starts', () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ expect(search.hasAttribute('aria-label')).toBe(true);
+ expect(search.getAttribute('aria-label')).toEqual(document.title + ' - ' + labelText);
+
+ });
+
+ test('gets the original label back when focus moves away', () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ // shift focus away from textbox
+ helpers.triggerEvent(search, 'blur');
+
+ expect(search.hasAttribute('aria-label')).toBe(false);
+
+ });
+
test('is not focused if the window has scrolled', () => {
// mock the window being scrolled 25px