Creating a component
Once you've established a new pattern is required, it's time to create a new component. For the purpose of this tutorial, let's assume the component is called Filter Bar.
First of all, create a directory to house the component: src/_includes/components/filter-bar
Note, use kebab-case for directory & file names.
Next up, a few files will need to be created in that new folder:
filter-bar.njk
- The component codemacro.njk
- The wrapper around the componentfilter-bar.scss
- Optional: For styling the componentfilter-bar.config.js
- Optional: For rendering the component in Project Franklinfilter-bar.md
- Optional: For documenting the component
filter-bar.njk
Variables passed to this file can be accessed via the params
variable. Other than that, this is a totally encapsulated file, with no access to global variables. Here's a very basic component that outputs the title parameter:
<div class="mp c-filter-bar">
<h2 class="c-h c-h--step-2">{{ params.title }}</h2>
</div>
You can also include other components inside this one. You can import it via the other component's macro, then call it like a function:
{%- from "components/icon/macro.njk" import icon -%}
<div class="mp c-filter-bar">
<h2 class="c-h c-h--step-2">{{ params.title }}</h2>
{{ icon({
id: 'twitter'
}) }}
</div>
These components aren't being included directly into application code, but will be used for reference, so think through the component composition and create sensible logic that can be recreated in Razor, DXA, PHP and any other language this is converted to. Read more about Nunjuck templating.
macro.njk
Every component has a corresponding macro file, allowing us to instantiate a component in a totally encapsulated area. The macro itself uses another .njk
file to render the component, allowing us to render the component in the same way within Project Franklin.
{%- from "components/component/component.njk" import c -%}
{%- macro filterBar(params) -%}
{{ c({ name: 'filter-bar' }, params) }}
{%- endmacro -%}
Note, use camelCase for the macro name.
This c
component takes two parameters:
- Component object with three parameters
name
: a string matching the.njk
file namefolder
: Optional: by default, the same as thename
root
: Optional: by default,../
, effectively thesrc/_includes/components
directory
- The
params
object, passed in from the macros own parameters
filter-bar.scss
This SCSS
file has access to all the mixins, functions and foundational elements documented in Project Franklin. To add it to the main bundle, head to src/assets/scss/components/index.scss
and add a new line pointing to your file:
@import '~comp/filter-bar/filter-bar.scss';
Note, ~comp
is a shorthand to the src/_includes/components
directory.
filter-bar.config.js
This file drives the component rendering for Project Franklin. It takes cues from Fractal and Storybook, and is effectively a big object that tells Project Franklin how to render the component, and what data to render it with. Let's run through the minimum viable config file:
module.exports = {
title: 'Filter bar',
component: {
name: 'filter-bar',
},
context: {
title: 'Filter bar title'
}
}
There are three mandatory sections:
title
: the name of componentcomponent
: the same object we passed into thec()
component, controlingname
,folder
, androot
context
: the data used to render the component
If this is all correct, you should see 'Filter bar' in the components section of the Project Franklin sidebar.
Additional parameters
preview
: Which 'preview' file should the component use to render. These live insrc/_includes/components/component
and allow you to test a component in a grid, with a different colour background, or in a totally different setting. To render your component with a new preview:- Create the
preview-[PREVIEW NAME].njk
file - Add any wrapping markup
- Insert:
{{ comp | safe }}
wherever you wish to output the component - Add
preview: '[PREVIEW NAME]'
to your original component
- Create the
docs
: Does this component have a documentation markdown file (more on this later)?figma
: A link to the originally designed component within Figmastatus
: A string representation of how 'complete' the component is. This is 'slugified' and can be styled according to the nameversion
: A numeric value that can be increased every time you change the componentcategory
: Components can be grouped in the Project Franklin sidebar. Categories are stored globally. To reference a category:- Require the categories file in
filter-bar.config.js
:
const categories = require('../component/categories');
- If you need a new category, create a new key/value pair in the above file
- Add the
category
parameter:category: categories.card
- Require the categories file in
props
: an array of prop tables, allowing you to document the inputs and outputs of the component:
{
props: [
{
title: 'Table title', // optional, defaults to 'Props'
table: [
['Parameter name', 'Parameter type', 'Parameter description']
]
}
]
}
variants
: Variants are used to render the same component with different data, allowing you to test the parameters thoroughly, as well as test for extreme amounts of content and edge-cases. In Project Franklin, the variant objects are written like components within components. You must pass in a newtitle
and acontext
object. Project Franklin will then merge the original component data with this new variant object to create a rendering context. This means you only have to pass the differences between the components, not every piece of context. It also means you can supply a differentpreview
element, allowing you to test the same component in different scenarios.
{
variants: [
{
title: 'With long title',
context: {
title: 'Cras mattis consectetur purus sit amet fermentum. Etiam porta sem malesuada magna mollis euismod.'
}
},
{
title: 'In a grid',
preview: 'cards-three',
context: {}
}
]
}
filter-bar.md
Finally, you can create a Markdown file for a component. This is a space for you to write about the component in whatever way you deem helpful. This could be research insights, documentation of internal decisions, a changelog - it's up to you. This renders using the prose()
component, so any styling from Project Franklin will be included here.