Skip to content

Commit

Permalink
Experience 8346: Add configuration for local instance of Storybook
Browse files Browse the repository at this point in the history
This changeset adds the initial configuration needed to spin up Storybook locally.  It's incredibly simple right now, but it should include most addons that we need.  Also included is a very simple CSF example for the StaticAlert component as a proof-of-concept.

There's a known error with React 18 [here](storybookjs/storybook#17831 (comment)), but it shouldn't affect us for the time being.  We could also install the bleeding-edge version of Storybook, but it looks like there are other issues with that as well -- so I recommend just sticking with the latest stable version until the next version is officially out.
  • Loading branch information
Stephen Kao committed Mar 22, 2023
1 parent e178ef5 commit 3e6a744
Show file tree
Hide file tree
Showing 6 changed files with 6,533 additions and 344 deletions.
171 changes: 71 additions & 100 deletions frontend-react/.eslintrc.js
@@ -1,102 +1,73 @@
module.exports = {
extends: [
"react-app",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier",
],
plugins: ["testing-library", "unused-imports", "jest-dom", "prettier"],
env: {
browser: true,
node: false,
es6: true,
},
globals: {
RequestInit: true,
process: "readonly",
cy: "readonly",
},
extends: ["react-app", "plugin:import/recommended", "plugin:import/typescript", "prettier", "plugin:storybook/recommended"],
plugins: ["testing-library", "unused-imports", "jest-dom", "prettier"],
env: {
browser: true,
node: false,
es6: true
},
globals: {
RequestInit: true,
process: "readonly",
cy: "readonly"
},
rules: {
"import/no-unresolved": "off",
"import/first": "warn",
"import/order": ["warn", {
"newlines-between": "always",
pathGroups: [{
pattern: "~/**",
group: "external",
position: "after"
}]
}],
"import/newline-after-import": "warn",
"import/no-commonjs": "off",
"import/no-named-as-default": "off",
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": ["warn", {
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_"
}],
camelcase: ["warn", {
allow: ["days_to_show", "organization_id", "sending_at"]
}]
},
overrides: [{
files: ["**/*.stories.*"],
rules: {
"import/no-unresolved": "off",
"import/first": "warn",
"import/order": [
"warn",
{
"newlines-between": "always",
pathGroups: [
{
pattern: "~/**",
group: "external",
position: "after",
},
],
},
],
"import/newline-after-import": "warn",
"import/no-commonjs": "off",
"import/no-named-as-default": "off",
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_",
},
],
camelcase: [
"warn",
{
allow: ["days_to_show", "organization_id", "sending_at"],
},
],
},
overrides: [
{
files: ["**/*.stories.*"],
rules: {
"import/no-anonymous-default-export": "off",
},
},
{
files: [
"./src/**/__tests__/**/*.[jt]s?(x)",
"./src/**/?(*.)+(spec|test).[jt]s?(x)",
],
extends: [
"plugin:testing-library/react",
"plugin:jest-dom/recommended",
],
rules: {
"testing-library/no-render-in-setup": [
"error",
{
allowTestingFrameworkSetupHook: "beforeEach",
},
],
"testing-library/no-node-access": "off",
"testing-library/prefer-screen-queries": "warn",
"testing-library/no-unnecessary-act": "warn",
"testing-library/no-await-sync-query": "warn",
},
},
{
plugins: ["chai-friendly"],
files: ["**/cypress/**/*.[jt]s?(x)"],
extends: ["plugin:cypress/recommended"],
rules: {
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": "off",
"chai-friendly/no-unused-expressions": "error",
},
},
],
settings: {
"import/resolver": {},
},
ignorePatterns: ["node_modules/", "build/", "src/reportWebVitals.js"],
};
"import/no-anonymous-default-export": "off"
}
}, {
files: ["./src/**/__tests__/**/*.[jt]s?(x)", "./src/**/?(*.)+(spec|test).[jt]s?(x)"],
extends: ["plugin:testing-library/react", "plugin:jest-dom/recommended"],
rules: {
"testing-library/no-render-in-setup": ["error", {
allowTestingFrameworkSetupHook: "beforeEach"
}],
"testing-library/no-node-access": "off",
"testing-library/prefer-screen-queries": "warn",
"testing-library/no-unnecessary-act": "warn",
"testing-library/no-await-sync-query": "warn"
}
}, {
plugins: ["chai-friendly"],
files: ["**/cypress/**/*.[jt]s?(x)"],
extends: ["plugin:cypress/recommended"],
rules: {
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": "off",
"chai-friendly/no-unused-expressions": "error"
}
}],
settings: {
"import/resolver": {}
},
ignorePatterns: ["node_modules/", "build/", "src/reportWebVitals.js"]
};
24 changes: 24 additions & 0 deletions frontend-react/.storybook/main.js
@@ -0,0 +1,24 @@
module.exports = {
stories: [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"@storybook/addon-a11y",
],
framework: "@storybook/react",
core: {
builder: "@storybook/builder-webpack5",
},
refs: {
trussworks: {
title: "Trussworks Storybook",
url: "https://trussworks.github.io/react-uswds/",
expanded: false, // optional, true by default
},
},
staticDirs: ["../src/content", "../src/content/product"],
};
48 changes: 48 additions & 0 deletions frontend-react/.storybook/preview.js
@@ -0,0 +1,48 @@
import { initializeWorker, mswDecorator } from "msw-storybook-addon";
import { CacheProvider } from "rest-hooks";
import { BrowserRouter } from "react-router-dom";
import { HelmetProvider } from "react-helmet-async";

import "../src/content/generated/global.out.css";

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
options: {
storySort: {
method: "alphabetical",
order: [],
},
},
};

function withRestHooksCacheProvider(Story) {
return (
<CacheProvider>
<Story />
</CacheProvider>
);
}

function withRouter(Story) {
return (
<BrowserRouter>
<Story />
</BrowserRouter>
);
}

function withHelmet(Story) {
return (
<HelmetProvider>
<Story />
</HelmetProvider>
);
}

export const decorators = [withHelmet, withRouter, withRestHooksCacheProvider];
22 changes: 21 additions & 1 deletion frontend-react/package.json
Expand Up @@ -75,7 +75,9 @@
"lint:eslint": "cross-env NODE_ENV=production eslint \"src/**/*.{js,ts,jsx,tsx}\"",
"lint:eslint:write": "cross-env NODE_ENV=production eslint --fix \"src/**/*.{js,ts,jsx,tsx}\"",
"yarn:show-outdated-packages": "yarn outdated",
"run-build-dir": "yarn build:localdev:csp && yarn global add serve && serve -s build"
"run-build-dir": "yarn build:localdev:csp && yarn global add serve && serve -s build",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"browserslist": {
"production": [
Expand All @@ -98,6 +100,20 @@
"@jest/globals": "^29.5.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@rest-hooks/test": "^7.3.1",
"@storybook/addon-a11y": "^6.5.16",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
"@storybook/addon-interactions": "^6.5.16",
"@storybook/addon-links": "^6.5.16",
"@storybook/addons": "^6.5.16",
"@storybook/api": "^6.5.16",
"@storybook/builder-webpack5": "^6.5.16",
"@storybook/components": "^6.5.16",
"@storybook/core-events": "^6.5.16",
"@storybook/manager-webpack5": "^6.5.16",
"@storybook/react": "^6.5.16",
"@storybook/testing-library": "^0.0.13",
"@storybook/theming": "^6.5.16",
"@svgr/webpack": "^6.5.1",
"@testing-library/cypress": "^9.0.0",
"@testing-library/dom": "^9.0.0",
Expand Down Expand Up @@ -143,6 +159,7 @@
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-jest-dom": "^4.0.3",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-storybook": "^0.6.11",
"eslint-plugin-testing-library": "^5.10.2",
"eslint-plugin-unused-imports": "2.0.0",
"eslint-webpack-plugin": "^4.0.0",
Expand All @@ -156,7 +173,9 @@
"jest-watch-typeahead": "^2.2.2",
"loader-utils": "3.2.1",
"mini-css-extract-plugin": "^2.7.5",
"mockdate": "^3.0.5",
"msw": "^1.1.0",
"msw-storybook-addon": "^1.8.0",
"npm-run-all": "^4.1.5",
"otplib": "^12.0.1",
"postcss": "^8.4.21",
Expand All @@ -177,6 +196,7 @@
"sass-loader": "^13.2.1",
"semver": "^7.3.8",
"source-map-loader": "^4.0.1",
"storybook-addon-react-router-v6": "^0.3.1",
"style-loader": "^3.3.1",
"tailwindcss": "^3.2.7",
"terser-webpack-plugin": "^5.3.6",
Expand Down
30 changes: 30 additions & 0 deletions frontend-react/src/components/StaticAlert.stories.tsx
@@ -0,0 +1,30 @@
// MyComponent.story.ts|tsx

import React from "react";

import { ComponentMeta, ComponentStory } from "@storybook/react";

import { StaticAlert, StaticAlertType } from "./StaticAlert";

export default {
title: "components/StaticAlert",
component: StaticAlert,
argTypes: {
type: {
control: {
type: "select",
options: Object.values(StaticAlertType),
},
},
},
} as ComponentMeta<typeof StaticAlert>;

export const Default: ComponentStory<typeof StaticAlert> = (args) => (
<StaticAlert {...args} />
);
Default.args = {
type: StaticAlertType.Success,
heading: "StaticAlert Heading",
message: "This is the message of the StaticAlert",
children: "Children are optional! (Try clearing me out)",
};

0 comments on commit 3e6a744

Please sign in to comment.