Skip to content

Commit

Permalink
Add errorCallback to createAll
Browse files Browse the repository at this point in the history
New parameter for `createAll`, `errorCallback` allows user to supply a
callback function that's executed if any of the components fail to
initialize. Add additional test for the `errorCallback` if specified.
  • Loading branch information
patrickpatrickpatrick committed Aug 21, 2024
1 parent e944196 commit e03e380
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
32 changes: 32 additions & 0 deletions packages/govuk-frontend/src/govuk/init.jsdom.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,38 @@ describe('createAll', () => {
}
}

it('executes callback if specified', () => {
document.body.innerHTML = `<div data-module="mock-component" data-boom></div>`

const errorCallback = jest.fn((error, context) => {
console.log(error)
console.log(context)
})

// Silence warnings in test output, and allow us to 'expect' them
jest.spyOn(global.console, 'log').mockImplementation()

expect(() => {
createAll(
MockComponentThatErrors,
{ attribute: 'random' },
document,
errorCallback
)
}).not.toThrow()

expect(errorCallback).toHaveBeenCalled()

expect(global.console.log).toHaveBeenCalledWith(expect.any(Error))
expect(global.console.log).toHaveBeenCalledWith(
expect.objectContaining({
component: MockComponentThatErrors,
config: { attribute: 'random' },
element: document.querySelector('[data-module="mock-component"]')
})
)
})

it('catches errors thrown by components and logs them to the console', () => {
document.body.innerHTML = `<div data-module="mock-component" data-boom></div>`

Expand Down
29 changes: 27 additions & 2 deletions packages/govuk-frontend/src/govuk/init.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ function initAll(config) {
})
}

/**
* @template {CompatibleClass} T
* @callback errorCallback
* @param {Error} error - Thrown error
* @param {ErrorContext<T>} context - Object containing the element, component class and configuration
*/

/**
* Create all instances of a specific component on the page
*
Expand All @@ -67,9 +74,10 @@ function initAll(config) {
* @param {T} Component - class of the component to create
* @param {T["defaults"]} [config] - config for the component
* @param {Element|Document} [$scope] - scope of the document to search within
* @param {errorCallback<T>} [errorCallback] - scope of the document to search within
* @returns {Array<InstanceType<T>>} - array of instantiated components
*/
function createAll(Component, config, $scope = document) {
function createAll(Component, config, $scope = document, errorCallback) {
const $elements = $scope.querySelectorAll(
`[data-module="${Component.moduleName}"]`
)
Expand All @@ -90,7 +98,16 @@ function createAll(Component, config, $scope = document) {
? new Component($element, config)
: new Component($element)
} catch (error) {
console.log(error)
if (errorCallback) {
errorCallback(/** @type {Error} */ (error), {
element: $element,
component: Component,
config
})
} else {
console.log(error)
}

return null
}
})
Expand Down Expand Up @@ -144,3 +161,11 @@ export { initAll, createAll }
*
* @typedef {keyof Config} ConfigKey
*/

/**
* @template {CompatibleClass} T
* @typedef {object} ErrorContext
* @property {Element} element - Element used for component module initialisation
* @property {T} component - Class of component
* @property {T["defaults"]} config - Config supplied to component
*/

0 comments on commit e03e380

Please sign in to comment.