diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d5f46a8a9..f9fe876ca2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ For advice on how to use these release notes see [our guidance on staying up to ## Unreleased +### Use our base configurable component to build your own configurable component + +We've added a `ConfigurableComponent` class to help you build your own configurable components. It extends our base component class and so it allows you to focus on your components' specific features by handling these shared behaviours across components: + +- checking that GOV.UK Frontend is supported +- checking that the component is not already initialised on its root element +- checking the type of the root element and storing it for access within the component as this.$root +- taking a configuration object as a parameter and then storing it for access within the component as this.config +- merging a passed configuration object with configuration options specified on the data attributes of the root element + +We introduced this change in: + +- [#5499: Rename GOVUKFrontendComponentConfigurable, export ConfigurableComponent](https://github.com/alphagov/govuk-frontend/pull/5499) +- [#5456: Refactor Accordion to extend from a GOVUKFrontendConfigurableComponent](https://github.com/alphagov/govuk-frontend/issues/5456) + ## v5.7.1 (Fix release) To install this version with npm, run `npm install govuk-frontend@5.7.1`. You can also find more information about [how to stay up to date](https://frontend.design-system.service.gov.uk/staying-up-to-date/#updating-to-the-latest-version) in our documentation. diff --git a/packages/govuk-frontend/src/govuk/all.mjs b/packages/govuk-frontend/src/govuk/all.mjs index fe0de924eb..684d6984c9 100644 --- a/packages/govuk-frontend/src/govuk/all.mjs +++ b/packages/govuk-frontend/src/govuk/all.mjs @@ -15,6 +15,7 @@ export { Tabs } from './components/tabs/tabs.mjs' export { initAll, createAll } from './init.mjs' export { isSupported } from './common/index.mjs' export { GOVUKFrontendComponent as Component } from './govuk-frontend-component.mjs' +export { ConfigurableComponent } from './common/configuration.mjs' /** * @typedef {import('./init.mjs').Config} Config diff --git a/packages/govuk-frontend/src/govuk/all.puppeteer.test.js b/packages/govuk-frontend/src/govuk/all.puppeteer.test.js index 6d6a61491b..254fbaa26b 100644 --- a/packages/govuk-frontend/src/govuk/all.puppeteer.test.js +++ b/packages/govuk-frontend/src/govuk/all.puppeteer.test.js @@ -69,6 +69,7 @@ describe('GOV.UK Frontend', () => { 'CharacterCount', 'Checkboxes', 'Component', + 'ConfigurableComponent', 'ErrorSummary', 'ExitThisPage', 'Header', diff --git a/packages/govuk-frontend/src/govuk/common/configuration.mjs b/packages/govuk-frontend/src/govuk/common/configuration.mjs index 78595cffaf..11a6a57d66 100644 --- a/packages/govuk-frontend/src/govuk/common/configuration.mjs +++ b/packages/govuk-frontend/src/govuk/common/configuration.mjs @@ -15,7 +15,7 @@ export const configOverride = Symbol.for('configOverride') * @template {Element & { dataset: DOMStringMap }} [RootElementType=HTMLElement] * @augments GOVUKFrontendComponent */ -export class GOVUKFrontendComponentConfigurable extends GOVUKFrontendComponent { +export class ConfigurableComponent extends GOVUKFrontendComponent { /** * configOverride * diff --git a/packages/govuk-frontend/src/govuk/common/configuration/govuk-frontend-component-configurable.jsdom.test.mjs b/packages/govuk-frontend/src/govuk/common/configuration/configurable-component.jsdom.test.mjs similarity index 78% rename from packages/govuk-frontend/src/govuk/common/configuration/govuk-frontend-component-configurable.jsdom.test.mjs rename to packages/govuk-frontend/src/govuk/common/configuration/configurable-component.jsdom.test.mjs index 5e1f4831c3..ea96c0f6b5 100644 --- a/packages/govuk-frontend/src/govuk/common/configuration/govuk-frontend-component-configurable.jsdom.test.mjs +++ b/packages/govuk-frontend/src/govuk/common/configuration/configurable-component.jsdom.test.mjs @@ -1,10 +1,7 @@ import { ConfigError } from '../../errors/index.mjs' -import { - GOVUKFrontendComponentConfigurable, - configOverride -} from '../configuration.mjs' +import { ConfigurableComponent, configOverride } from '../configuration.mjs' -describe('GOVUKFrontendComponentConfigurable', () => { +describe('ConfigurableComponent', () => { beforeEach(() => { // Jest does not tidy the JSDOM document between tests // so we need to take care of that ourselves @@ -18,7 +15,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { describe('throws error', () => { it('if no schema defined', () => { - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { static moduleName = 'config-component' static defaults = { @@ -26,7 +23,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { } } - expect(() => new ConfigurableComponent(document.body)).toThrow( + expect(() => new MockConfigurableComponent(document.body)).toThrow( new ConfigError( 'config-component: Config passed as parameter into constructor but no schema defined' ) @@ -34,7 +31,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { }) it('if no defaults defined', () => { - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { static moduleName = 'config-component' static schema = { @@ -44,7 +41,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { } } - expect(() => new ConfigurableComponent(document.body)).toThrow( + expect(() => new MockConfigurableComponent(document.body)).toThrow( new ConfigError( 'config-component: Config passed as parameter into constructor but no defaults defined' ) @@ -58,7 +55,7 @@ describe('GOVUKFrontendComponentConfigurable', () => {
` - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { static moduleName = 'config-component' static schema = { @@ -74,7 +71,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { const testComponent = document.querySelector('#test-component') - const configComponent = new ConfigurableComponent(testComponent) + const configComponent = new MockConfigurableComponent(testComponent) expect(configComponent._config).toMatchObject({ randomAttribute: 0 }) }) @@ -84,7 +81,7 @@ describe('GOVUKFrontendComponentConfigurable', () => {
` - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { static moduleName = 'config-component' static schema = { @@ -100,7 +97,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { const testComponent = document.querySelector('#test-component') - const configComponent = new ConfigurableComponent(testComponent) + const configComponent = new MockConfigurableComponent(testComponent) expect(configComponent._config).toMatchObject({ randomAttribute: 42 }) }) @@ -110,7 +107,7 @@ describe('GOVUKFrontendComponentConfigurable', () => {
` - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { static moduleName = 'config-component' static schema = { @@ -126,7 +123,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { const testComponent = document.querySelector('#test-component') - const configComponent = new ConfigurableComponent(testComponent, { + const configComponent = new MockConfigurableComponent(testComponent, { randomAttribute: 100 }) @@ -138,7 +135,7 @@ describe('GOVUKFrontendComponentConfigurable', () => {
` - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { static moduleName = 'config-component' static schema = { @@ -154,7 +151,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { const testComponent = document.querySelector('#test-component') - const configComponent = new ConfigurableComponent(testComponent, { + const configComponent = new MockConfigurableComponent(testComponent, { randomAttribute: 100 }) @@ -170,7 +167,7 @@ describe('GOVUKFrontendComponentConfigurable', () => {
` - class ConfigurableComponent extends GOVUKFrontendComponentConfigurable { + class MockConfigurableComponent extends ConfigurableComponent { [configOverride](config) { return configOverrideFunction(config) } @@ -190,7 +187,7 @@ describe('GOVUKFrontendComponentConfigurable', () => { const testComponent = document.querySelector('#test-component') - const configComponent = new ConfigurableComponent(testComponent, { + const configComponent = new MockConfigurableComponent(testComponent, { randomAttribute: '14' }) diff --git a/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs b/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs index bc6e592cf3..97d52624b6 100644 --- a/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs +++ b/packages/govuk-frontend/src/govuk/components/accordion/accordion.mjs @@ -1,4 +1,4 @@ -import { GOVUKFrontendComponentConfigurable } from '../../common/configuration.mjs' +import { ConfigurableComponent } from '../../common/configuration.mjs' import { ElementError } from '../../errors/index.mjs' import { I18n } from '../../i18n.mjs' @@ -15,9 +15,9 @@ import { I18n } from '../../i18n.mjs' * attribute, which also provides accessibility. * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class Accordion extends GOVUKFrontendComponentConfigurable { +export class Accordion extends ConfigurableComponent { /** @private */ i18n diff --git a/packages/govuk-frontend/src/govuk/components/button/button.mjs b/packages/govuk-frontend/src/govuk/components/button/button.mjs index 5f0c8e8d42..b081708286 100644 --- a/packages/govuk-frontend/src/govuk/components/button/button.mjs +++ b/packages/govuk-frontend/src/govuk/components/button/button.mjs @@ -1,4 +1,4 @@ -import { GOVUKFrontendComponentConfigurable } from '../../common/configuration.mjs' +import { ConfigurableComponent } from '../../common/configuration.mjs' const DEBOUNCE_TIMEOUT_IN_SECONDS = 1 @@ -6,9 +6,9 @@ const DEBOUNCE_TIMEOUT_IN_SECONDS = 1 * JavaScript enhancements for the Button component * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class Button extends GOVUKFrontendComponentConfigurable { +export class Button extends ConfigurableComponent { /** * @private * @type {number | null} diff --git a/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs b/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs index dd0c276b5c..57b8268d9c 100644 --- a/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs +++ b/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs @@ -1,7 +1,7 @@ import { closestAttributeValue } from '../../common/closest-attribute-value.mjs' import { validateConfig, - GOVUKFrontendComponentConfigurable, + ConfigurableComponent, configOverride } from '../../common/configuration.mjs' import { formatErrorMessage } from '../../common/index.mjs' @@ -19,9 +19,9 @@ import { I18n } from '../../i18n.mjs' * of the available characters/words has been entered. * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class CharacterCount extends GOVUKFrontendComponentConfigurable { +export class CharacterCount extends ConfigurableComponent { /** @private */ $textarea diff --git a/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.mjs b/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.mjs index 5d34dd7785..57e4681923 100644 --- a/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.mjs +++ b/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.mjs @@ -1,4 +1,4 @@ -import { GOVUKFrontendComponentConfigurable } from '../../common/configuration.mjs' +import { ConfigurableComponent } from '../../common/configuration.mjs' import { getFragmentFromUrl, setFocus } from '../../common/index.mjs' /** @@ -8,9 +8,9 @@ import { getFragmentFromUrl, setFocus } from '../../common/index.mjs' * configuration. * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class ErrorSummary extends GOVUKFrontendComponentConfigurable { +export class ErrorSummary extends ConfigurableComponent { /** * @param {Element | null} $root - HTML element to use for error summary * @param {ErrorSummaryConfig} [config] - Error summary config diff --git a/packages/govuk-frontend/src/govuk/components/exit-this-page/exit-this-page.mjs b/packages/govuk-frontend/src/govuk/components/exit-this-page/exit-this-page.mjs index 71fcedb19b..891fc3f2f2 100644 --- a/packages/govuk-frontend/src/govuk/components/exit-this-page/exit-this-page.mjs +++ b/packages/govuk-frontend/src/govuk/components/exit-this-page/exit-this-page.mjs @@ -1,4 +1,4 @@ -import { GOVUKFrontendComponentConfigurable } from '../../common/configuration.mjs' +import { ConfigurableComponent } from '../../common/configuration.mjs' import { ElementError } from '../../errors/index.mjs' import { I18n } from '../../i18n.mjs' @@ -6,9 +6,9 @@ import { I18n } from '../../i18n.mjs' * Exit this page component * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class ExitThisPage extends GOVUKFrontendComponentConfigurable { +export class ExitThisPage extends ConfigurableComponent { /** @private */ i18n diff --git a/packages/govuk-frontend/src/govuk/components/notification-banner/notification-banner.mjs b/packages/govuk-frontend/src/govuk/components/notification-banner/notification-banner.mjs index c4f175e9fd..32caded5be 100644 --- a/packages/govuk-frontend/src/govuk/components/notification-banner/notification-banner.mjs +++ b/packages/govuk-frontend/src/govuk/components/notification-banner/notification-banner.mjs @@ -1,13 +1,13 @@ -import { GOVUKFrontendComponentConfigurable } from '../../common/configuration.mjs' +import { ConfigurableComponent } from '../../common/configuration.mjs' import { setFocus } from '../../common/index.mjs' /** * Notification Banner component * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class NotificationBanner extends GOVUKFrontendComponentConfigurable { +export class NotificationBanner extends ConfigurableComponent { /** * @param {Element | null} $root - HTML element to use for notification banner * @param {NotificationBannerConfig} [config] - Notification banner config diff --git a/packages/govuk-frontend/src/govuk/components/password-input/password-input.mjs b/packages/govuk-frontend/src/govuk/components/password-input/password-input.mjs index 36d716a50e..f031f9cd5e 100644 --- a/packages/govuk-frontend/src/govuk/components/password-input/password-input.mjs +++ b/packages/govuk-frontend/src/govuk/components/password-input/password-input.mjs @@ -1,5 +1,5 @@ import { closestAttributeValue } from '../../common/closest-attribute-value.mjs' -import { GOVUKFrontendComponentConfigurable } from '../../common/configuration.mjs' +import { ConfigurableComponent } from '../../common/configuration.mjs' import { ElementError } from '../../errors/index.mjs' import { I18n } from '../../i18n.mjs' @@ -7,9 +7,9 @@ import { I18n } from '../../i18n.mjs' * Password input component * * @preserve - * @augments GOVUKFrontendComponentConfigurable + * @augments ConfigurableComponent */ -export class PasswordInput extends GOVUKFrontendComponentConfigurable { +export class PasswordInput extends ConfigurableComponent { /** @private */ i18n