-
Notifications
You must be signed in to change notification settings - Fork 0
createbehavior
createBehavior
is a factory function for defining a new behavior.
Here's an example, where we define an "Accordion" behavior:
// Accordion.js
import { createBehavior } from '@area17/a17-helpers';
const Accordion = createBehavior('Accordion', {
myCustomFn() {
// ...
},
}, {
init() {
console.log(this.node); // HTMLElement
this.myCustomFn();
}
});
export default Accordion;
Arguments
-
Behavior name
string
(Needs to be explicitly defined for usage inside behavior, and also for debugging) -
Instance methods
Object<string,Function>
-
Lifecycle
Object<string,any>
See Lifecycle
The following lifecycle properties are supported:
-
init
Function
- Called onaccordionInstance.init()
-
destroy
Function
- Called onaccordionInstance.destroy()
-
enabled
Function
- Called onaccordionInstance.enable()
(See Breakpoints) -
disabled
Function
- Called onaccordionInstance.disable()
(See Breakpoints) -
mixins
Array<Object>
- An array of mixin objects (See Mixins)
Whilst you can still define a behavior as a standard function, using createBehavior
enables the following features:
All data attributes on the root node with the following format: data-[BehaviorName]-[myoption]
will be captured into the this.options
object as this.options.myoption
.
<div data-behavior="Accordion" data-Accordion-animate="true"></div>
// Accordion.js
init() {
console.log(Boolean(this.options.animate)); // => true
}
Note: There are no types for these options, and they are not parsed like jQuery data attributes. (TODO!)
Child elements can be queried concisely, like so:
<div data-behavior="Accordion">
<div data-Accordion-title>My Title</div>
<div data-Accordion-panel></div>
<div data-Accordion-panel></div>
</div>
// Accordion.js
init() {
this.panels = this.getChildren('panel'); // => NodeList
this.title = this.getChild('title'); // => HTMLElement
}
Behaviors can be scoped to a breakpoint. This creates opportunities for more generic behaviors, that can differ from instance-to-instance. For example, some instances of the Accordion behavior might only apply to mobile.
The breakpoint is defined by this.options.media
, which must be a valid breakpoint alias. (See Configuring breakpoints)
<div data-behavior="Accordion Overlay" data-Accordion-media="small" data-Overlay-media="large">
</div>
// Accordion.js
{
enabled() {
// Called when behavior's breakpoint becomes valid (either after init() or on window resize).
// Also fires after init() if there's no breakpoint defined.
},
disabled() {
// Called when behavior's breakpoint becomes invalid (either after init() or on window resize).
// Also fires when the behavior is destroyed
},
}
It's up to each behavior implementation how its state is handled across these lifecycle methods. For example, an Accordion behavior might need to close all the panels when it becomes disabled (for which you'd make use of enabled/disabled
; or alternatively, the panels might need to persist on resize (for which you'd keep it in init/destroy
).
Before any breakpoints are instantiated, you must ensure have registered the breakpoint config with createBehavior.setBreakpointConfig
:
// app.js
import { createBehavior } from '@area17/a17-helpers';
window.A17.onReady = function() {
createBehavior.setBreakpointConfig({
small: { start: null, end: 999 },
large: { start: 1000, end: null },
});
};
(The config format is similar to $breakpoints
_variables.scss
. TODO: automate this?)
Whilst on most occasions, the behavior will be instantiated declaratively from HTML attributes, it's also possible to instantiate from JavaScript:
const OtherBehavior = createBehavior('OtherBehavior', {}, {
init() {
this.accordion = new Accordion(this.node, {
options: {
animate: true,
},
children: {
title: this.getChild('othertitle'),
panel: this.getChildren('otherpanel'),
}
});
this.accordion.init();
},
destroy() {
this.accordion.destroy();
}
});
When constructing a behavior from JS, the arguments are:
- The node element to be used as
this.node
(HTMLElement
) - Configuration (
Object
): -
options (
Object<string, any>
) -
children (
Object<string, HTMLElement|NodeList>
) An object of pre-queried children
There is an more concise syntax if you don't need fine-grained control over the lifecycle:
init() {
this.accordion = this.addSubBehavior(Accordion, this.node, /** config **/)
}
addSubBehavior
will also run its init()
immediately, and also run its destroy()
whenever the parent behavior is destroyed.
Shared functionality can be composed onto a behavior, without it having to be behaviors themselves.
const MyMixin = {
init() {
// ... called after the behavior's own init()
},
destroy() {
// ... called after the behavior's own destroy()
},
otherFn() {
// ... any other functions are augmented onto the behavior
}
};
const Accordion = createBehavior('Accordion', {}, {
mixins: [MyMixin] // Can contain multiple mixins
});
Use mixins sparingly. Using sub-behaviors instead makes it more reusable overall, as they can also be used by themselves.