Build a React UI components library by Typescript, React-Storybook and JEST

Recently I’ve been working on a project which aims for building UI modules and then used in other frontend typescript projects.

Steps to start your project

Generate .d.ts File

In order to provide the code-completion within the IDE like (Vscode, Webstorm), we must generated typescript declaration files for all the react components. This could be done easily by setting declaration in tsconfig.json, like below:

{
    "compilerOptions": {
        ...
        "declaration": true,        
        "outDir": "../dist"
    },
    ...
}

Add typings definitions to package.json

{
    "name": "ui-widgets-ts",
    "version": "1.0.0",
    "description": "Module by React Typescript",  
    "main": "./dist/index.js",
    "typings": "./dist/index.d.ts",
    ...
}

Button component

import * as React from "react";

export type ButtonType = "danger" | "info" | "warning" | "success" | "primary" ;

export interface IBtnProps {
    type: ButtonType;
    onClick?: () => {};
}

/**
 * Implement Bootstrap button react component by Typescript.
 * For the client project who used this, just add bootstrap CSS thus will work.
 */
export class Button extends React.Component<IBtnProps, any> {

    public render() {
        const {type, onClick, children} = this.props;
        return (
            <button onClick={onClick} type="button" className={`btn btn-${type}`}>{children}</button>
        );
    }
}

 

JEST testing

import {shallow} from "enzyme";
import * as React from "react";
import {Button} from "./index";

test("Button should work", () => {
    // Render a button with type
    const btn = shallow(
        <Button type="danger">Test</Button>,
    );

    expect(btn.text()).toEqual("Test");
});

 

Storybook usage

The purpose of storybook is to mimic our real use scenario for buttons, so we configure storybook as typescript-based and use the generated /dist folder from our source code.

webpack.config.js

// load the default config generator.
const genDefaultConfig = require('@kadira/storybook/dist/server/config/defaults/webpack.config.js');
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');

/**
 * Webpack config for Storybook
 * In reality, most project will use Webpack2, TS, React to bundle the build
 * So the configuration here is also a mimic/prove for the client projects who use ui-widgets-ts package.
 */
module.exports = function (config, env) {
    const default_config = genDefaultConfig(config, env);

    // Extend it as you need.
    const final_config = webpackMerge.smart(default_config, {
        module: {
            loaders: [
                {
                    test: /\.ts(x)?$/,
                    loaders: ['ts-loader']
                },
                { test: require.resolve("jquery"), loader: "expose-loader?$!expose-loader?jQuery" }
            ]
        },

        resolve: {
            // Resolve alias so as to make import { Button } from "ui-widgets" works
            alias: {
                "ui-widgets-ts": path.resolve(__dirname, "../../dist")
            }
        }
    });

    return final_config;
};

tsconfig.json

We need to set path and baseUrl, in order to import our module in the storybook like this:

import {Button} from "ui-widgets-ts";
{
    "compilerOptions": {
        ...
        "baseUrl": ".",
        "paths": {
            "ui-widgets-ts": [
                "../dist"
            ]
        }
    }
}

Story

import {action, storiesOf} from "@kadira/storybook";
import {text, withKnobs} from "@kadira/storybook-addon-knobs";

import {Button} from "ui-widgets-ts";
import * as React from "react";

const stories: any = storiesOf("Bootstrap", module);
stories.addDecorator(withKnobs);

stories.addWithInfo("Button", "Demo bootstrap buttons", () => (
    <div>
        <Button type="danger"
                onClick={action("clicked")}>
            {text("Label", "danger Button")}
        </Button>
        <Button type="info"
                onClick={action("clicked")}>
            {text("Label", "info Button")}
        </Button>
    </div>
));

 

Here is my Github link

https://github.com/davidctj/ui-react-module-typescript-storybook-jest

Please follow and like us:

Leave a Reply

Your email address will not be published. Required fields are marked *