Storybook is an open source tool for developing UI components with various technologies, including AEM. It allows frontend developers to develop components in isolation and it captures functional test scenarios for each component. Let’s find out how to setup a new project created with the AEM Project Archetype for use with @storybook/aem. These steps will also work for configuring an existing project. We will assume the project name is “culebra-peak” for the purpose of illustration.
Create a project using the AEM Project Archetype
To begin we simply want to use the AEM Project Archetype to create a new project. We will want to include the frontend module. Modify the other configurations as needed for your project or skip this step if you already have a project that you want to use.
mvn -B archetype:generate \ -D archetypeGroupId=com.adobe.granite.archetypes \ -D archetypeArtifactId=aem-project-archetype \ -D archetypeVersion=23 \ -D aemVersion=6.5 \ -D appTitle="Culebra Peak" \ -D appId="culebra-peak" \ -D groupId="com.culebrapeak" \ -D frontendModule=general \ -D includeExamples=n
Install the project
Now that we have the project created, let’s install it to AEM. This blog post assumes that you have AEM running on localhost:4502. I had to update the uber-jar version from the pom.xml file from 6.5
to 6.5.0
in order to get Maven to install.
cd culebra-peak mvn clean install -PautoInstallPackage
Now open /editor.html/content/culebra-peak/us/en.html and make sure that the page loads as expected. You will see some initial content without much in the way of styling.
Configure Storybook AEM
Now that we have installed the project to AEM we can begin to configure @storybook/aem. To start we need to add a script to your package.json and add some configurations to the @storybook/aem-cli section. These configurations will be used to create stories for Storybook and content in AEM for those stories. Here is what it should look like:
{ ... "scripts": { ... "storybook": "start-storybook -p 4501" }, "@storybook/aem-cli": { "storybookLocation": ".storybook", "storybookStoryLocation": "./stories", "localPackagePath": ".storybook/aem-library/", "appsPath": "../ui.apps/src/main/content/jcr_root/apps", "componentPaths": [ "../ui.apps/src/main/content/jcr_root/apps/culebra-peak/components" ], "storybookAEMStyleSystem": true, "storybookAEMGrid": true, "storybookAEMPageTemplate": true, "storybookAEMConfluence": false, "storybookAEMFoundation": false, "aemContentDefaultPageResourceType": "culebra-peak/components/page", "aemContentDefaultPageTemplate": "/conf/culebra-peak/settings/wcm/templates/page-content", "aemContentDefaultPageContentPath": "/root/container/container", "aemStoryHeadingComponentResourceType": "culebra-peak/components/title", "aemStoryHeadingComponentTitleProperty": "jcr:title", "storyRoot": "Components", "packageGroup": "culebrapeak", "packageName": "Culebra Peak Storybook Library.zip", "aemContentPath": "/content/culebra-peak-design-system", "createAEMContent": true } }
We will also need to configure some files under the ui.frontend/.stories directory. We will need a few files here in order for Storybook to work. Create a ui.frontend/.storybook folder and a ui.frontend/stories folder. Next we need to tell Storybook where to look for our stories with a ui.frontend/.storybook/main.js file.
module.exports = { stories: [ `../stories/**/*.stories.*`, ], addons: [ ] };
Now we will need to configure our stories with a ui.frontend/.storybook/preview.js file.
import { addParameters, addDecorator } from '@storybook/client-api'; import { aemMetadata } from '@storybook/aem'; addDecorator(aemMetadata({ components: [ ], models: { } }));
Finally, we will need to tell Storybook how to connect to AEM with a ui.frontend/.storybook/middleware.js file.
const { createProxyMiddleware } = require('http-proxy-middleware') module.exports = function expressMiddleware (router) { router.use('/content', createProxyMiddleware({ target: 'http://admin:admin@localhost:4502', changeOrigin: true })); router.use('/etc.clientlibs', createProxyMiddleware({ target: 'http://admin:admin@localhost:4502', changeOrigin: true })); router.use('/apps', createProxyMiddleware({ target: 'http://admin:admin@localhost:4502', changeOrigin: true })); router.use('/conf', createProxyMiddleware({ target: 'http://admin:admin@localhost:4502', changeOrigin: true })); }
Install the needed dependencies
Now that the package.json is configured let’s add the NPM dependencies that we will need available. These are needed for our stories to properly connect to AEM.
# Make sure you are use NodeJS version 12+ cd ui.frontend npm install --global @storybook/aem-cli npm install --save-dev @storybook/aem npm install --save-dev storybook-aem-wrappers npm install --save-dev storybook-aem-style-system npm install --save-dev storybook-aem-grid npm install --save-dev http-proxy-middleware npm install --save-dev @babel/preset-typescript npm run storybook
This should open localhost:4501 with the message: “Sorry, but you either have no stories or none are selected somehow.” This is okay since we have not created any stories yet. However, we have added all the proper configurations so now we can use the @storybook/aem-cli to create stories for all of our components.
Create empty stories for each component
Run the sba story
command. This will ask us some questions about what we want to do. The prompts and answers are shown below. If these steps are followed correctly it will create an empty story for each component in our project.
sba story # Do you want to create a story or create stories for all components? > All stories # Create content in AEM for the stories? > yes
This will create a page for every component at /content/culebra-peak-design-system and will create a stories file for each component under ui.frontend/stories. Now run npm run storybook
. You should now see a bunch of empty stories. Depending on the component they will either look totally blank or have some basic visual elements such as an empty button. This is because by default the sba story
command creates a single empty story for each component.
Create stories for the button component
Let’s create some extra stories for the button component. The goal would be to create enough stories to fully test all the functional requirements of the component. For our purposes here we will just create a ‘long text’ variation and a ‘short text’ variation to see what the button looks like with various amounts of text within it. To do this we will again run the sba story
command but will answer the prompts differently. The prompts and answers are shown below.
$ sba story # Do you want to create a story or create stories for all components? > Single story # Generate a Storybook Story for which component? > button # Would you like to add some initial stories? We will add the default empty story for you > yes # Add a comma separated list of stories: > Long Text, Short Text # Create content in AEM for the stories? > yes
Now you should see two more empty stories for the button component in storybook. Let’s create some content for them. The story generator built content in AEM for our button stories here: /content/culebra-peak-design-system/button.html. Open this page and you should see some headings with the name of the stories and some button components. These button components is what is getting pulled into Storybook.
For the long text button edit the text field to contain “Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua”.
For the short text button edit the text field to contain “Short text”.
Now go back to Storybook and refresh the page. You should see the buttons updated to reflect the content changes that we made.
Make style changes to the button component
Let’s go ahead and update the style of these buttons. First we need to make sure our component styles make it into our AEM client library. It looks like the AEM Project Archetype has a bug at this point. To fix this go to the ui.frontend/src/main/webpack/site/main.scss file and update the component import line as shown below. Notice the ./
was replaced with ../
.
@import '../components/*.scss';
Now go to ui.frontend/src/main/webpack/components/_button.scss and add some button styles as shown below.
.cmp-button { padding: 1rem; background-color: white; transition: all 300ms; } .cmp-button:hover { background-color: #0e83cd; color: white; }
After this rerun the mvn clean install -PautoInstallPackage
command from the project root directory. After this is done you should see the button style updates at /content/culebra-peak-design-system/button.html?wcmmode=disabled. However they will not be available in Storybook because the client libraries have not been added yet.
To add our project’s client libraries to Storybook open up /content/culebra-peak/us/en.html?wcmmode=disabled and open DevTools to go to the Network tab. Filter for CSS and JS to check for all the dependencies that you need. For this project it is the following:
- /etc.clientlibs/culebra-peak/clientlibs/clientlib-base.css
- /etc.clientlibs/culebra-peak/clientlibs/clientlib-dependencies.css
- /etc.clientlibs/culebra-peak/clientlibs/clientlib-site.css
- /etc.clientlibs/culebra-peak/clientlibs/clientlib-base.js
- /etc.clientlibs/culebra-peak/clientlibs/clientlib-dependencies.js
- /etc.clientlibs/culebra-peak/clientlibs/clientlib-site.js
Now add these to Storybook by creating a file at ui.frontend/.storybook/preview-head.html and adding the following:
<link rel="stylesheet" type="text/css" href="/etc.clientlibs/culebra-peak/clientlibs/clientlib-base.css"> <link rel="stylesheet" type="text/css" href="/etc.clientlibs/culebra-peak/clientlibs/clientlib-dependencies.css"> <link rel="stylesheet" type="text/css" href="/etc.clientlibs/culebra-peak/clientlibs/clientlib-site.css"> <script src="/etc.clientlibs/culebra-peak/clientlibs/clientlib-base.js"></script> <script src="/etc.clientlibs/culebra-peak/clientlibs/clientlib-dependencies.js"></script> <script src="/etc.clientlibs/culebra-peak/clientlibs/clientlib-site.js"></script>
Now refresh Storybook in your browser and you should see your new button styles reflected in Storybook. You now have successfully integrated Storybook with AEM! Here is the code for a completed version of this blog post.
Where to go from here
Now that you have @storybook/aem integrated into your AEM project you can create stories for each component. These stories should functionally test all of the features of those components. Frontend developers can develop components and work on enhancements in an isolated context that is more friendly to their development workflow. Storybook also had many powerful plugins for accessibility, color branding, AEM style system integration, and many more. These can help empower the development process. The powerful world of Storybook plugins is now in the hands of the frontend AEM developers.
However, we can do more to improve how we are making use of Storybook.
Version controlled content
What we have done so far requires content in AEM for our stories to work. However, we do not want to be required to constantly share out of sync AEM content packages between developers. To make this a more reliable part of your development workflow we can use the @storybook/aem-cli to add content to the codebase, commit it to version control, and auto install changes when developers the latest code. This means that the content that drives the library of functional tests becomes a part of the version controlled codebase. Stay tuned for a future blog post on how to do this.
Sharing with the team
Wouldn’t it be nice if other team members could see Storybook? Testers could use Storybook as a library of functional tests for doing regression testing. Product owners could refer to it as a place to demo functionality. User experience engineers could go to it as a reference for existing functionality. It could be more holistically integrated into the teams workflow as a design system should be. What’s more, wouldn’t it be nice if Storybook was automatically deployed to AEM environments; from a development environment, to a test environment, to a staging environment, and even production? Component versions could be compared as they are released to higher environments. Well, we can do all of that by hosting Storybook in AEM. Stay tuned for a future blog post on how to do this.
How to add javascript code for the custom component?
I suppose the JS clientlibs added in head preview should include the JS for component
This is superb, much needed feature for AEM components. Followed the steps, worked like a charm.
Great Article!
Could you please mention the version (AEM, Node) used?
cool article!