Open16

Next13 x tailwind x storybook

wwwywwwy

tailwind対応

pnpm i @storybook/addon-styling -D

.storybook/main.ts
addons: [
     // ...,
+  '@storybook/addon-styling',
]

実行

# Run the postinstall script from the root of your project
$ node node_modules/@storybook/addon-styling/bin/postinstall.js

=========================================

 🧰 Configuring @storybook/addon-styling

=========================================

(1/3) Project summary
  • Built with webpack
  • Styled with tailwind

(2/3) .storybook/main.ts
  • Registering @storybook/addon-styling.

(3/3) .storybook/preview.ts
  • Adding import for withThemeByClassName decorator
  • Adding import for stylesheet
  • Adding withThemeByClassName decorator to config

✨ Done

https://storybook.js.org/recipes/tailwindcss

wwwywwwy

code generator

手動で作るのは面倒なので避けたい。hygenしか知らなかったが、Plopが良いと聞いて使って見る。

https://plopjs.com/documentation/#getting-started

pnpm i -D plop
wwwywwwy
plopfile.mjs
export default function (plop) {
  plop.setGenerator('component', {
    description: 'create a new component',
    prompts: [
      {
        type: 'input',
        name: 'path',
        message: 'Where to create components? (e.g. components/atoms)',
      },
      {
        type: 'input',
        name: 'name',
        message: 'What is the component name?',
      },
    ],
    actions: [
      {
        type: 'add',
        path: '{{path}}/{{pascalCase name}}/index.tsx',
        templateFile: 'plop-templates/component/index.tsx.hbs',
        skipIfExists: true,
      },
      {
        type: 'add',
        path: '{{path}}/{{pascalCase name}}/{{pascalCase name}}.test.tsx',
        templateFile: 'plop-templates/component/component.test.tsx.hbs',
      },
      {
        type: 'add',
        path: '{{path}}/{{pascalCase name}}/{{pascalCase name}}.stories.tsx',
        templateFile: 'plop-templates/component/component.stories.tsx.hbs',
      },
    ],
  })
}

wwwywwwy

hbsのsyntax highlight良いのないのか...

plop-templates/component/index.tsx.hbs
import { FC } from 'react'

type Props = {}

export const {{pascalCase name}}: FC<Props> = () => {
  return (
    <div>
      {{pascalCase name}}
    </div>
  )
}
plop-templates/component/component.stories.tsx.hbs
import type { Meta, StoryObj } from "@storybook/react"
import { {{pascalCase name}} } from '.'

type T = typeof {{pascalCase name}}

export default {
  component: {{pascalCase name}},
} satisfies Meta<T>

export const Default: StoryObj<T> = {}
plop-templates/component/component.test.tsx.hbs
import { render, screen } from "@testing-library/react"
import { {{pascalCase name}} } from "."

test('renders {{pascalCase name}} component', () => {
  render(<{{pascalCase name}} />)

  const titleElement = screen.getByRole( "heading", { level: 1, name: /{{pascalCase name}} Component/i })

  expect(titleElement).toBeInTheDocument()
})
wwwywwwy
package.json
"scripts": {
...
"codegen": "plop component"
}
wwwywwwy

storybookのコンパイルエラー

- event compiled client and server successfully in 288 ms (20 modules)
- wait compiling...
- event compiled client and server successfully in 79 ms (20 modules)
WARN You (or an addon) are using the 'config' preset field. This has been replaced by 'previewAnnotations' and will be removed in 8.0
info Addon-docs: using MDX2
info => Using implicit CSS loaders
info => Using default Webpack5 setup
<i> [webpack-dev-middleware] wait until bundle finished
99% end closing watch compilationWARN Force closed preview build
ModuleNotFoundError: Module not found: Error: Can't resolve '../src/index.css'
wwwywwwy

.storybook/preview.tsでtailwindの読み込みができていなかった

.preview.ts
/* TODO: update import to your tailwind styles file. If you're using Angular, inject this through your angular.json config instead */
import '../index.css'
wwwywwwy

使っていないstories/を削除

.storybook/main.ts
-    '../stories/**/*.mdx',
-     '../stories/**/*.stories.@(js|jsx|ts|tsx)',
wwwywwwy

テストライブラリを追加

pnpm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

https://nextjs.org/docs/pages/building-your-application/optimizing/testing#jest-and-react-testing-library

wwwywwwy
jest.config.mjs
import nextJest from 'next/jest.js'
 
const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})
 
// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const config = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
 
  testEnvironment: 'jest-environment-jsdom',
}
 
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
export default createJestConfig(config)
wwwywwwy

型がないと言われたので
pnpm i --save-dev @types/jest

package.json
"scripts": {
  "test": "jest"
}
wwwywwwy

ついでに

launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Jest Tests",
      "type": "node",
      "request": "launch",
      "runtimeArgs": ["--inspect-brk", "${workspaceRoot}/node_modules/jest/bin/jest.js", "--runInBand"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}