Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add createAll function to initialise individual components #4975

Merged
merged 7 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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'
Comment on lines +1 to +14
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note With the removal of initAll from all.mjs, all the file does is re-exporting the difference pieces of our library that constitute our public API, so we can switch to the export { NAMED_IMPORT } from 'module' syntax rather than have a bunch of imports followed by an export.


/**
* Component config keys, e.g. `accordion` and `characterCount`
*
* @typedef {keyof Config} ConfigKey
* @typedef {import('./init.mjs').Config} Config
* @typedef {import('./init.mjs').ConfigKey} ConfigKey
*/
Original file line number Diff line number Diff line change
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
Loading