Skip to content

nelsieborja/gorg-ui-v3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

40 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ¦‹ Gorg UI v3 CircleCI codecov Netlify Status Storybook

This repo contains the base architecture of the project, while further development was moved to a private monorepo (for now). You might also like to checkout the first ever version of the project for prior updates.

βš™οΈ Installation

  1. Create the application:

    $ npx create-react-app gorg-ui-v3 --typescript
  2. Add Storybook:

    $ cd gorg-ui-v3 && npx -p @storybook/cli sb init
  3. Quickly check that all environments are working properly:

    # Run the test runner (Jest) in a terminal:
    $ yarn test
    
    # Start the component explorer on port 9009:
    $ yarn run storybook
    
    # Run the frontend app proper on port 3000:
    $ yarn start
  4. Automated Testing via StoryShots:

    Install the package and its types:

    $ yarn add -D @storybook/addon-storyshots @types/storybook__addon-storyshots

    Create a src/storybook.test.js file and put:

    import initStoryshots from "@storybook/addon-storyshots";
    initStoryshots();
  5. Configure the App for Jest (Component Story Format (CSF) version):

    Configure Jest to work with Webpack's require.context():

    // .storybook/config.js
    
    import { configure } from "@storybook/react";
    
    const req = require.context("../src/components", true, /\.stories\.js$/); // <- import all the stories at once
    configure(req, module);

    The above works only during the build with Webpack, polyfill this to work with Jest by first installing Macro (for CRA v2+):

    $ yarn add -D require-context.macro

    In the same file, import the Macro and run it in place of require.context:

    import requireContext from "require-context.macro";
    
    // const req = require.context('../src/components', true, /\.stories\.js$/); <-- replaced
    const req = requireContext("../src/components", true, /\.stories\.js$/);

    StoryShots is dependent on react-test-renderer:

    $ yarn add -D react-test-renderer
  6. Setting Up Addons - Knobs

    Install Addon Knobs:

    $ yarn add @storybook/addon-knobs

    Register Knobs in your .storybook/addons.js file:

    import "@storybook/addon-actions/register";
    import "@storybook/addon-knobs/register";
    import "@storybook/addon-links/register";

    Usage:

    import { withKnobs, text } from "@storybook/addon-knobs/react";
    import Button from "./Button";
    
    export default {
      title: "Button",
      component: Button,
      decorators: [withKnobs]
    };
    
    export const Default = () => (
      <Button>{text("children", "Simple Button")}</Button>
    );
  7. Finishing up TypeScript Setup

    Add custom Webpack config by creating .storybook/webpack.config.js file and put:

    module.exports = ({ config, mode }) => {
      config.module.rules.push({
        test: /\.(ts|tsx)$/,
        loader: require.resolve("babel-loader"),
        options: {
          presets: [["react-app", { flow: false, typescript: true }]]
        }
      });
      config.resolve.extensions.push(".ts", ".tsx");
      return config;
    };

    Make sure to add babel-loader:

    $ yarn add -D babel-loader

    Update extensions accordingly (from .js to .ts|tsx):

    .storybook/config.js => .storybook/config.ts

    requireContext('../src/components', true, /\.stories\.js$/) => requireContext('../src/components', true, /\.stories\.tsx$/)

πŸ› Bug Fixes

TypeScript Issue(s)

Could not find a declaration file for module '@storybook/addon-knobs/react'

// BEFORE:
import { withKnobs, object } from "@storybook/addon-knobs/react";

// AFTER:
import { withKnobs, object } from "@storybook/addon-knobs";

Styled System Issue(s)

Type '{ color?: ... }' is not assignable to type 'ButtonHTMLAttributes'

// FIX 1: Set styled component type to `any`:
const Button: any = styled("button") < ButtonProps > ``;

// Fix 2: Define custom prop `textColor` in place of `color`:
const textColor = system({
  textColor: {
    property: "color", // <-- CSS property
    scale: "colors" // <-- key reference in the `theme` object
  }
});

For Fix 2, make sure to do the same thing for prop bg|backgroundColor since this is also part of the built-in API color.


πŸ“„ DocsPage Setup

  1. Install DocsPage:

    $ yarn add -D @storybook/addon-docs
  2. Create the file .storybook/presets.js and put:

    module.exports = ["@storybook/addon-docs/react/preset"];
  3. Install react-docgen-typescript-loader:

    $ yarn add -D react-docgen-typescript-loader

    May need to specify the tsconfig for the loader in case props are not displaying:

    // webpack.config.js
    
    config.module.rules.push({
      ...
      use: [
        {
          loader: require.resolve('react-docgen-typescript-loader'),
          options: {
            tsconfigPath: path.resolve(__dirname, '../tsconfig.json')
          }
        }
      ]
    });

Storybook tests UI visually via an add-on called storyshot-puppeteer:

Same as storyshots, it works by comparing screenshots – only this time it takes screenshots of the browser and not code

Quick Setup

  1. Install storyshot-puppeteer:

    $  yarn add D @storybook/addon-storyshots-puppeteer
  2. Update the test file storybook.test.ts to override the test comparison with imageSnapshot from the puppeteer add-on:

    // storybook.test.ts
    
    import initStoryshots from "@storybook/addon-storyshots";
    import { imageSnapshot } from "@storybook/addon-storyshots-puppeteer";
    
    initStoryshots({
      test: imageSnapshot({ storybookUrl: "http://localhost:9009/" })
    });

    Note that the tests now depend on having an instance of Storybook running, so make sure Storybook server is up and running before running the tests.

Running on CI

Running two terminals (one for Storybook, another one for Testing) at the same time on CI environment is made possible by start-server-and-test.

  1. Install it:

    $ yarn add start-server-and-test
  2. Sample script:

    // package.json
    
    "scripts": {
      "test": "react-scripts test --coverage",
      "storybook": "start-storybook -p 9009 -s public",
      "ci:test": "start-test storybook 9009 test",
    }

CircleCI Setup

Collecting Jest Data via Junit (CRA version)

  1. Install jest-junit:

    $ yarn add -D jest-junit
  2. Tell Junit to save the report output to a directory of your choice (test-reports in my case):

    // package.json
    
    "jest-junit": {
      "outputDirectory": "test-reports"
    }

Sample command line (CRA version):

$ CI=true yarn run test:coverage -i --reporters=default --reporters=jest-junit

More CircleCI required config:

# .circleci/config.yml

- store_artifacts:
    path: test-reports/ # upload the test result folder as an artifact
- store_test_results:
    path: test-reports/ # display test result in build summary section

  1. Install the required dependencies:

    $ yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
  2. Create the .eslintrc file and put:

    {
      "parser": "@typescript-eslint/parser",
      "plugins": ["@typescript-eslint"]
    }

    In the same file, add the following recommended rules:

    // .eslintrc
    
    {
      ...
      // Add this to avoid runtime error
      "parserOptions": {
        "project": "./tsconfig.json"
      },
      "extends": [
        // Rules which recommended for all projects by the ESLint Team
        "eslint:recommended",
        // Make all eslint rules compatible with TS
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended",
        // for type-checking to work properly with highly-valuable rules
        "plugin:@typescript-eslint/recommended-requiring-type-checking"
      ]
    }
$ npx install-peerdeps --dev eslint-config-airbnb
// .eslintrc

{
  ...
  "extends": [
    // ESLint rules, including ES6+ and React
    "airbnb",
    // Enable the linting rules for React hooks
    "airbnb/hooks",
    ...
  ],
}
$ yarn add -D eslint-config-prettier
// .eslintrc

{
  ...
  "extends": [
    ...
    // Disable above code formatting related rules
    "prettier",
    // Required if a config uses a plugin to avoid conflict with Prettier
    "prettier/@typescript-eslint",
    "prettier/react"
  ],
}

In case of Prettier not configured in a workspace, add eslint-plugin-prettier to enforce the formatter.

$ yarn add -D prettier eslint-plugin-prettier
// .eslintrc

{
  ...
  "plugins": [..., "prettier"],
  "extends": [
    "prettier/react"
    "plugin:prettier/recommended"
  ],
  "prettier/prettier": [
      "error",
      {
        "no-var-keyword": true,
        "printWidth": 100,
        "semi": true,
        "singleQuote": true,
        "trailingComma": "es5",
        "tabWidth": 2
      }
    ]
}

Git Hooks

  1. Install the required dependencies:

    $ yarn add -D husky lint-staged prettier pretty-quick
  2. Sample config files:

    // .huskyrc
    
    {
      "hooks": {
        "pre-commit": "pretty-quick --staged --verbose && lint-staged",
        "pre-push": "yarn run test:all"
      }
    }
    // .lintstagedrc
    
    {
      "src/**/*.{js,ts,tsx}": [
        "eslint --fix",
        "git add"
      ]
    }