Skip to content

Commit

Permalink
Merge pull request #1842 from alphagov/conditional-reveals-back-button
Browse files Browse the repository at this point in the history
Preserve the state of conditional reveals when navigating 'back' in the browser
  • Loading branch information
36degrees authored Jun 30, 2020
2 parents 4a9d392 + 82c5bf0 commit ffc30f4
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
We’ve made fixes to GOV.UK Frontend in the following pull requests:

- [#1838: Correctly camel case SVG attributes in the header and footer](https://github.com/alphagov/govuk-frontend/pull/1838)
- [#1842: Preserve the state of conditional reveals when navigating 'back' in the browser](https://github.com/alphagov/govuk-frontend/pull/1842)

## 3.7.0 (Feature release)

Expand Down
29 changes: 24 additions & 5 deletions src/govuk/components/checkboxes/checkboxes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event' // addEventListener and event.target normaliziation
// addEventListener, event.target normalization and DOMContentLoaded
import '../../vendor/polyfills/Event'
import '../../vendor/polyfills/Element/prototype/classList'
import { nodeListForEach } from '../../common'

Expand Down Expand Up @@ -29,14 +30,32 @@ Checkboxes.prototype.init = function () {
// If we have content that is controlled, set attributes.
$input.setAttribute('aria-controls', controls)
$input.removeAttribute('data-aria-controls')
this.setAttributes($input)
}.bind(this))
})

// When the page is restored after navigating 'back' in some browsers the
// state of form controls is not restored until *after* the DOMContentLoaded
// event is fired, so we need to sync after the pageshow event in browsers
// that support it.
if ('onpageshow' in window) {
window.addEventListener('pageshow', this.syncAll.bind(this))
} else {
window.addEventListener('DOMContentLoaded', this.syncAll.bind(this))
}

// Although we've set up handlers to sync state on the pageshow or
// DOMContentLoaded event, init could be called after those events have fired,
// for example if they are added to the page dynamically, so sync now too.
this.syncAll()

// Handle events
$module.addEventListener('click', this.handleClick.bind(this))
}

Checkboxes.prototype.setAttributes = function ($input) {
Checkboxes.prototype.syncAll = function () {
nodeListForEach(this.$inputs, this.syncWithInputState.bind(this))
}

Checkboxes.prototype.syncWithInputState = function ($input) {
var inputIsChecked = $input.checked
$input.setAttribute('aria-expanded', inputIsChecked)

Expand All @@ -53,7 +72,7 @@ Checkboxes.prototype.handleClick = function (event) {
var isCheckbox = $target.getAttribute('type') === 'checkbox'
var hasAriaControls = $target.getAttribute('aria-controls')
if (isCheckbox && hasAriaControls) {
this.setAttributes($target)
this.syncWithInputState($target)
}
}

Expand Down
32 changes: 26 additions & 6 deletions src/govuk/components/radios/radios.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event' // addEventListener and event.target normaliziation
// addEventListener, event.target normalization and DOMContentLoaded
import '../../vendor/polyfills/Event'
import '../../vendor/polyfills/Element/prototype/classList'
import { nodeListForEach } from '../../common'

function Radios ($module) {
this.$module = $module
this.$inputs = $module.querySelectorAll('input[type="radio"]')
}

Radios.prototype.init = function () {
var $module = this.$module
var $inputs = $module.querySelectorAll('input[type="radio"]')
var $inputs = this.$inputs

/**
* Loop over all items with [data-controls]
Expand All @@ -28,14 +30,32 @@ Radios.prototype.init = function () {
// If we have content that is controlled, set attributes.
$input.setAttribute('aria-controls', controls)
$input.removeAttribute('data-aria-controls')
this.setAttributes($input)
}.bind(this))
})

// When the page is restored after navigating 'back' in some browsers the
// state of form controls is not restored until *after* the DOMContentLoaded
// event is fired, so we need to sync after the pageshow event in browsers
// that support it.
if ('onpageshow' in window) {
window.addEventListener('pageshow', this.syncAll.bind(this))
} else {
window.addEventListener('DOMContentLoaded', this.syncAll.bind(this))
}

// Although we've set up handlers to sync state on the pageshow or
// DOMContentLoaded event, init could be called after those events have fired,
// for example if they are added to the page dynamically, so sync now too.
this.syncAll()

// Handle events
$module.addEventListener('click', this.handleClick.bind(this))
}

Radios.prototype.setAttributes = function ($input) {
Radios.prototype.syncAll = function () {
nodeListForEach(this.$inputs, this.syncWithInputState.bind(this))
}

Radios.prototype.syncWithInputState = function ($input) {
var $content = document.querySelector('#' + $input.getAttribute('aria-controls'))

if ($content && $content.classList.contains('govuk-radios__conditional')) {
Expand Down Expand Up @@ -65,7 +85,7 @@ Radios.prototype.handleClick = function (event) {
// In radios, only radios with the same name will affect each other.
var hasSameName = ($input.name === $clickedInput.name)
if (hasSameName && hasSameFormOwner) {
this.setAttributes($input)
this.syncWithInputState($input)
}
}.bind(this))
}
Expand Down

0 comments on commit ffc30f4

Please sign in to comment.