Storybook: Please provide a working example for vue with typescript

Created on 23 May 2019  路  3Comments  路  Source: storybookjs/storybook

Describe the bug

Using Vue framework initialized with typescript, typescript integration with storybook doesn't work at all.

To reproduce

Steps to reproduce the behavior:

Part 1 - Create the Project

  1. Create a vue project using vue create sample and select Manually select features.
  2. Check Babel, TypeScript, Linter / Formatter, and Unit Testing.
  3. Answer the prompts with the following:

    • Use class-style component syntax? Y

    • Use Babel alongside TypeScript for auto-detected polyfills? Y

    • Pick a linter / formatter config: TSLint

    • Pick additional lint features: Check both

    • Pick a unit testing solution: Jest

    • Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files

    • Save this as a preset for future projects? N

  4. This creates a default component called HelloWorld.vue. To test if this works properly, issue the command yarn run test:unit.

Part 2 - Initializing Storybook

I followed the official guide for setting up storybook for vue.
However, I think it assumes just plain Javascript and uses npm.
That's why I will modify the steps a bit.

  1. Add all dependencies:
yarn add -D @storybook/vue vue-loader vue-template-compiler @babel/core babel-loader babel-preset-vue
  1. Add to scripts section of package.json:
{
  ...
  "scripts": {
    ...
    "storybook": "start-storybook"
  }
  ...
}
  1. Create .storybook directory in your project root, then create a file named config.js with the contents:
import { configure } from "@storybook/vue";
import Vue from "vue";
import HelloWorld from "../src/components/HelloWorld.vue";

Vue.component("hello-world", HelloWorld);

function loadStories() {
  const req = require.context("../stories", true, /\.stories\.js$/);
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);
  1. Create stories directory in your project root, then create a file named HelloWorld.stories.js with the contents:
import { storiesOf } from "@storybook/vue";
import HelloWorld from "../src/components/HelloWorld.vue";

storiesOf("HelloWorld", module).add("my first story", () => ({
  components: {
    "hello-world": HelloWorld
  },
  template: "<hello-world>rounded</hello-world>"
}));
  1. To verify everything works so far, open src/components/HelloWorld.vue, cut the entire script tag and save it. Now run yarn run storybook and see that it works.

  2. Undo the changes so that the script tag is there again. This will be needed on the next step.

Part 3 - Configure Storybook to Use TypeScript

I followed the official guide for configuring typescript integration with storybook.
However, the guide assumes that I am using React, which I'm not.
I have modified the steps below that I think fits Vue.

  1. Install dependencies:

I did not install any dependencies from react.

yarn add -D typescript awesome-typescript-loader @storybook/addon-info jest @types/jest ts-jest @ypes/storybook__vue
  1. In the .storybook folder, create a file named webpack.config.js with the contents:
module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: require.resolve("awesome-typescript-loader")
      }
    ]
  });
  config.resolve.extensions.push(".ts", ".tsx");
  return config;
};
  1. In the .storybook folder, create a file named tsconfig.json with the contents:
{
  "compilerOptions": {
    "outDir": "build/lib",
    "module": "commonjs",
    "target": "es5",
    "lib": ["es5", "es6", "es7", "es2017", "dom"],
    "sourceMap": true,
    "allowJs": false,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDirs": ["src", "stories"],
    "baseUrl": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "declaration": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "build", "scripts"]
}
  1. Finally, run storybook: yarn run storybook and the error will show:

I replaced the home directory with ~

ERROR in ./src/components/HelloWorld.vue?vue&type=script&lang=ts& (./node_modules/awesome-typescript-loader/dist/entry.js!./node_modules/vue-loader/lib??vue-loader-options!./src/components/HelloWorld.vue?vue&type=script&lang=ts&)
Module build failed: Error: Final loader (./node_modules/awesome-typescript-loader/dist/entry.js) didn't return a Buffer or String
    at runLoaders (~/Desktop/sample/node_modules/@storybook/core/node_modules/webpack/lib/NormalModule.js:319:18)
    at ~/Desktop/sample/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (~/Desktop/sample/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at iterateNormalLoaders (~/Desktop/sample/node_modules/loader-runner/lib/LoaderRunner.js:221:10)
    at ~/Desktop/sample/node_modules/loader-runner/lib/LoaderRunner.js:236:3
    at context.callback (~/Desktop/sample/node_modules/loader-runner/lib/LoaderRunner.js:111:13)
    at processTicksAndRejections (internal/process/next_tick.js:81:5)
 @ ./src/components/HelloWorld.vue?vue&type=script&lang=ts& 1:0-184 1:200-203 1:205-386 1:205-386
 @ ./src/components/HelloWorld.vue
 @ ./.storybook/config.js
 @ multi ./node_modules/@storybook/core/dist/server/common/polyfills.js ./node_modules/@storybook/core/dist/server/preview/globals.js ./.storybook/config.js ./node_modules/webpack-hot-middleware/client.js?reload=true

Expected behavior

I expected that the storybook will run properly just like what happened in Part 2 Step 8.

Screenshots

Part 2 Step 8 screenshot:

working

Code snippets

Already included in the steps above. Tell me if I need to edit and move or copy them here.

System:

  • OS: Ubuntu 18.04.2 LTS
  • Device: Laptop
  • Browser: Firefox, Chrome
  • Framework: Vue
  • Addons: not relevant but @storybook/addon-info is installed
  • Versions:

Globally Installed:

  • yarn 1.16.0
  • node 11.12.0
  • @vue/cli 3.7.0
  • typescript 3.4.5

Project Dependencies:

{
  ...
  "dependencies": {
    "core-js": "^2.6.5",
    "vue": "^2.6.10",
    "vue-class-component": "^7.0.2",
    "vue-property-decorator": "^8.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.4.5",
    "@storybook/addon-info": "^5.0.11",
    "@storybook/vue": "^5.0.11",
    "@types/jest": "^24.0.13",
    "@types/storybook__vue": "^5.0.1",
    "@vue/cli-plugin-babel": "^3.7.0",
    "@vue/cli-plugin-typescript": "^3.7.0",
    "@vue/cli-plugin-unit-jest": "^3.7.0",
    "@vue/cli-service": "^3.7.0",
    "@vue/test-utils": "1.0.0-beta.29",
    "awesome-typescript-loader": "^5.2.1",
    "babel-core": "7.0.0-bridge.0",
    "babel-loader": "^8.0.6",
    "babel-preset-vue": "^2.0.2",
    "jest": "^24.8.0",
    "lint-staged": "^8.1.5",
    "ts-jest": "^24.0.2",
    "ts-loader": "^6.0.1",
    "typescript": "^3.4.5",
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "^2.6.10"
  }
}

Additional context

It's 2019. Every issue I read dates back at least last year. I tried their solutions, even trying all loaders, nothing really works. Please tell me what I did wrong in the setup, or at least some configuration that will work on the versions today. I am willing to test out suggestions even the ones I've tested, for the purpose of documenting what works and what doesn't.

vue has workaround question / support typescript

Most helpful comment

I was actually able to get it running following your guide, but skipping the installation of babel-preset-vue.

You'll need to edit node_modules/@storybook/vue/dist/server/framework-preset-vue.js:34:42 to make it just

function babelDefault(config) {
  return config;
}

The rest I did pretty much as you described (using a webpack config similar to the one provided by the vue-cli plugin) and it works. I'll upload a test repo shortly.

/edit: here's the test repo: https://github.com/graup/storybook-vue-typescript
/edit2:The test repo now contains a patch file, so it should work out of the box

All 3 comments

The whole Vue support is kind of bad. There's also #4475 which prevents us from using up-to-date presets (like the one offered by vue-cli). I wonder if this issue also has to do with that.

I was actually able to get it running following your guide, but skipping the installation of babel-preset-vue.

You'll need to edit node_modules/@storybook/vue/dist/server/framework-preset-vue.js:34:42 to make it just

function babelDefault(config) {
  return config;
}

The rest I did pretty much as you described (using a webpack config similar to the one provided by the vue-cli plugin) and it works. I'll upload a test repo shortly.

/edit: here's the test repo: https://github.com/graup/storybook-vue-typescript
/edit2:The test repo now contains a patch file, so it should work out of the box

Thank you so much @graup ! It worked!

First I cloned the repo and did a diff. It didn't really need much modification like I did before. I followed what you did and it worked.

Maybe I can say that this is a workaround because it requires modifying a file in node_modules which can cause problems in CI environments. However, this can be solved by creating a script that modifies the file in the node_modules as soon as yarn install is done with the execution.

In addition (unrelated), if you are using the experimental decorator feature of typescript where you can use class-style component definition, you should add these to your tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "allowJs": true,
    ...
  },
  ...
}

I'll be closing this issue now since the problem has been solved. However, if you have a permanent solution, an update, or any other way to solve it, please feel free to join the thread or reopen it.

Was this page helpful?
0 / 5 - 0 ratings