Skip to content

Commit

Permalink
Added Storybook integration (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
scottrippey authored Sep 29, 2023
1 parent c4b20dc commit c0e37f5
Show file tree
Hide file tree
Showing 19 changed files with 10,809 additions and 1,734 deletions.
75 changes: 66 additions & 9 deletions .github/workflows/code-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ on:
branches:
- main
pull_request:
branches:
- main

env:
NEXT_PUBLIC_SANITY_PROJECT_ID: 5bsv02jj

jobs:
build:
name: Check codebase (lint and typecheck)
checks:
name: "Check codebase (lint and typecheck)"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -22,23 +23,79 @@ jobs:
with:
version: 7

- name: Get pnpm store directory
- name: "Get pnpm store directory"
id: pnpm-cache
run: echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"

- name: Setup pnpm cache
- name: "Setup pnpm cache"
uses: actions/cache@v3
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('./pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
- name: "Install dependencies"
run: pnpm install

- name: Lint
- name: "Lint"
run: pnpm run lint

- name: TypeCheck
- name: "TypeCheck"
run: pnpm run typecheck

tests:
name: "Unit Tests"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v2
with:
node-version: 18.x

- uses: pnpm/[email protected]
with:
version: 7

- name: "Get pnpm store directory"
id: pnpm-cache
run: echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"

- name: "Setup pnpm cache"
uses: actions/cache@v3
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('./pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: "Install dependencies"
run: pnpm install

- name: "Configure .env"
working-directory: packages/nextjs
run: cp .env.sample .env

- name: "Storybook: Install Extra Dependencies"
working-directory: packages/nextjs
run: pnpm exec playwright install

- name: "Storybook: Build"
working-directory: packages/nextjs
run: pnpm run storybook:build

- name: "Storybook: Chromatic"
uses: chromaui/action@v1
# Chromatic GitHub Action options
with:
workingDir: packages/nextjs
storybookBuildDir: storybook-static
exitOnceUploaded: true
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

- name: "Storybook: Run Tests"
working-directory: packages/nextjs
run: pnpm run test:storybook:start

40 changes: 0 additions & 40 deletions .github/workflows/deploySanity.yml

This file was deleted.

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ yarn-error.log*
dist
.idea
.vscode

storybook-static/
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "next-sanity-fastly-ecomm-demo",
"name": "nextjs-sanity-fe",
"version": "0.0.1",
"description": "A demo ecommerce site using Next.js, Sanity CMS, and Fastly.",
"keywords": [],
"author": "Formidable",
"main": "index.js",
"license": "MIT",
"scripts": {
"local": "pnpm run --filter ./packages/nextjs --filter ./packages/sanity --parallel dev",
"plop": "plop",
"dev:nextjs": "pnpm run --filter ./packages/nextjs dev",
"next:dev": "pnpm run --filter ./packages/nextjs next:dev",
"next:dev:mock": "pnpm run --filter ./packages/nextjs next:dev:mock",
Expand All @@ -31,8 +31,11 @@
"eslint": "^8.31.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-prettier": "^4.2.1",
"glob": "^10.3.4",
"husky": "^8.0.3",
"inquirer-autocomplete-prompt": "^3.0.0",
"lint-staged": "^13.1.0",
"plop": "^4.0.0",
"prettier": "^2.8.2",
"start-server-and-test": "^1.15.2",
"typescript": "4.9.4"
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ FASTLY_SERVICE_ID=<redacted>
SANITY_WEBHOOK_SECRET=<redacted>

# Enable this to use our mock Sanity server
NEXT_PUBLIC_API_MOCKING=enabled
#NEXT_PUBLIC_API_MOCKING=enabled
6 changes: 5 additions & 1 deletion packages/nextjs/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"extends": ["../../.eslintrc.json", "next/core-web-vitals"],
"extends": [
"../../.eslintrc.json",
"next/core-web-vitals",
"plugin:storybook/recommended"
],
"rules": {
"import/order": [
"error",
Expand Down
25 changes: 25 additions & 0 deletions packages/nextjs/.storybook/decorators/TestHarness.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FC, PropsWithChildren } from "react";
import { Decorator } from "@storybook/react";
import { MotionConfig, MotionConfigProps } from "framer-motion";
import { TypedParameters } from "../types";

declare module "../types" {
export interface TypedParameters extends TestHarnessProps {}
}

export type TestHarnessProps = {
motionConfig?: MotionConfigProps;
};

export const TestHarness: FC<PropsWithChildren<TestHarnessProps>> = ({ motionConfig, children }) => {
return <MotionConfig {...motionConfig}>{children}</MotionConfig>;
};

export const TestHarnessDecorator: Decorator = (Story, ctx) => {
const { motionConfig } = ctx.parameters as TypedParameters;
return (
<TestHarness motionConfig={motionConfig}>
<Story />
</TestHarness>
);
};
19 changes: 19 additions & 0 deletions packages/nextjs/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { StorybookConfig } from "@storybook/nextjs";

const config: StorybookConfig = {
stories: ["../**/*.mdx", "../**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/nextjs",
options: {},
},
docs: {
autodocs: "tag",
},
};
export default config;
22 changes: 22 additions & 0 deletions packages/nextjs/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Preview } from "@storybook/react";

import "../styles/global.css";
import { TestHarnessDecorator } from "./decorators/TestHarness";

const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
decorators: [
// List all global decorators:
TestHarnessDecorator,
],
};

export default preview;
29 changes: 29 additions & 0 deletions packages/nextjs/.storybook/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StoryObj as _StoryObj, Meta as _Meta } from "@storybook/react";

/*
Storybook does not give us strongly-typed parameters,
so we're going to export our own overrides.
*/

export interface TypedParameters {
/*
This interface intentionally left blank,
so that it can be extended by the decorators that use it.
*/
}

type Override<TOriginal, TOverrides> = TOverrides & Omit<TOriginal, keyof TOverrides>;

export type StoryObj<TComponent> = Override<
_StoryObj<TComponent>,
{
parameters?: TypedParameters;
}
>;

export type Meta<TComponent> = Override<
_Meta<TComponent>,
{
parameters?: TypedParameters;
}
>;
46 changes: 46 additions & 0 deletions packages/nextjs/__plop_templates/COMPONENT.stories.tsx.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { Meta, StoryObj } from ".storybook/types";

import { screen, userEvent, waitFor, within } from "@storybook/testing-library";
import { expect, jest } from "@storybook/jest";

import { {{COMPONENT.BASENAME}} } from "./{{COMPONENT.BASENAME}}";

const meta: Meta<typeof {{COMPONENT.BASENAME}}> = {
component: {{COMPONENT.BASENAME}},
args: {},
};

export default meta;

type Story = StoryObj<typeof {{COMPONENT.BASENAME}}>;

export const Default: Story = {
async play({ canvasElement, step }) {
const ui = wrap(canvasElement);
await step("TODO: add unit tests", async () => {
expect(ui.TODO).toBeVisible();
});
},
};

export const Variant: Story = {
args: {
// TODO: Customize the variant args here
},
async play({ canvasElement, step }) {
const ui = wrap(canvasElement);
await step("TODO: add unit tests", async () => {
expect(ui.TODO).toBeVisible();
});
},
};

/** Encapsulate all UI elements here for easier testing */
function wrap(canvasElement: HTMLElement) {
const container = within(canvasElement);
return {
get TODO() {
return container.getByRole("TODO");
},
};
}
49 changes: 49 additions & 0 deletions packages/nextjs/components/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Meta, StoryObj } from ".storybook/types";
import { Button } from "./Button";

const meta: Meta<typeof Button> = {
component: Button,
args: {
children: <>Button</>,
},
};
export default meta;

type Story = StoryObj<typeof Button>;

export const Variants: Story = {
render() {
return (
<div className="flex flex-col items-start p-2 gap-2 bg-gray">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="text">Text</Button>
</div>
);
},
};

export const Primary: Story = {
args: {
variant: "primary",
},
};

export const Secondary: Story = {
args: {
variant: "secondary",
},
};

export const Tertiary: Story = {
args: {
variant: "tertiary",
},
};

export const Text: Story = {
args: {
variant: "text",
},
};
Loading

1 comment on commit c0e37f5

@vercel
Copy link

@vercel vercel bot commented on c0e37f5 Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.