Skip to content

Commit

Permalink
Merge pull request #4011 from alphagov/bk-remove-init
Browse files Browse the repository at this point in the history
Remove component init() methods and initialise in constructor
  • Loading branch information
domoscargin authored Jul 26, 2023
2 parents 5e3ae52 + ecd4f71 commit dcc78a7
Show file tree
Hide file tree
Showing 19 changed files with 74 additions and 179 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ This change was made in [pull request #3819: Add linked image focus style](https

You must make the following changes when you migrate to this release, or your service might break.

#### Update component initialisation

Remove `.init()` from individually instantiated components as initialisation now happens automatically:

```mjs
new Radios($radio).init()
```

```mjs
new Radios($radio)
```

If you import the JavaScript using `window.GOVUKFrontend.initAll()`, you will not need to make any changes.

This change was introduced in [pull request #4011: Remove component init() methods and initialise in constructor](https://github.com/alphagov/govuk-frontend/pull/4011).

#### Check that details components work as expected

The Details component no longer uses JavaScript, and is no longer polyfilled in older browsers.
Expand Down
16 changes: 2 additions & 14 deletions docs/contributing/coding-standards/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,6 @@ export class Example {

this.$module = $module

// Code goes here
}

/**
* Initialise component
*/
init () {
// Check that required elements are present
if (!this.$module) {
return
}

// Code goes here
this.$module.addEventListener('click', () => {
// ...
Expand Down Expand Up @@ -104,14 +92,14 @@ Add methods to the class.
```mjs
// Good
class Example {
init () {
doSomething () {
// Code goes here
}
}

// Bad
Example.prototype = {
init: function () {
doSomething: function () {
// Code goes here
}
}
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/webpack/src/javascripts/app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { Button } from 'govuk-frontend'
const $buttons = document.querySelectorAll('[data-module="govuk-button"]')

$buttons.forEach(($button) => {
new Button($button).init()
/* eslint-disable-next-line no-new */
new Button($button)
})
24 changes: 13 additions & 11 deletions packages/govuk-frontend/src/govuk/all.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable no-new */

import { version } from './common/govuk-frontend-version.mjs'
import { Accordion } from './components/accordion/accordion.mjs'
import { Button } from './components/button/button.mjs'
Expand Down Expand Up @@ -33,60 +35,60 @@ function initAll (config) {

const $accordions = $scope.querySelectorAll('[data-module="govuk-accordion"]')
$accordions.forEach(($accordion) => {
new Accordion($accordion, config.accordion).init()
new Accordion($accordion, config.accordion)
})

const $buttons = $scope.querySelectorAll('[data-module="govuk-button"]')
$buttons.forEach(($button) => {
new Button($button, config.button).init()
new Button($button, config.button)
})

const $characterCounts = $scope.querySelectorAll('[data-module="govuk-character-count"]')
$characterCounts.forEach(($characterCount) => {
new CharacterCount($characterCount, config.characterCount).init()
new CharacterCount($characterCount, config.characterCount)
})

const $checkboxes = $scope.querySelectorAll('[data-module="govuk-checkboxes"]')
$checkboxes.forEach(($checkbox) => {
new Checkboxes($checkbox).init()
new Checkboxes($checkbox)
})

// Find first error summary module to enhance.
const $errorSummary = $scope.querySelector('[data-module="govuk-error-summary"]')
if ($errorSummary) {
new ErrorSummary($errorSummary, config.errorSummary).init()
new ErrorSummary($errorSummary, config.errorSummary)
}

const $exitThisPageButtons = $scope.querySelectorAll('[data-module="govuk-exit-this-page"]')
$exitThisPageButtons.forEach(($button) => {
new ExitThisPage($button, config.exitThisPage).init()
new ExitThisPage($button, config.exitThisPage)
})

// Find first header module to enhance.
const $header = $scope.querySelector('[data-module="govuk-header"]')
if ($header) {
new Header($header).init()
new Header($header)
}

const $notificationBanners = $scope.querySelectorAll('[data-module="govuk-notification-banner"]')
$notificationBanners.forEach(($notificationBanner) => {
new NotificationBanner($notificationBanner, config.notificationBanner).init()
new NotificationBanner($notificationBanner, config.notificationBanner)
})

const $radios = $scope.querySelectorAll('[data-module="govuk-radios"]')
$radios.forEach(($radio) => {
new Radios($radio).init()
new Radios($radio)
})

// Find first skip link module to enhance.
const $skipLink = $scope.querySelector('[data-module="govuk-skip-link"]')
if ($skipLink) {
new SkipLink($skipLink).init()
new SkipLink($skipLink)
}

const $tabs = $scope.querySelectorAll('[data-module="govuk-tabs"]')
$tabs.forEach(($tabs) => {
new Tabs($tabs).init()
new Tabs($tabs)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,6 @@ export class Accordion {

this.$sections = $sections
this.browserSupportsSessionStorage = helper.checkForSessionStorage()
}

/**
* Initialise component
*/
init () {
// Check that required elements are present
if (!this.$module || !this.$sections) {
return
}

this.initControls()
this.initSectionHeaders()
Expand Down
10 changes: 0 additions & 10 deletions packages/govuk-frontend/src/govuk/components/button/button.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ export class Button {
config || {},
normaliseDataset($module.dataset)
)
}

/**
* Initialise component
*/
init () {
// Check that required elements are present
if (!this.$module) {
return
}

this.$module.addEventListener('keydown', (event) => this.handleKeyDown(event))
this.$module.addEventListener('click', (event) => this.debounce(event))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ describe('/components/button', () => {

// `undefined` simulates the element being missing,
// from an unchecked `document.querySelector` for example
new namespace[exportName](undefined).init()
/* eslint-disable-next-line no-new */
new namespace[exportName](undefined)

// If our component initialisation breaks, this won't run
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,6 @@ export class CharacterCount {

this.$module = $module
this.$textarea = $textarea
}

/**
* Initialise component
*/
init () {
// Check that required elements are present
if (!this.$module || !this.$textarea) {
return
}

const $textareaDescription = document.getElementById(`${this.$textarea.id}-info`)
if (!$textareaDescription) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ export class Checkboxes {
$inputs

/**
* 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.
*
* @param {Element} $module - HTML element to use for checkboxes
*/
constructor ($module) {
Expand All @@ -24,27 +35,6 @@ export class Checkboxes {

this.$module = $module
this.$inputs = $inputs
}

/**
* Initialise component
*
* 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.
*/
init () {
// Check that required elements are present
if (!this.$module || !this.$inputs) {
return
}

this.$inputs.forEach(($input) => {
const targetId = $input.getAttribute('data-aria-controls')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ export class ErrorSummary {
config || {},
normaliseDataset($module.dataset)
)
}

/**
* Initialise component
*/
init () {
// Check that required elements are present
if (!this.$module) {
return
}

this.setFocus()
this.$module.addEventListener('click', (event) => this.handleClick(event))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ describe('Error Summary', () => {

// `undefined` simulates the element being missing,
// from an unchecked `document.querySelector` for example
new namespace[exportName](undefined).init()
/* eslint-disable-next-line no-new */
new namespace[exportName](undefined)

// If our component initialisation breaks, this won't run
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,7 @@ export class ExitThisPage {
if ($skiplinkButton instanceof HTMLAnchorElement) {
this.$skiplinkButton = $skiplinkButton
}
}

/**
* Initialise component
*/
init () {
this.buildIndicator()
this.initUpdateSpan()
this.initButtonClickHandler()
Expand Down
16 changes: 0 additions & 16 deletions packages/govuk-frontend/src/govuk/components/globals.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,6 @@ describe('GOV.UK Frontend', () => {
])
})

it('exported Components have an init function', async () => {
const components = exported
.filter(method => !['initAll', 'version'].includes(method))

const componentsWithoutInitFunctions = await page.evaluate(async (components) => {
const namespace = await import('govuk-frontend')

return components.filter(component => {
const prototype = namespace[component].prototype
return typeof prototype.init !== 'function'
})
}, components)

expect(componentsWithoutInitFunctions).toEqual([])
})

it('can be initialised scoped to certain sections of the page', async () => {
await goToExample(page, 'scoped-initialisation')

Expand Down
28 changes: 12 additions & 16 deletions packages/govuk-frontend/src/govuk/components/header/header.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ export class Header {

/**
* A global const for storing a matchMedia instance which we'll use to
* detect when a screen size change happens. We set this later during the
* init function and rely on it being null if the feature isn't available
* to initially apply hidden attributes
* detect when a screen size change happens. We rely on it being null if the
* feature isn't available to initially apply hidden attributes
*
* @private
* @type {MediaQueryList | null}
*/
mql = null

/**
* Apply a matchMedia for desktop which will trigger a state sync if the browser
* viewport moves between states.
*
* @param {Element} $module - HTML element to use for header
*/
constructor ($module) {
Expand All @@ -44,20 +46,14 @@ export class Header {
this.$menu = this.$menuButton && $module.querySelector(
`#${this.$menuButton.getAttribute('aria-controls')}`
)
}

/**
* Initialise component
*
* Check for the presence of the header, menu and menu button – if any are
* missing then there's nothing to do so return early.
* Apply a matchMedia for desktop which will trigger a state sync if the browser
* viewport moves between states.
*/
init () {
// Check that required elements are present
if (!this.$module || !this.$menuButton || !this.$menu) {
return
if (
!(
this.$menuButton instanceof HTMLElement ||
this.$menu instanceof HTMLElement
)
) {
return this
}

// Set the matchMedia to the govuk-frontend desktop breakpoint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,6 @@ export class NotificationBanner {
config || {},
normaliseDataset($module.dataset)
)
}

/**
* Initialise component
*/
init () {
// Check that required elements are present
if (!this.$module) {
return
}

this.setFocus()
}
Expand Down
Loading

0 comments on commit dcc78a7

Please sign in to comment.