@d4l/web-components-library
TypeScript icon, indicating that this package has built-in type declarations

3.23.0 • Public • Published

D4L Storybook and Component Library

🎨 Stencil based component library for data4life design system

Gitmoji Netlify Status

Technologies

Install

npm i

Usage

# Main dev command — concurrently running 'npm run stencil' and  'npm run storybook'
npm run all

# Run stencil
npm run stencil

# Build stencil which is required before you run storybook
npm run build-stencil

# Run the storybook on http://localhost:6006/
npm run storybook

# Export the storybook as a static website, including Stencil build
npm run build-storybook

# Test locally an exported static version of the storybook
npx http-server out

# Run component structural tests
npm test

# Run component structural tests in watch mode
npm run test-watch

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:

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 <button class={buttonClasses}>{text}</button>;
  }
}

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:

// 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`
  <d4l-button
    classes="${classes ?? ''}"
    .text="${label}"
    ?is-route-link=${isRouteLink}
    ?disabled="${disabled}"
    ?is-loading="${isLoading}"
    .handleClick=${handleClick}
    style=${borderRadius ? `--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:

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: `<d4l-button text="Yeah!"></d4l-button>`,
    });

    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:

# go into the Component Library directory
cd ~/hc-ui-storybook
# creates global link
npm link

# go 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:

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

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.

Integration with a framework

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:

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();
});

Integration without a framework

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>
  <head>
    <link
      rel="stylesheet"
      href="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui/d4l-ui.css"
    />
    <script
      defer
      type="module"
      src="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui/d4l-ui.esm.js"
    ></script>
    <script
      defer
      nomodule=""
      src="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui.js"
    ></script>
  </head>
  <body>
    <d4l-button text="Hello world 👋" />
  </body>
</html>

You can find further information like how to pass objects properties from a non-JSX element at Stencil JavaScript page.

Styles and Types

By adding the import '@d4l/web-components-library/dist/d4l-ui/d4l-ui.css' statement (framework integration) or using the <link rel="stylesheet" href="//unpkg.com/@d4l/web-components-library@latest/dist/d4l-ui/d4l-ui.css" /> (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.

Readme

Keywords

Package Sidebar

Install

npm i @d4l/web-components-library

Weekly Downloads

2,018

Version

3.23.0

License

BSD-3-Clause

Unpacked Size

2.65 MB

Total Files

513

Last publish

Collaborators

  • d4l-team