Skip to content

Commit

Permalink
Add hide this page component
Browse files Browse the repository at this point in the history
  • Loading branch information
owenatgov committed Mar 24, 2022
1 parent fcce09a commit 159922e
Show file tree
Hide file tree
Showing 12 changed files with 325 additions and 0 deletions.
5 changes: 5 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 HideThisPage from './components/hide-this-page/hide-this-page'
import NotificationBanner from './components/notification-banner/notification-banner'
import Header from './components/header/header'
import Radios from './components/radios/radios'
Expand Down Expand Up @@ -52,6 +53,9 @@ function initAll (options) {
var $toggleButton = scope.querySelector('[data-module="govuk-header"]')
new Header($toggleButton).init()

var $hideThisPageButtons = scope.querySelectorAll('[data-module="govuk-hide-this-page"]')
new HideThisPage($hideThisPageButtons).init()

var $notificationBanners = scope.querySelectorAll('[data-module="govuk-notification-banner"]')
nodeListForEach($notificationBanners, function ($notificationBanner) {
new NotificationBanner($notificationBanner).init()
Expand Down Expand Up @@ -81,6 +85,7 @@ export {
Checkboxes,
ErrorSummary,
Header,
HideThisPage,
NotificationBanner,
Radios,
SkipLink,
Expand Down
1 change: 1 addition & 0 deletions src/govuk/all.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('GOV.UK Frontend', () => {
'Checkboxes',
'ErrorSummary',
'Header',
'HideThisPage',
'NotificationBanner',
'Radios',
'SkipLink',
Expand Down
1 change: 1 addition & 0 deletions src/govuk/components/_all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
@import "file-upload/index";
@import "footer/index";
@import "hint/index";
@import "hide-this-page/index";
@import "header/index";
@import "input/index";
@import "inset-text/index";
Expand Down
15 changes: 15 additions & 0 deletions src/govuk/components/hide-this-page/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Hide this page

## Installation

See the [main README quick start guide](https://github.com/alphagov/govuk-frontend#quick-start) for how to install this component.

## Guidance and Examples

Find out when to use the inset text component in your service in the [GOV.UK Design System](https://design-system.service.gov.uk/components/inset-text).

## Component options

Use options to customise the appearance, content and behaviour of a component when using a macro, for example, changing the text.

See [options table](https://design-system.service.gov.uk/components/inset-text/#options-inset-text-example) for details.
2 changes: 2 additions & 0 deletions src/govuk/components/hide-this-page/_hide-this-page.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import "../../base";
@import "./index";
31 changes: 31 additions & 0 deletions src/govuk/components/hide-this-page/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@import "../button/index";

@include govuk-exports("govuk/component/hide-this-page") {
.govuk-hide-this-page {
position: fixed;
z-index: 1000;
top: 0;
left: 0;
width: 100%;

@include govuk-media-query($from: tablet) {
display: inline-block;
position: sticky;
top: 0;
right: 0;
left: auto;
width: auto;
float: right;
}
}

.govuk-hide-this-page__button {
margin-bottom: 0;
}

@media only print {
.govuk-hide-this-page {
display: none;
}
}
}
44 changes: 44 additions & 0 deletions src/govuk/components/hide-this-page/hide-this-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { nodeListForEach } from '../../common'

function HideThisPage ($module) {
this.$module = $module
}

HideThisPage.prototype.openNewTab = function (e) {
var isClickEvent = typeof e !== 'undefined'

if (isClickEvent) {
e.preventDefault()
}

var target = isClickEvent ? e.target : document.querySelector('.govuk-js-hide-this-page-button')

window.open(target.getAttribute('data-new-tab-url'), '_newtab')

// Check if the user's browser supports the history API
// If so, we apply a history scrambling enhancement
if (typeof window.history !== 'undefined' && typeof window.history === 'object') {
document.title = target.getAttribute('data-fake-page-title')
// Do not remove the empty value in the below function call. They are required to ensure we comply with the history API
// https://developer.mozilla.org/en-US/docs/Web/API/History_API
window.history.replaceState(target.getAttribute('data-fake-page-title'), '')
}

window.location.href = target.href
}

HideThisPage.prototype.init = function () {
// We put the loop here instead of in all.js because we want to have both listeners on the individual buttons for clicks
// and a single listener for the keyboard shortcut
nodeListForEach(this.$module, function ($button) {
$button.querySelector('.govuk-js-hide-this-page-button').addEventListener('click', this.openNewTab)
}.bind(this))

window.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
this.openNewTab()
}
}.bind(this))
}

export default HideThisPage
67 changes: 67 additions & 0 deletions src/govuk/components/hide-this-page/hide-this-page.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* eslint-env jest */

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

const baseUrl = 'http://localhost:' + PORT
const buttonClass = '.govuk-js-hide-this-page-button'

describe('/components/hide-this-page', () => {
beforeEach(async () => {
await page.goto(baseUrl + '/components/hide-this-page/preview', { waitUntil: 'load' })
})

it('navigates to the href of the button', async () => {
const href = await page.evaluate((buttonClass) => document.querySelector(buttonClass).href, buttonClass)

await Promise.all([
page.waitForNavigation(),
page.click(buttonClass)
])

const url = await page.url()
expect(url).toBe(href)
})

it('opens a page in a new tab when the button is clicked', async () => {
const expectedNewTabUrl = await page.evaluate((buttonClass) => document.querySelector(buttonClass).getAttribute('data-new-tab-url'), buttonClass)
const pageTarget = page.target()

await Promise.all([
page.waitForNavigation(),
page.click(buttonClass)
])

// Because we're opening a new tab, we need to wait for that tab to open and load before we can test for it
// We do do this by waiting for a new target to appear in the browser object which was opened by our button and getting it's page object
const newTarget = await browser.waitForTarget(target => target.opener() === pageTarget)
const newPage = await newTarget.page()
const newTabUrl = await newPage.url()

expect(newTabUrl).toBe(expectedNewTabUrl)
})

// Checking the browser history from within jest is very difficult, meaning we can't completely test this feature
// To get around this, we alter the button href so that it doesn't navigate, allowing us to at least ensure that the title is changing
it('changes the page title before navigating to scramble the browser history', async () => {
const fakePageTitle = await page.evaluate((buttonClass) => document.querySelector(buttonClass).getAttribute('data-fake-page-title'), buttonClass)
await page.evaluate((buttonClass) => {
document.querySelector(buttonClass).href = '#'
}, buttonClass)

await page.click(buttonClass)
const pageTitle = await page.title()

expect(pageTitle).toBe(fakePageTitle)
})

it('activates the button functionality when the escape key is pressed', async () => {
const href = await page.evaluate((buttonClass) => document.querySelector(buttonClass).href, buttonClass)

await page.keyboard.press('Escape')
await page.waitForNavigation()

const url = await page.url()
expect(url).toBe(href)
})
})
54 changes: 54 additions & 0 deletions src/govuk/components/hide-this-page/hide-this-page.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
params:
- name: text
type: string
required: false
description: Text for the link. Defaults to `Hide this page`.
- name: currentTabURL
type: string
required: false
description: URL to redirect the current tab to. Defaults to `https://www.google.com`.
- name: newTabURL
type: string
required: false
description: URL to open a new tab to. Defaults to `https://www.bbc.co.uk/weather`.
- name: fakePageTitle
type: string
required: false
description: Fake title that will be passed to the original page's title attribute to mask the user's history. Defaults to `How to prevent the spread of Coronavirus - GOV.UK`.
- name: id
type: string
required: false
description: ID attribute to add to the hide this page container.
- name: classes
type: string
required: false
description: Classes to add to the hide this page container.
- name: attributes
type: object
required: false
description: HTML attributes (for example data attributes) to add to the hide this page container.

examples:
- name: default
data:
text: Hide this page
currentTabURL: "https://www.gov.uk"
newTabURL: "https://www.google.com/search?q=weather"
fakePageTitle: How to prevent the spread of Coronavirus - GOV.UK
id: null
classes: null
attributes:
{}

# Hidden examples are not shown in the review app, but are used for tests and HTML fixtures
- name: testing
hidden: true
data:
text: Hide this test
currentTabURL: "https://www.test.co.uk"
newTabURL: "https://www.google.com/search?q=test"
fakePageTitle: This is a test
id: test-id
classes: test-class
attributes:
test-attribute: true
3 changes: 3 additions & 0 deletions src/govuk/components/hide-this-page/macro.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% macro govukHideThisPage(params) %}
{%- include "./template.njk" -%}
{% endmacro %}
21 changes: 21 additions & 0 deletions src/govuk/components/hide-this-page/template.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% from "../button/macro.njk" import govukButton %}
{% set currentTabURL = params.currentTabURL | default() -%}
{% set newTabURL = params.newTabURL | default() -%}
{% set text = params.text | default() -%}
{% set fakePageTitle = params.fakePageTitle | default() -%}

<div
{%- if params.id %} id="{{ params.id }}"{% endif %}
class="govuk-hide-this-page {%- if params.classes %} {{ params.classes }}{% endif %}"
data-module="govuk-hide-this-page" {%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %}
>
{{ govukButton({
text: params.text,
classes: "govuk-button--warning govuk-hide-this-page__button govuk-js-hide-this-page-button",
href: currentTabURL,
attributes: {
"data-new-tab-url": newTabURL,
"data-fake-page-title": fakePageTitle
}
})}}
</div>
81 changes: 81 additions & 0 deletions src/govuk/components/hide-this-page/template.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @jest-environment jsdom
*/
/* eslint-env jest */

const axe = require('../../../../lib/axe-helper')

const { render, getExamples } = require('../../../../lib/jest-helpers')

const examples = getExamples('hide-this-page')

describe('Hide this page', () => {
it('default example passes accessibility tests', async () => {
const $ = render('hide-this-page', examples.default)

const results = await axe($.html())
expect(results).toHaveNoViolations()
})

it('renders the default example', () => {
const $ = render('hide-this-page', examples.default)
const $button = $('.govuk-hide-this-page').find('.govuk-button')

expect($button.hasClass('govuk-button--warning')).toBeTruthy()
expect($button.text()).toContain('Hide this page')
expect($button.attr('href')).toBe('https://www.gov.uk')
expect($button.attr('data-new-tab-url')).toBe('https://www.google.com/search?q=weather')
expect($button.attr('data-fake-page-title')).toBe('How to prevent the spread of Coronavirus - GOV.UK')
})
})

describe('Custom options', () => {
it('renders with text', () => {
const $ = render('hide-this-page', examples.testing)
const $button = $('.govuk-hide-this-page').find('.govuk-button')

expect($button.text()).toContain('Hide this test')
})

it('renders with a current tab URL', () => {
const $ = render('hide-this-page', examples.testing)
const $button = $('.govuk-hide-this-page').find('.govuk-button')

expect($button.attr('href')).toBe('https://www.test.co.uk')
})

it('renders with a new tab URL', () => {
const $ = render('hide-this-page', examples.testing)
const $button = $('.govuk-hide-this-page').find('.govuk-button')

expect($button.attr('data-new-tab-url')).toBe('https://www.google.com/search?q=test')
})

it('renders with a fake page title', () => {
const $ = render('hide-this-page', examples.testing)
const $button = $('.govuk-hide-this-page').find('.govuk-button')

expect($button.attr('data-fake-page-title')).toBe('This is a test')
})

it('renders with a custom id', () => {
const $ = render('hide-this-page', examples.testing)
const $component = $('.govuk-hide-this-page')

expect($component.attr('id')).toBe('test-id')
})

it('renders with a custom class', () => {
const $ = render('hide-this-page', examples.testing)
const $component = $('.govuk-hide-this-page')

expect($component.hasClass('test-class')).toBeTruthy()
})

it('renders with custom attributes', () => {
const $ = render('hide-this-page', examples.testing)
const $component = $('.govuk-hide-this-page')

expect($component.attr('test-attribute')).toBe('true')
})
})

0 comments on commit 159922e

Please sign in to comment.