Open
Description
Is your enhancement related to a problem? Please describe.
Based on a recent Slack conversation: https://10up.slack.com/archives/G11CA43FW/p1643728684014569
@rdimascio and I believe it would be a great idea to include this boilerplate JavaScript code into frontend.js
so that engineers can better work with dynamically imported JavaScript components:
import importAndRun from './util/import-and-run';
const COMPONENTS = {
'components/accordion-block': {
selectors: ['.accordion'],
},
'components/site-header': {
selectors: ['body'],
},
'components/site-footer': {
selectors: ['body'],
},
'components/story-navigation': {
selectors: ['.home-nav'],
},
'components/service-explorer': {
selectors: ['.service-explorer'],
},
'components/story-comments': {
selectors: ['.story-comments'],
},
'components/social-share': {
selectors: ['.social-share'],
},
'components/child-filters': {
selectors: ['.photolisting__aside-filters'],
},
'components/cookie-consent': {
selectors: ['#cookie-consent'],
},
};
const initComponents = () => {
Object.entries(COMPONENTS).forEach(([key, value]) => {
importAndRun(key, value.selectors, value.callback);
});
};
const init = () => {
window.requestAnimationFrame(() => {
document.body.classList.add('js-loaded');
});
initComponents();
};
/* eslint-disable-next-line @wordpress/no-global-event-listener */
document.addEventListener('DOMContentLoaded', init);
The above allows us to cleanly and easily keep track of components and import / run them as required.
This task would also require us adding the importAndRun
utility to a new assets/js/util
folder.
It's our hope that this will close the loop on dynamically imported JavaScript as the scaffold already supports Code Splitting out the box.
/**
* Default callback when using `importAndRun`
*
* @param {Object} Module - Factory function or class
*/
const DEFAULT_CALLBACK = (Module) => {
try {
const module = new Module();
module.init();
} catch (error) {
// Something failed
}
};
/**
* Dynamically import a module if it appears on the page.
*
* @param {string} module - Path to module
* @param {string[]} selectors - List of selectors. Used to conditionally load the module
* @param {Function} cb - Callback function to run after loading the module
* @return {Promise} - Returns the dynamic import
*/
export default (module, selectors = ['body'], cb = DEFAULT_CALLBACK) => {
let shouldImport = false;
if (Array.isArray(selectors) && selectors.length) {
shouldImport = Boolean(
selectors.find((selector) => {
return Boolean(document.querySelector(selector));
}),
);
}
if (!shouldImport) {
Promise.resolve();
}
/* eslint-disable-next-line consistent-return */
return import(`./../${module}`).then(({ default: module }) => {
if (cb && typeof cb === 'function') {
cb(module);
}
});
};
Designs
No response
Describe alternatives you've considered
No response
Code of Conduct
- I agree to follow this project's Code of Conduct