-
Notifications
You must be signed in to change notification settings - Fork 2
4. Writing a pattern (aka block)
The Firefly pattern library supports blocks in two formats; XSLT and React Templates. A block can contain definitions for both.
Let's say we want to write a new block called ff_module-salad-spinner
. This pattern presents the user with a graphical representation of a number of students, with a button that invites the user to "Spin!". Clicking the button will result in a wonderful animation of spinning students at the end of which on is chosen at random. It could look something like this:
The data for this wonderful block can either come from the pages XSL or it could be rendered as a React Component and fed its data via Ajax.
The first thing we'll have to do is create a new folder for our block under /blocks/ff_modules/ff_module-salad-spinner
.
Next, we'll want to scaffold out the files we'll be using, so we add 5 new files:
-
ff_module-salad-spinner.md
(the pattern definition) -
ff_module-salad-spinner.xml
(the pattern data) -
ff_module-salad-spinner.xsl
(the pattern HTML) -
ff_module-salad-spinner.less
(the pattern styles) -
ff_module-salad-spinner.js
(the pattern view logic)
The Markdown file contains all the information the pattern library needs to know how to build this block. So, for the salad spinner this file may look something like this:
---
data:
classes: "ff_module-otherblock__item ff_mdoule-otherblock__item--is-active"
modifier: "active"
data:
-
attr: ff-data-attr
value: "salad-spinner-container"
users:
-
name: "Sally Student"
id: "1"
image: "sally.jpg"
url: "/students/profiles/1"
-
name: "Peter Pupil"
id: "2"
image: "peter.jpg"
url: "/students/profiles/2"
-
name: "Sarah Studiesalot"
id: "3"
image: "sarah.jpg"
url: "/students/profiles/3"
requires:
- ff_module-user-image
---
There's quite a bit going on here, so let's look at the XML file in order to unpick this somewhat.
The pattern library uses Swig as a templating langage to scaffold XML files, in order that they can use data from the pattern's .md
definition file. For the salad spinner, it'd look something like this:
<salad-spinner
{% if modifier %} modifier="{{modifier}}" {% endif %}
{% if classes %} classes="{{classes}}" {% endif %}
>
{% for d in data %}
<data attr="{{d.attr}}">{{d.value}}</data>
{% endfor %}
{% for u in users %}
<user name="{{u.name}}" id="{{u.id}}" image="{{u.image}}" url="{{u.url}}"/>
{% endfor %}
</salad-spinner>
The pattern library would then compile this to:
<salad-spinner modifier="active" classes="ff_module-otherblock__item ff_mdoule-otherblock__item--is-active">
<data attr="ff-data-attr">salad-spinner-conatiner</data>
<user name="Sally Student" id="1" image="sally.jpg" url="/students/profiles/1"/>
<user name="Peter Pupil" id="2" image="peter.jpg" url="/students/profiles/2"/>
<user name="Sarah Studiesalot" id="3" image="sarah.jpg" url="/students/profiles/3"/>
</salad-spinner>
Now comes the fun bit! (Honest)!
We'll use the xml
we've defined to create a XSLT file that will display the salad spinner. As you may have already guessed we're building blocks in a slightly convoluted way so that they can be used in the pattern library and called directly into Firefly with the minimal of fuss. Anyway, onto the promised fun:
<xsl:template name="ff_module-salad-spinner">
<xsl:param name="data" />
<div>
<!-- add in any modifier OR third party block classes -->
<xsl:attribute name="class">
<xsl:text>ff_module-salad-spinner</xsl:text>
<xsl:text> </xsl:text><xsl:value-of select="$data/salad-spinner/@classes"/>
<xsl:text> ff_module-salad-spinner--</xsl:text><xsl:value-of select="$data/salad-spinner/@modifier"/>
</xsl:attribute>
<!-- add in any data attrs -->
<xsl:if test="$data/salad-spinner/data">
<xsl:for-each select="$data/salad-spinner/data">
<xsl:attribute name="{./@attr}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</xsl:if>
<ul class="ff_module-salad-spinner__leaves">
<xsl:for-each select="$data/salad-spinner/users">
<li class="ff_module-salad-spinner__leaf">
<!-- call in user-image block -->
<xsl:call-template name="ff_module-user-image">
<xsl:with-param name="data" select="exsl:node-set(.)"/>
</xsl:call-template>
</li>
</xsl:for-each>
</ul>
<button class="ff_module-salad-spinner__activate">Spin!</button>
</div>
</xsl:template>
Whoa! There's a lot going on there. Don't panic my friend, let's go through it bit-by-bit and discover its inner workings:
Firstly, we define a new XSLT template <xsl:template name="ff_module-salad-spinner">
.
Next, we need to pull in any data that's being passed to this block. <xsl:param name="data" />
.
A quick note on <xsl:param name="data" />
. We ALWAYS pass a node-set
into a block as <data/>
. The pattern library expects this, and won't work without it. You have been warned!
Next we do some checking for any classes we need to add and any data attributes we need to add in:
<!-- add in any modifier OR third party block classes -->
<xsl:attribute name="class">
<xsl:text>ff_module-salad-spinner</xsl:text>
<xsl:text> </xsl:text><xsl:value-of select="$data/salad-spinner/@classes"/>
<xsl:text> ff_module-salad-spinner--</xsl:text><xsl:value-of select="$data/salad-spinner/@modifier"/>
</xsl:attribute>
<!-- add in any data attrs -->
<xsl:if test="$data/salad-spinner/data">
<xsl:for-each select="$data/salad-spinner/data">
<xsl:attribute name="{./@attr}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</xsl:if>
The only thing that's worth going over in more detail is this call:
<!-- call in user-image block -->
<xsl:call-template name="ff_module-user-image">
<xsl:with-param name="data" select="exsl:node-set(.)"/>
</xsl:call-template>
Here, we're using another block by passing it XML
in a format it expects. In this case the format of the XML
for ff_module-salad-spinner
's node <user/>
is the same format that ff_module-user-image
expects to be passed.
The only caveat here is that we need to pass any node data as a node-set
.
There's more information on this in the section on wiring up blocks into Firefly.
Not much to say on these, suffice to say the less and js files get automatically compiled and added to the project by gulp — there's no need to reference them from anywhere.
Coming Soon