Storybook & Atomic Design - 1.11. - Building Our Header
by Jack Pritchard
Hey! Just so you know, this article is over 2 years old. Some of the information in it might be outdated, so take it with a grain of salt. I'm not saying it's not worth a read, but don't take everything in it as gospel. If you're curious about something, it never hurts to double-check with a more up-to-date source!
In this lesson, we'll be building our Header component. The Header component falls under the concept of an organism, as it could contain molecules and atoms as children nodes.
Initially, we will focus on passing one prop to our Header component. The prop will be -
navigation - array
For each array item in our `navigation'prop we can expect an object with the following key -
items - array
title - string
For each array item in our `items'prop we can expect an object with the following keys -
We will want to create the Header component in a new directory under components, organisms then Header . This directory will house the component itself, the associated stories, emulated JSON payload data, and styled-components logic.
Before we get started on the markup required to generate our Header component elements, let's first import some styles to make our front-end implementation align with the Figma designs I've provided at the start of the course.
import styled from 'styled-components';
const headerColour = props => {
// Fallback value if we can't get access to props
if (!props || !props.theme || !props.theme.black) return '#131313';
// If no variant is specified, return the white colour
if (!props.variant) return props.theme.black;
// Dynamically determine the background colour based on props
let colour;
switch (props.variant) {
case 'fixedLight':
colour = props.theme.white;
break;
case 'fixedDark':
colour = props.theme.black;
break;
default:
colour = props.theme.black;
break;
}
return colour;
};
const headerPosition = props => {
// Fallback value if we can't get access to props
if (!props || !props.variant) return 'relative';
// Dynamically determine the background colour based on props
Our Header component is made up of three sub-components, a navigation menu on the left, a logo image in the center and a navigation menu on the right.
Our Header component accepts one prop named `navigation'which is an array of objects that we can map over to return several instance of our Navigation molecule component within our Header component.
First we will start by destructuring the accepted props passed to our Header component and extract the named value of
navigation
.
We start our component by referencing our `StyledHeader'as a parent wrapper of the component HTML elements and React child components.
If
navigation'is defined and iterable, the Header component will loop over each node it finds, in this case, we have objects with a single key
items`. We destructure this value immediately in our array map function and pass them into a return statement.
Next, we return our `Navigation'molecule with the items (navigation items) value passed as a prop.
import React from 'react';
import StyledHeader from './header.styles';
import Button from '../../atoms/button/button';
import Navigation from '../../molecules/navigation/navigation';
So far we've been working with SVG images only, and in an ideal world we would have the logo available to us as an SVG. However, I've been provided the logo as a PNG file and whilst it's possible to convert this simple shape into a vector, I thought it'd be good to show you how we can use raster images in our React application.
First we'll import the asset as we would any other media file in our Header component.
You'll notice we've included the image as the last element in our Header, this is because I believe the navigation should take priority for screen readers and accessible tools, with flexbox and CSS properties styling the layout order only for browsers.
import React from 'react';
import StyledHeader from './header.styles';
import Logo from '../../../assets/images/logo.png';
import Button from '../../atoms/button/button';
import Navigation from '../../molecules/navigation/navigation';
In our React application, there are going to be several instances where we reference a global state store to manage our shopping cart quantities, shipping information and other bits of information we want accessible globally to our application.
However, there are some instances where we want to isolate state to a single component.
The Header component is one of those cases. I want to include a button which will allow us to toggle a
className'for our
StyledHeader'component, allowing us to show and hide the navigation menus on a mobile device.
Think of a hamburger menu but with a label.
To achieve this, we'll be using React hooks.
Hooks are a fairly new concept in React and allow us to write functional components which can still use the benefits class components once provided us without the mess.
To get started, we'll first extend our React imports to include the `useState'hook.
import React, { useState } from 'react';
import { arrayOf, shape, string } from 'prop-types';
With our new hook available, we'll need to rewrite our Header component to use a scope opener, instead of returning our `StyledHeader'element immediately, this is so we can set up the hook to be used within our component.
We then use
[currentValue, setValue]'as an array destructure to name the two values coming out of our
useState'function.
Finally, we pass an initial value to our
useState'hook itself. This is the value that the
currentValue'will equal when the React component is first mounted to the DOM.
With our new state variables available to our Header component, we can now build our Button to toggle the
className'applied to our
StyledHeader`.
Before we set about creating the HTML button itself, let's create the functionality the button needs. I've created a function named `toggleNavigation'that accepts an event as an argument. The function prevents the default functionality of the button event, stopping any unexpected behaviour in our function.
We then use the
setOpen'function we destructured from our
useState'hook to invert the value of
isOpen
. So when the button is first clicked, it will take the value of
false'and invert it to
true`. Once clicked again it will do the opposite (true to false).
Finally, we'll create a button the precedes the navigation menus, allowing those on mobile devices using screen assisted tools to access the button before any other Header element.
We then assign the function we've created to the
onClick'event of the button. The button element is smart enough to know to pass the event of
onClick'to the function so we don't need to set up any references there.
You may have noticed that in our designs, we have SVG icons in place of two navigation items on the right-hand side of the Header component.
To implement these, we'll need to revisit our Navigation molecule first and add support for a new prop value of
icon
.
In the updated Navigation component I've -
Imported the icons I want to make available to our component
Assigned the icon assets to an `Icons'object with key names
Created a new sub-component named `NavigationIcon'which accepts all navigation item keys as props and spreads them into the component as props
Created the `NavigationIcon'sub-component which returns a React Fragment holding the icon and a visibly hidden text label to describe the navigation item to screen readers.
import React from 'react';
import { arrayOf, shape, string } from 'prop-types';
import StyledNavigation from './navigation.styles';
import IconBag from '../../../assets/images/icons/shopping-bag.svg';
import IconCart from '../../../assets/images/icons/shopping-cart.svg';
import IconPlus from '../../../assets/images/icons/plus.svg';
import IconUser from '../../../assets/images/icons/user.svg';
import IconX from '../../../assets/images/icons/x.svg';
To keep consistency with our other components and to ensure the Header component accepts a valid array of values, we will define the propTypes our component should expect.
We can then import the JSON payload in our Header Storybook story and configure the `array'knob to give viewers of the component an input field to manipulate the prop data fed to the Header.
import React from 'react';
import { withKnobs, array } from '@storybook/addon-knobs';
Now we have a type defined Header component which makes use of our smaller component concepts (Navigation molecule) and dynamically renders navigation lists based on the prop data available to the component.
Not only this but we've touched on our first React hook, allowing us to make use of the state functionality React is praised for. Giving us the ability to toggle the Header components class which will create a hamburger navigation toggle on mobile devices.