Skip to content

Commit

Permalink
Add JSDoc and improve function names
Browse files Browse the repository at this point in the history
  • Loading branch information
36degrees committed Jul 9, 2020
1 parent 3e7a435 commit 5373696
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 18 deletions.
47 changes: 40 additions & 7 deletions src/govuk/components/checkboxes/checkboxes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ function Checkboxes ($module) {
this.$inputs = $module.querySelectorAll('input[type="checkbox"]')
}

/**
* Initialise Checkboxes
*
* Checkboxes can be associated with a 'conditionally revealed' content block –
* for example, a checkbox for 'Phone' could reveal an additional form field for
* the user to enter their phone number.
*
* These associations are made using a `data-aria-controls` attribute, which is
* promoted to an aria-controls attribute during initialisation.
*
* We also need to restore the state of any conditional reveals on the page (for
* example if the user has navigated back), and set up event handlers to keep
* the reveal in sync with the checkbox state.
*/
Checkboxes.prototype.init = function () {
var $module = this.$module
var $inputs = this.$inputs
Expand All @@ -33,24 +47,35 @@ Checkboxes.prototype.init = function () {
// 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))
window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this))
} else {
window.addEventListener('DOMContentLoaded', this.syncAll.bind(this))
window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.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()
this.syncAllConditionalReveals()

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

Checkboxes.prototype.syncAll = function () {
nodeListForEach(this.$inputs, this.syncWithInputState.bind(this))
/**
* Sync the conditional reveal states for all inputs in this $module.
*/
Checkboxes.prototype.syncAllConditionalReveals = function () {
nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this))
}

Checkboxes.prototype.syncWithInputState = function ($input) {
/**
* Sync conditional reveal with the input state
*
* Synchronise the visibility of the conditional reveal, and its accessible
* state, with the input's checked state.
*
* @param {HTMLInputElement} $input Checkbox input
*/
Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
var $target = this.$module.querySelector('#' + $input.getAttribute('aria-controls'))

if ($target && $target.classList.contains('govuk-checkboxes__conditional')) {
Expand All @@ -61,14 +86,22 @@ Checkboxes.prototype.syncWithInputState = function ($input) {
}
}

/**
* Click event handler
*
* Handle a click within the $module – if the click occurred on a checkbox, sync
* the state of any associated conditional reveal with the checkbox state.
*
* @param {MouseEvent} event Click event
*/
Checkboxes.prototype.handleClick = function (event) {
var $target = event.target

// If a checkbox with aria-controls, handle click
var isCheckbox = $target.getAttribute('type') === 'checkbox'
var hasAriaControls = $target.getAttribute('aria-controls')
if (isCheckbox && hasAriaControls) {
this.syncWithInputState($target)
this.syncConditionalRevealWithInputState($target)
}
}

Expand Down
53 changes: 42 additions & 11 deletions src/govuk/components/radios/radios.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ function Radios ($module) {
this.$inputs = $module.querySelectorAll('input[type="radio"]')
}

/**
* Initialise Radios
*
* Radios can be associated with a 'conditionally revealed' content block – for
* example, a radio for 'Phone' could reveal an additional form field for the
* user to enter their phone number.
*
* These associations are made using a `data-aria-controls` attribute, which is
* promoted to an aria-controls attribute during initialisation.
*
* We also need to restore the state of any conditional reveals on the page (for
* example if the user has navigated back), and set up event handlers to keep
* the reveal in sync with the radio state.
*/
Radios.prototype.init = function () {
var $module = this.$module
var $inputs = this.$inputs
Expand All @@ -33,25 +47,36 @@ Radios.prototype.init = function () {
// 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))
window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this))
} else {
window.addEventListener('DOMContentLoaded', this.syncAll.bind(this))
window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.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()
this.syncAllConditionalReveals()

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

Radios.prototype.syncAll = function () {
nodeListForEach(this.$inputs, this.syncWithInputState.bind(this))
/**
* Sync the conditional reveal states for all inputs in this $module.
*/
Radios.prototype.syncAllConditionalReveals = function () {
nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this))
}

Radios.prototype.syncWithInputState = function ($input) {
/**
* Sync conditional reveal with the input state
*
* Synchronise the visibility of the conditional reveal, and its accessible
* state, with the input's checked state.
*
* @param {HTMLInputElement} $input Radio input
*/
Radios.prototype.syncConditionalRevealWithInputState = function ($input) {
var $target = document.querySelector('#' + $input.getAttribute('aria-controls'))

if ($target && $target.classList.contains('govuk-radios__conditional')) {
Expand All @@ -62,6 +87,16 @@ Radios.prototype.syncWithInputState = function ($input) {
}
}

/**
* Click event handler
*
* Handle a click within the $module – if the click occurred on a radio, sync
* the state of the conditional reveal for all radio buttons in the same form
* with the same name (because checking one radio could have un-checked a radio
* in another $module)
*
* @param {MouseEvent} event Click event
*/
Radios.prototype.handleClick = function (event) {
var $clickedInput = event.target

Expand All @@ -70,10 +105,6 @@ Radios.prototype.handleClick = function (event) {
return
}

// Because checking one radio can un-check a radio in another $module, we need
// to call set attributes on all radios in the same form that have the same
// name.
//
// We only need to consider radios with conditional reveals, which will have
// aria-controls attributes.
var $allInputs = document.querySelectorAll('input[type="radio"][aria-controls]')
Expand All @@ -83,7 +114,7 @@ Radios.prototype.handleClick = function (event) {
var hasSameName = ($input.name === $clickedInput.name)

if (hasSameName && hasSameFormOwner) {
this.syncWithInputState($input)
this.syncConditionalRevealWithInputState($input)
}
}.bind(this))
}
Expand Down

0 comments on commit 5373696

Please sign in to comment.