Skip to content

Commit

Permalink
Merge pull request #4975 from alphagov/easier-js-init
Browse files Browse the repository at this point in the history
Add `createAll` function to initialise individual components
  • Loading branch information
36degrees committed May 10, 2024
2 parents 4cbd9ce + f4667fd commit 6c9d8d7
Show file tree
Hide file tree
Showing 8 changed files with 563 additions and 286 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,8 +4,34 @@ For advice on how to use these release notes see [our guidance on staying up to

## Unreleased

This release includes new features to help you include only the components your service uses. Doing this can help reduce the weight of the JavaScript and CSS files sent to users, improving their experience.

### New features

#### Create individual components with `createAll`

We've added a new `createAll` function that lets you initialise specific components in the same way that `initAll` does.

It will:

- find all elements in the page with the corresponding `data-module` attribute
- instantiate a component object for each element
- catch errors and log them in the console
- return an array of all the successfully instantiated component objects.

```js
import { createAll, Button, Checkboxes } from 'govuk-frontend'

createAll(Button)
createAll(Checkboxes)
```

You can also pass a config object and a scope within which to search for elements.

You can find out more about [how to use the `createAll` function](https://frontend.design-system.service.gov.uk/import-javascript/#import-individual-components) in our documentation.

This change was introduced in [pull request #4975: Add createAll function to initialise individual components](https://github.com/alphagov/govuk-frontend/pull/4975).

#### Use tabular numbers easily with `govuk-!-font-tabular-numbers`

We've added a new override class for tabular number styling: `govuk-!-font-tabular-numbers`.
Expand Down
146 changes: 0 additions & 146 deletions packages/govuk-frontend/src/govuk/all.jsdom.test.mjs

This file was deleted.

137 changes: 16 additions & 121 deletions packages/govuk-frontend/src/govuk/all.mjs
@@ -1,124 +1,19 @@
/* eslint-disable no-new */

import { version } from './common/govuk-frontend-version.mjs'
import { isSupported } from './common/index.mjs'
import { Accordion } from './components/accordion/accordion.mjs'
import { Button } from './components/button/button.mjs'
import { CharacterCount } from './components/character-count/character-count.mjs'
import { Checkboxes } from './components/checkboxes/checkboxes.mjs'
import { ErrorSummary } from './components/error-summary/error-summary.mjs'
import { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs'
import { Header } from './components/header/header.mjs'
import { NotificationBanner } from './components/notification-banner/notification-banner.mjs'
import { PasswordInput } from './components/password-input/password-input.mjs'
import { Radios } from './components/radios/radios.mjs'
import { SkipLink } from './components/skip-link/skip-link.mjs'
import { Tabs } from './components/tabs/tabs.mjs'
import { SupportError } from './errors/index.mjs'

/**
* Initialise all components
*
* Use the `data-module` attributes to find, instantiate and init all of the
* components provided as part of GOV.UK Frontend.
*
* @param {Config & { scope?: Element }} [config] - Config for all components (with optional scope)
*/
function initAll(config) {
config = typeof config !== 'undefined' ? config : {}

// Skip initialisation when GOV.UK Frontend is not supported
if (!isSupported()) {
console.log(new SupportError())
return
}

const components = /** @type {const} */ ([
[Accordion, config.accordion],
[Button, config.button],
[CharacterCount, config.characterCount],
[Checkboxes],
[ErrorSummary, config.errorSummary],
[ExitThisPage, config.exitThisPage],
[Header],
[NotificationBanner, config.notificationBanner],
[PasswordInput, config.passwordInput],
[Radios],
[SkipLink],
[Tabs]
])

// Allow the user to initialise GOV.UK Frontend in only certain sections of the page
// Defaults to the entire document if nothing is set.
const $scope = config.scope ?? document

components.forEach(([Component, config]) => {
const $elements = $scope.querySelectorAll(
`[data-module="${Component.moduleName}"]`
)

$elements.forEach(($element) => {
try {
// Only pass config to components that accept it
'defaults' in Component
? new Component($element, config)
: new Component($element)
} catch (error) {
console.log(error)
}
})
})
}

export {
initAll,
version,

// Components
Accordion,
Button,
CharacterCount,
Checkboxes,
ErrorSummary,
ExitThisPage,
Header,
NotificationBanner,
PasswordInput,
Radios,
SkipLink,
Tabs
}

/**
* Config for all components via `initAll()`
*
* @typedef {object} Config
* @property {AccordionConfig} [accordion] - Accordion config
* @property {ButtonConfig} [button] - Button config
* @property {CharacterCountConfig} [characterCount] - Character Count config
* @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
* @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
* @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
* @property {PasswordInputConfig} [passwordInput] - Password input config
*/

/**
* Config for individual components
*
* @typedef {import('./components/accordion/accordion.mjs').AccordionConfig} AccordionConfig
* @typedef {import('./components/accordion/accordion.mjs').AccordionTranslations} AccordionTranslations
* @typedef {import('./components/button/button.mjs').ButtonConfig} ButtonConfig
* @typedef {import('./components/character-count/character-count.mjs').CharacterCountConfig} CharacterCountConfig
* @typedef {import('./components/character-count/character-count.mjs').CharacterCountTranslations} CharacterCountTranslations
* @typedef {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} ErrorSummaryConfig
* @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} ExitThisPageConfig
* @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageTranslations} ExitThisPageTranslations
* @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
* @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
*/
export { version } from './common/govuk-frontend-version.mjs'
export { Accordion } from './components/accordion/accordion.mjs'
export { Button } from './components/button/button.mjs'
export { CharacterCount } from './components/character-count/character-count.mjs'
export { Checkboxes } from './components/checkboxes/checkboxes.mjs'
export { ErrorSummary } from './components/error-summary/error-summary.mjs'
export { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs'
export { Header } from './components/header/header.mjs'
export { NotificationBanner } from './components/notification-banner/notification-banner.mjs'
export { PasswordInput } from './components/password-input/password-input.mjs'
export { Radios } from './components/radios/radios.mjs'
export { SkipLink } from './components/skip-link/skip-link.mjs'
export { Tabs } from './components/tabs/tabs.mjs'
export { initAll, createAll } from './init.mjs'

/**
* Component config keys, e.g. `accordion` and `characterCount`
*
* @typedef {keyof Config} ConfigKey
* @typedef {import('./init.mjs').Config} Config
* @typedef {import('./init.mjs').ConfigKey} ConfigKey
*/
Expand Up @@ -28,9 +28,24 @@ describe('GOV.UK Frontend', () => {
expect(typeofInitAll).toBe('function')
})

it('exports `createAll` function', async () => {
const typeofCreateAll = await page.evaluate(
async (importPath, exportName) => {
const namespace = await import(importPath)
return typeof namespace[exportName]
},
scriptsPath.href,
'createAll'
)

expect(typeofCreateAll).toBe('function')
})

it('exports Components', async () => {
const components = exported
.filter((method) => !['initAll', 'version'].includes(method))
.filter(
(method) => !['initAll', 'createAll', 'version'].includes(method)
)
.sort()

// Ensure GOV.UK Frontend exports the expected components
Expand Down

0 comments on commit 6c9d8d7

Please sign in to comment.