Skip to content

Commit 1c6e98d

Browse files
36degreesowenatgov
authored andcommitted
Split component instantiation into separate function
TypeScript doesn’t really have the concept of passing a class to a function, so we have to fake it by defining a type that is constructable and has the properties we need to be able to instantiate it (a `moduleName` and optionally a set of `defaults` from which we can infer the config type). This is based on approaches from: - https://stackoverflow.com/questions/71086547/build-a-function-that-accepts-a-class-in-typescript - microsoft/TypeScript#17572 Co-authored-by: Owen Jones <[email protected]>
1 parent 3f313d8 commit 1c6e98d

File tree

1 file changed

+43
-13
lines changed
  • packages/govuk-frontend/src/govuk

1 file changed

+43
-13
lines changed

packages/govuk-frontend/src/govuk/init.mjs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,55 @@ function initAll(config) {
5050
const $scope = config.scope ?? document
5151

5252
components.forEach(([Component, config]) => {
53-
const $elements = $scope.querySelectorAll(
54-
`[data-module="${Component.moduleName}"]`
55-
)
53+
createAll(Component, config, $scope)
54+
})
55+
}
5656

57-
$elements.forEach(($element) => {
58-
try {
59-
// Only pass config to components that accept it
60-
'defaults' in Component
61-
? new Component($element, config)
62-
: new Component($element)
63-
} catch (error) {
64-
console.log(error)
65-
}
66-
})
57+
/**
58+
* Create all instances of a specific component on the page
59+
*
60+
* Uses the `data-module` attribute to find all elements matching the specified
61+
* component on the page, creating instances of the component object for each
62+
* of them.
63+
*
64+
* Any component errors will be caught and logged to the console.
65+
*
66+
* @template {CompatibleClass} T
67+
* @param {T} Component - class of the component to create
68+
* @param {T["defaults"]} [config] - config for the component
69+
* @param {Element|Document} [$scope] - scope of the document to search within
70+
*/
71+
function createAll(Component, config, $scope = document) {
72+
const $elements = $scope.querySelectorAll(
73+
`[data-module="${Component.moduleName}"]`
74+
)
75+
76+
$elements.forEach(($element) => {
77+
try {
78+
// Only pass config to components that accept it
79+
'defaults' in Component
80+
? new Component($element, config)
81+
: new Component($element)
82+
} catch (error) {
83+
console.log(error)
84+
}
6785
})
6886
}
6987

7088
export { initAll }
7189

90+
/* eslint-disable jsdoc/valid-types --
91+
* `{new(...args: any[] ): object}` is not recognised as valid
92+
* https://github.com/gajus/eslint-plugin-jsdoc/issues/145#issuecomment-1308722878
93+
* https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131
94+
**/
95+
96+
/**
97+
* @typedef {{new (...args: any[]): unknown, defaults?: object, moduleName: string}} CompatibleClass
98+
*/
99+
100+
/* eslint-enable jsdoc/valid-types */
101+
72102
/**
73103
* Config for all components via `initAll()`
74104
*

0 commit comments

Comments
 (0)