Web Components for Data4Life design system
npm install @d4l/web-components-library> 🎨 Stencil based component library for data4life design system
- Storybook
- Stencil
- TypeScript
- WebComponents
``sh`
npm i
`shMain dev command — concurrently running 'npm run stencil' and 'npm run storybook'
npm run all
Build your components
Components are stored under the
/src/components directory. The technology of choice is Stencil.
You can follow the documentation for the Component API when you're building your
components. An example of a Button components can look like the following:`tsx
import { Component, Prop, Listen, h } from '@stencil/core';// @Component decorator for defining the element tag, connect and scope its style
@Component({
tag: 'd4l-button', // For consistency and to avoid name conflicts, let's use d4l prefix
styleUrl: 'button.css',
shadow: true,
})
export class Button {
// @Prop decorator for declaring custom attribute/properties exposed publicly on the element
@Prop() text: string = '';
@Prop() secondary: boolean = false;
@Prop() tertiary: boolean = false;
// @Listen decorator for attaching an event listener and react on it
@Listen('click', { capture: true })
handleClick(ev) {
// Placeholder
console.log('button clicked', ev.target);
}
// JSX template for our component
render() {
const { text, secondary, tertiary } = this;
const buttonClasses = {
button: true,
secondary,
tertiary,
};
return ;
}
}
`Write your stories
Stories represent a single state of one component. Technically, they are functions that return information which can be rendered on the screen.
You can find the stories under the
/stories directory, though their location can be configured to be inside component directory (please check the .storybook/main.js for more information).Assuming that we have a component called
Button, an example story can look like the following:`js
// We're using lit-html for rendering the web components.
import { html } from 'lit-html';// Configure the main aspect of our story, indluding the argument types for the controls.
export default {
title: 'Components/Button',
argTypes: {
label: { control: 'text' },
disabled: { control: 'boolean' },
borderRadius: {
control: {
type: 'inline-radio',
options: [4, 8, 20],
},
},
},
};
// We can define a template for all Button's stories.
// The special notation for binding properties and attributes comes from lit-html.
// Please check https://lit-html.polymer-project.org/guide/writing-templates for more details.
const Template = ({
classes,
label,
disabled,
borderRadius,
isRouteLink,
isLoading,
handleClick,
}) => html
--border-radius-button: ${borderRadius}px : ''};// Template.bind() is required to assure a clean state for each story.
export const Primary = Template.bind({});
// Provide properties values for each story argument and expose them in the Controls tab.
Primary.args = {
label: 'Default',
disabled: false,
};
export const CustomRadius = Template.bind({});
// We can reuse argument values from previous story via object destructuring.
CustomRadius.args = {
...Primary.args,
borderRadius: 4,
};
`Storybook 🤝 Zeplin
To synchronize web components with design, Stories needs to be linked up with their Zeplin counterparts.
This video explains the plugin, and its advantages: https://www.youtube.com/watch?v=YGQSvf2yNQ8
Instructions to do this can be found in pull request.
Write structural tests
Our structural tests are performed using Stencil's utility functions on top of Jest.
Example test for an existing component called
Button can look like the following:`js
import { newSpecPage } from '@stencil/core/testing';
import { Button } from './button';describe('button', () => {
it('should build', async () => {
expect(new Button()).toBeTruthy();
});
it('should render with provided text', async () => {
const page = await newSpecPage({
components: [Button],
html:
,
}); const buttonElement = page.root.shadowRoot.querySelector('.button');
expect(buttonElement.textContent).toContain('Yeah!');
});
});
`Test changes before publishing (npm link)
You can test locally your code changes before publishing the library to the npm registry
by using the
npm link feature.For example, if you'd like to test the Component Library locally in the Delphi project
use the next commands:
`sh
go into the Component Library directory
cd ~/hc-ui-storybook
creates global link
npm linkgo into the Delphi project directory
cd ~/delphi
link-install the package
npm link @d4l/web-components-library
`Note that running the
npm install command in the project of choice (in this case Delphi)
will remove the linked version.Addons
Currently, you can find the following addons installed:
- Accessibility
- Actions
- Viewport
- Design token
- Zeplin
You can adjust their configuration from
.storybook/addons and .storybook/config.js files.Global styles and static files
Static files e.g. fonts, favicon etc. are stored under the
public directory.
Global styles including CSS variables are defined in public/base.css and then referenced in the preview-head.html which is loaded for each component iframe.Stencil output directoty is
www and it contains all resources which are needed to import and use the generated web components.
You can find that configration at stencil.config.ts.Additional resources
- Getting started with Stencil
- A collection of design systems
Deployment
With every merge to base branch, a new deployment for the storybook is made with the help of Netlify to https://storybook.d4l.io.
Release
To complete a production release of the Component Library (publish on NPM) please follow the next steps:
1. Make sure that the
package.json version has been updated (it's a TODO item in the PR template).
2. Create and publish a new GitHub Release by listing all included commits since the last release:
- specify a tag according to the semantic versioning methodology.
- leave the target pointing to the base branch.
3. Once the release has been published, a GitHub Action will automatically publish the latest version of the Component Library to our NPM registry.Integration
You can integrate the D4L web components into applications built using any of the most common JavaScript frameworks or web projects without a framework.
$3
The Stencil team provides and updates regularly their integration documentation which also includes code examples on how exactly to configure the web components for the following frameworks:
For instance, you can bind the custom elements to the
window object in React by using the applyPolyfills() and the defineCustomElements() functions:`js
import React from 'react';
import ReactDOM from 'react-dom';
import {
applyPolyfills,
defineCustomElements,
} from '@d4l/web-components-library/dist/loader';
import '@d4l/web-components-library/dist/d4l-ui/d4l-ui.css';applyPolyfills().then(() => {
defineCustomElements();
});
`$3
To use the D4L web components on a website or application without a framework you can include them via a script tag e.g.:
`html
rel="stylesheet"
href="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui/d4l-ui.css"
/>
defer
type="module"
src="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui/d4l-ui.esm.js"
>
defer
nomodule=""
src="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui.js"
>
`You can find further information like how to pass objects properties from a non-JSX element at Stencil JavaScript page.
$3
By adding the
import '@d4l/web-components-library/dist/d4l-ui/d4l-ui.css' statement (framework integration) or using the (non-framework integration), you can also include the styles for the exported components to your project.For TypeScript projects, you can add the
import { Components } from '@d4l/web-components-library/dist/types' statement and then use the exported Interfaces for each component e.g. Components.D4lAccordion`.