Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-focus the notification banner, add tests #1994

Merged
merged 2 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/govuk/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Details from './components/details/details'
import CharacterCount from './components/character-count/character-count'
import Checkboxes from './components/checkboxes/checkboxes'
import ErrorSummary from './components/error-summary/error-summary'
import NotificationBanner from './components/notification-banner/notification-banner'
import Header from './components/header/header'
import Radios from './components/radios/radios'
import Tabs from './components/tabs/tabs'
Expand Down Expand Up @@ -50,6 +51,11 @@ function initAll (options) {
var $toggleButton = scope.querySelector('[data-module="govuk-header"]')
new Header($toggleButton).init()

var $notificationBanners = scope.querySelectorAll('[data-module="govuk-notification-banner"]')
hannalaakso marked this conversation as resolved.
Show resolved Hide resolved
nodeListForEach($notificationBanners, function ($notificationBanner) {
new NotificationBanner($notificationBanner).init()
})

var $radios = scope.querySelectorAll('[data-module="govuk-radios"]')
nodeListForEach($radios, function ($radio) {
new Radios($radio).init()
Expand Down
47 changes: 47 additions & 0 deletions src/govuk/components/notification-banner/notification-banner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
function NotificationBanner ($module) {
this.$module = $module
}

/**
* Initialise the component
*/
NotificationBanner.prototype.init = function () {
var $module = this.$module
// Check for module
if (!$module) {
return
}

this.setFocus()
}

/**
* Focus the element
*
* If `role="alert"` is set, focus the element to help some assistive technologies
* prioritise announcing it.
*
* You can turn off the auto-focus functionality by setting `data-disable-auto-focus="true"` in the
* component HTML. You might wish to do this based on user research findings, or to avoid a clash
* with another element which should be focused when the page loads.
*/
NotificationBanner.prototype.setFocus = function () {
var $module = this.$module

if ($module.getAttribute('data-disable-auto-focus') === 'true') {
return
}

if ($module.getAttribute('role') !== 'alert') {
return
}

// Set tabindex to -1 to make the element focusable with JavaScript.
if (!$module.getAttribute('tabindex')) {
$module.setAttribute('tabindex', '-1')
}

$module.focus()
}

export default NotificationBanner
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-env jest */

const configPaths = require('../../../../config/paths.json')
const PORT = configPaths.ports.test

const baseUrl = 'http://localhost:' + PORT

describe('/components/notification-banner/with-type-as-success', () => {
hannalaakso marked this conversation as resolved.
Show resolved Hide resolved
it('has the correct tabindex to be focused with JavaScript', async () => {
await page.goto(baseUrl + '/components/notification-banner/with-type-as-success/preview', { waitUntil: 'load' })

const tabindex = await page.$eval('.govuk-notification-banner', el => el.getAttribute('tabindex'))

expect(tabindex).toEqual('-1')
})

it('is automatically focused when the page loads', async () => {
await page.goto(baseUrl + '/components/notification-banner/with-type-as-success/preview', { waitUntil: 'load' })

const activeElement = await page.evaluate(() => document.activeElement.dataset.module)

expect(activeElement).toBe('govuk-notification-banner')
})
})

describe('components/notification-banner/auto-focus-disabled,-with-type-as-success/', () => {
describe('when auto-focus is disabled', () => {
it('does not have a tabindex attribute', async () => {
await page.goto(`${baseUrl}/components/notification-banner/auto-focus-disabled,-with-type-as-success/preview`, { waitUntil: 'load' })

const tabindex = await page.$eval('.govuk-notification-banner', el => el.getAttribute('tabindex'))

expect(tabindex).toBeFalsy()
})

it('does not focus the notification banner', async () => {
await page.goto(`${baseUrl}/components/notification-banner/auto-focus-disabled,-with-type-as-success/preview`, { waitUntil: 'load' })

const activeElement = await page.evaluate(() => document.activeElement.dataset.module)

expect(activeElement).not.toBe('govuk-notification-banner')
})
})
})

describe('components/notification-banner/role=alert-overridden-to-role=region,-with-type-as-success', () => {
describe('when role is not alert', () => {
it('does not have a tabindex attribute', async () => {
await page.goto(`${baseUrl}/components/notification-banner/role=alert-overridden-to-role=region,-with-type-as-success/preview`, { waitUntil: 'load' })

const tabindex = await page.$eval('.govuk-notification-banner', el => el.getAttribute('tabindex'))

expect(tabindex).toBeFalsy()
})

it('does not focus the notification banner', async () => {
await page.goto(`${baseUrl}/components/notification-banner/role=alert-overridden-to-role=region,-with-type-as-success/preview`, { waitUntil: 'load' })

const activeElement = await page.evaluate(() => document.activeElement.dataset.module)

expect(activeElement).not.toBe('govuk-notification-banner')
})
})
})
46 changes: 12 additions & 34 deletions src/govuk/components/notification-banner/notification-banner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,14 @@ params:
type: string
required: false
description: Overrides the value of the `role` attribute for the notification banner. Defaults to `region`. If `type` is set to `success` or `error`, defaults to `alert`.
- name: tabindex
type: string/boolean
required: false
description: Overrides the value of the `tabindex` attribute for the notification banner. Not set by default. If `type` is set to `success` or `error`, defaults to `-1`; the attribute can be removed by setting `tabindex` to `false`;
- name: titleId
type: string
required: false
description: Overrides the value of the `id` attribute for the title. `id` used by the `aria-labelledby` attribute on the notification banner to provide information to users of assistive technologies. `id` defaults to `govuk-notification-banner-title` if `role` is set to `region`. If `type` is set to `success` or `error`, `id` is not rendered by default.
- name: autoFocus
- name: disableAutoFocus
type: boolean
required: false
description: Moves keyboard focus to the notification banner when the page loads. Defaults to `false`. If `type` is set to `success` or `error`, defaults to `true`.
description: If you set 'type' to 'success' or 'error', or 'role' to 'alert', JavaScript moves the keyboard focus to the notification banner when the page loads. To disable this behaviour, set 'disableAutoFocus' to 'true'.
- name: classes
type: string
required: false
Expand Down Expand Up @@ -87,6 +83,16 @@ examples:
<li><a href="#" class="govuk-notification-banner__link">government-strategy-v3-FINAL.pdf</a></li>
<li><a href="#" class="govuk-notification-banner__link">government-strategy-v4-FINAL-v2.pdf</a></li>
</ul>
- name: auto-focus disabled, with type as success
data:
type: success
disableAutoFocus: true
text: Email sent to example@email.com
- name: role=alert overridden to role=region, with type as success
data:
type: success
role: region
text: Email sent to example@email.com-

# Hidden examples are not shown in the review app, but are used for tests and HTML fixtures

Expand Down Expand Up @@ -142,34 +148,6 @@ examples:
data:
role: banner
text: This publication was withdrawn on 7 March 2014.
- name: role overridden to region
hidden: true
data:
type: success
role: region
text: Email sent to example@email.com-
- name: custom tabindex
hidden: true
data:
tabindex: 0
text: This publication was withdrawn on 7 March 2014.
- name: tabindex as false and type as success
hidden: true
data:
type: success
tabindex: false
text: Email sent to example@email.com
- name: autoFocus as true
hidden: true
data:
autoFocus: true
text: This publication was withdrawn on 7 March 2014.
- name: autoFocus as false and type as success
hidden: true
data:
type: success
autoFocus: false
text: Email sent to example@email.com

- name: classes
hidden: true
Expand Down
22 changes: 3 additions & 19 deletions src/govuk/components/notification-banner/template.njk
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,6 @@
{% set role = "region" %}
{% endif %}

{# Check whether to add `data-auto-focus="true"` which focuses the notification banner on page load #}
{% if params.autoFocus is defined %}
{% set autoFocus = params.autoFocus %}
{% elif successOrError %}
{% set autoFocus = true %}
{% endif %}

{% if params.tabindex is defined %}
{% set tabindex = params.tabindex %}
{# Make sure that success or error banner should be focused on page load #}
{% elif successOrError and autoFocus %}
{# Make the notification banner focusable #}
{% set tabindex = "-1" %}
{% endif %}

{%- if params.titleHtml %}
{% set title = params.titleHtml | safe %}
{%- elif params.title %}
Expand All @@ -43,11 +28,10 @@
{% set title = "Important" %}
{%- endif -%}

<div class="govuk-notification-banner {{ typeClass }} {%- if params.classes %} {{ params.classes }}{% endif -%}" role="{{ role }}"
{%- if tabindex or tabindex === 0 %} tabindex="{{ tabindex }}" {% endif %}
{%- if (role == "region") %} aria-labelledby="{{ params.titleId | default('govuk-notification-banner-title') }}" {%- endif -%}
<div class="govuk-notification-banner{% if typeClass %} {{ typeClass }}{% endif %}{% if params.classes %} {{ params.classes }}{% endif %}" role="{{ role }}"
{%- if (role == "region") %} aria-labelledby="{{ params.titleId | default('govuk-notification-banner-title')}}" {% endif -%}
data-module="govuk-notification-banner"
{%- if autoFocus -%} data-auto-focus="true"{% endif %}
{%- if params.disableAutoFocus %} data-disable-auto-focus="true"{% endif %}
{%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %}>
<div class="govuk-notification-banner__header">
<h{{ params.titleHeadingLevel | default(2) }} class="govuk-notification-banner__title"{% if (role == "region" or params.titleId) %} id="{{ params.titleId | default('govuk-notification-banner-title') }}" {%- endif %}>
Expand Down
Loading