App
import App, { checkMediaQueries } from '@bbc/front-end-kit/js/components/App';
The App component provides a basic structure for every project to build upon. It extends Component. It sets a scope where instances can be managed with the InstanceManager and provides logic to quickly and easily hook into changes in viewport size during render. It also watches window resize and scroll events (directly and debounced)
Getting started
The App component is meant to be extended into a MainApp file for the project it will be used for. This setup allows to handle multiple apps within the webpage as well, if required. In that case it can be named anything else, although we advice to add the "App" suffix.
class MainApp extends App {
// constructor
constructor (args = {}) {
// pass parameters to App component
super(args);
}
// methods
initReady(args) {
// logic to run before and after the app can be set to a ready state
}
// other moethods
}
The class needs to be initialised in order to run in the desired container For a MainApp (only 1 app used throughout the project) the container can be the body of the page. In other cases it might be more desired to point to a specific DOM element within the body.
window.app = new MainApp({
$el: document.body
})
Media queries
The app can automatically tell you in which predefine media query the app is running. In order to do so, media queries need to be defined in the constructor arguments before the app initializes.
The name property of the mediaquery definition will be used to call 2 possible hooks to use in the App class. Example: If the name is test then initTest and destroyTest.
Example using the _defaultsDeep method from Lodash to merge the querydefinitions into the constructor arguments. We create 2 media query definitions small and desktop.
small- Triggers when media enters or leaves
(max-width: 50em) - calls
initSmallon enter - calls
destroySmallon leave
- Triggers when media enters or leaves
desktop- Triggers when media enters or leaves
(min-width: 50em) - calls
initDesktopon enter - calls
destroyDesktopon leave
- Triggers when media enters or leaves
class MainApp extends App {
// constructor
constructor (args = {}) {
// add mediaquery definitions to args
super(_defaultsDeep(args, {
mediaQueries: [
{ name: 'small', query: '(max-width: 50em)' },
{ name: 'desktop', query: '(min-width: 50em)' }
]
}));
}
// methods
initReady(args) {
// logic to run before and after the app can be set to a ready state
}
// methods that will be called when media queries hit or exit
initSmall(args) {
}
destroySmall(args) {
}
initDesktop(args) {
}
destroyDesktop(args) {
}
}
Every method in the class receives an args object with bits of information about the context in which it is being called. With this information it provides us the freedom to easily alter the outcome of the function.
Properties it contains:
mq: The media query match object which is used internally to decide if the init or destroy should runtype: The type of action that needs to take placeevent: A key that represents some type of event that might've triggered the method (in this case it can beinitorresize)key: A shorthand identifier made of joiningtypeandeventtogether with:, example:destroy:resize.
As mentioned before, this is not very usefull when directly extending from App, but comes in very handy when extending from other classes that in turn extend from this app as they will probably pass extra information through the args object to further inform the developer about the context in which the method was triggered.
Apps that extend from App are:
- HighwayApp: Adds highway integration to the class
- ShowpadApp: Adds Showpad integration to the class (by extending Highway App)
Instance manager
App class also provides a InstanceManager instance by default so that initialisations of instances can be done very quickly with the least amount of code possible. This helps to keep the file clear and bugfree in small to very large projects.
The instance is saved in the scope on the private variable _instances but is also accessible through the public getter instance
// methods
initReady() {
this.instances.init('key', 'selector', Class)
// init hero component
this.instances.init('heroes', '.hero', Hero);
}
Extra methods
As mentioned before, App provides methods out of the box that are triggered on the scroll and resize window events. These can be added in the same scope as the query methods
Note
These methods don't need to be defined in the MainApp file when they are nog used by the project. They're just there in case we need them.
Add them only when required. Keep the MainApp as short and clean as possible
class MainApp extends App {
// constructor
constructor (args = {}) {
// pass parameters to App component
super(args);
}
// methods
initReady() {
// logic to run before and after the app can be set to a ready state
}
// window methods
resize () {
}
scroll () {
}
}
But these methods are fired instantly. For better performance it is best practice to debounce these events in order to avoid glitching of the window. These are also provided by the App class. For both resize and scroll methods, the app provides 2 debounce method types:
debounce: The classic way of debouncing events, sets a timer the moment the event is fired. When fired it waits 100ms and bundles all the same events that are fired within that time as 1 event when the timer ends.delayed: Slightly different todebouncein that it resets the timer every time the event is called within the waiting period.
class MainApp extends App {
// constructor
constructor (args = {}) {
// pass parameters to App component
super(args);
}
// methods
initReady() {
// logic to run before and after the app can be set to a ready state
}
// window methods
resize () {
}
debouncedResize () {
}
delayedResize () {
}
scroll () {
}
debouncedScroll () {
}
delayedScroll () {
}
}
API
constructor (args = {})
The constructor method initialises the new App instance.
Parameters
args: The configuration object used by the constructor to initialise the App in the desired way.$el: The root DOM element for this App instance. Passed through to Component.mediaQueries: The mediaquery definition collection it needs to initialise for
Methods
init[MediaQueryName] (args = {})
The method that will be called when the media query of the same name is activated. (Replace [MediaQueryName] with a PascaleCase version of the media query definition, "ready" => "initReady()"). These will be created for every mediaquery definition.
Parameters
args: The configuration object used by the constructor to initialise the Popup in the desired way.mq: The media query match object which is used internally to decide if the init or destroy should runtype: The type of action that needs to take placeevent: A key that represents the event that triggered the method ('init','resize', or'scroll')key: A shorthand identifier made of joiningtypeandeventtogether with:, example:destroy:resize.
destroy[MediaQueryName] (args = {})
The method that will be called when the media query of the same name is activated. (Replace [MediaQueryName] with a PascaleCase version of the media query definition, "ready" => "destroyReady()")
Parameters
args: The configuration object used by the constructor to initialise the Popup in the desired way.mq: The media query match object which is used internally to decide if the init or destroy should runtype: The type of action that needs to take placeevent: A key that represents the event that triggered the method ('init','resize', or'scroll')key: A shorthand identifier made of joiningtypeandeventtogether with:, example:destroy:resize.
resize ()
Method that will be called the moment the window resize event is triggered by the borwser window.
debouncedResize ()
Resize are debounced automatically, when the debounce timer hits, it calls this method. Use this in favor of resize to optimize performance when resizing.
delayedResize ()
Works almost like debouncedResize but resets the timer every time the resize event is triggered. Great for performance but may cause some visual glitches it the user keeps resizing the window as inits and destroy will wait until after the user starts triggering the events.
scroll()
Method that will be called the moment the window scroll event is triggered by the borwser window.
debouncedScroll ()
Scroll are debounced automatically, when the debounce timer hits, it calls this method. Use this in favor of scroll to optimize performance when resizing.
delayedScroll ()
Works almost like debouncedScroll but resets the timer every time the scroll event is triggered. Great for performance but may cause some visual glitches it the user keeps resizing the window as inits and destroy will wait until after the user starts triggering the events.
Exported methods
Exported methods are methods that come along with the class, but still need to be imported when used. They'll be able to run stand-alone but may need a scope to work with. There is a high change the default exported class is actually using that exported method internally.
checkMediaQueries(args = {}, options = {})
This methods check all the mediaqueries within the scope it is run in (looks for this._mediaQueries). It will descide which mediaquery-related method of the given scope should be run depending on the media query information stored in the same scope depending on the matchMedia state change.
Note
checkMediaQueries is called on both resize and scroll window events. The event key passed to your media-query hooks reflects the actual event type ('resize' or 'scroll').
Parameters
args: Configuration object to influence the operations of the functiontype: populates thetypeproperty of the object that will be passed to the mediaquery-related methodevent: populates theeventproperty of the object that will be passed to the mediaquery-related methodforce: Will force the mediaquery related method to run eventhough the matchMedia state wasn't changed.
Note
Both type and event will be used to create the key property of the object that will be passed to the mediaquery-related method.
Private properties
The App class uses some private properties that will be present in the scope of your app class but aren't really meant to be accessed directly.
_instances
The property where the instance manager is kept in memory. The instances getter will return the instance kept there.
this._instances = new InstanceMgr({
$parentNode: this._$el
});
_debouncers
The property where the debounce timers are stored. When the value of a debounce property equals null, it means there is not timer running at that moment.
this._debouncers = {
resize: {
timers: {
debunce: null,
delay: null
}
},
scroll: {
timers: {
debunce: null,
delay: null
}
}
}
_mediaQueries
An array of mediaquery definitions.
this._mediaQueries = [
{ name: 'ready', query: '(min-width: 0em)' },
...(args.mediaQueries || [])
];
It will always contain at least the 'ready' definition so that initReady can be called out of the box. Using initReady ensures the developer that all the super constructors had had their time to run before the custom logic is fired for the project.
External dependencies
This class uses some external dependencies that may be necessary to install using NPM.
- lodash: For some quick and useful utility methods like:
camelCase: To build method names likeinitSmallanddestroyDesktopfrom the media querynameproperty
Todo
- Provide raw API documentation