Storybook Docs cannot handle union types in Vue/TS setup

See original GitHub issue

Describe the bug

Hey 👋

I tried to update SB to version 6. Our project setup is Vue/NuxtJS/Typescript + vue-property-decorator for writing class components.

Initially, no components would render and I’d get this error:

Cannot read property 'map' of undefined
TypeError: Cannot read property 'map' of undefined
    at convert (http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:5805:22)
    at convert (http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:5688:51)
    at javaScriptFactory (http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:6183:41)
    at extractProp (http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:6328:19)
    at http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:6292:12
    at Array.map (<anonymous>)
    at extractComponentSectionArray (http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:6291:24)
    at extractComponentProps (http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:6318:41)
    at http://localhost:1337/vendors~main.dcec9cf333cc0105409b.bundle.js:5554:51
    at Array.forEach (<anonymous>)

Going through single stories, I found out that apparently a union type declared within the props produces this error.

To Reproduce Steps to reproduce the behavior:

  1. Clone https://github.com/HerrBertling/nuxt-ts-storybook-docs-issue & run yarn
  2. Start Storybook via yarn storybook
  3. See error

If you switch the brokenMsg type to one of the working ones explained below, Storybook works as expected.

Expected behavior

This should™ just™ work™ 😄

Code snippets

Working prop:

<script lang="ts">
import Vue from 'vue'
import { Prop, Component } from 'vue-property-decorator'

@Component({})
export default class BaseCheckbox extends Vue {
  @Prop({ default: '' }) id!: string
  [… more props here]
}
</script>

Also working, but feels rather stupid

<script lang="ts">
import Vue from 'vue'
import { Prop, Component } from 'vue-property-decorator'

@Component({})
export default class BaseCheckbox extends Vue {
  @Prop({ default: '' }) id!: StringOrNumber
  [… more props here]
}
</script>

with a global type declared somewhere:

declare type StringOrNumber = string | number

Passing along different strings works also without issues, like for a button:

<script lang="ts">
import Vue from 'vue'
import { Prop, Component } from 'vue-property-decorator'

@Component({})
export default class BaseButton extends Vue {
  @Prop({ default: 'primary' }) variant!: 'primary' | 'secondary' | 'text'
  [… more props here]
}
</script>

System:

I guess this is not needed, but just in case:

System info
System:
    OS: macOS 10.15.6
    CPU: (4) x64 Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
  Binaries:
    Node: 12.16.3 - ~/.nvm/versions/node/v12.16.3/bin/node
    Yarn: 1.22.4 - ~/Sites/my/fancy/project/node_modules/.bin/yarn
    npm: 6.14.4 - ~/.nvm/versions/node/v12.16.3/bin/npm
  Browsers:
    Chrome: 84.0.4147.125
    Firefox: 77.0.1
    Safari: 13.1.2
  npmPackages:
    @storybook/addon-a11y: ^6.0.4 => 6.0.4
    @storybook/addon-essentials: ^6.0.4 => 6.0.4
    @storybook/addon-knobs: ^6.0.4 => 6.0.4
    @storybook/theming: ^6.0.4 => 6.0.4
    @storybook/vue: ^6.0.4 => 6.0.4

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:13
  • Comments:11 (7 by maintainers)

github_iconTop GitHub Comments

8reactions
HerrBertlingcommented, Aug 20, 2020

Okay, what’s more fun than digging deep into node_modules and console.logging the sh*t out of everything to track down the issue – right? 😄

I found the problem, I guess? This bit in the convert.ts destructures entries that should exist within the type argument:

https://github.com/storybookjs/storybook/blob/f3a9117937205f88d8da0264b59942edcfe29cb6/addons/docs/src/lib/convert/proptypes/convert.ts#L10

But for a union type in a Vue component, value does not exist and is undefined. That’s why the map cannot work here:

https://github.com/storybookjs/storybook/blob/f3a9117937205f88d8da0264b59942edcfe29cb6/addons/docs/src/lib/convert/proptypes/convert.ts#L40

But whatever should exist in type.value is present as type.elements. My nasty fix to get this working was falling back to type.elements. Yes, of course within the compiled source 😄

var convert = function convert(type) {
  var name = type.name,
      raw = type.raw,
      computed = type.computed,
      value = type.value ? type.value : type.elements;
     // … (other stuff)

The convert method is called here:

https://github.com/storybookjs/storybook/blob/f3a9117937205f88d8da0264b59942edcfe29cb6/addons/docs/src/lib/docgen/createPropDef.ts#L74-L79

So either the elements should be used or they should be converted into a value entry before passing it on? I guess converting beforehand is the sensible thing to to. Probably here:

https://github.com/storybookjs/storybook/blob/f3a9117937205f88d8da0264b59942edcfe29cb6/addons/docs/src/lib/docgen/extractDocgenProps.ts#L32-L39

Because that’s the part that explicitly targets vue-docgen-api, so it should be the best place to handle this edge case?

https://github.com/storybookjs/storybook/blob/f3a9117937205f88d8da0264b59942edcfe29cb6/addons/docs/src/lib/docgen/extractDocgenProps.ts#L64-L67

I guess @elevatebart knows how to handle this best?

EDIT: I tried this and it worksâ„¢

export const extractComponentSectionArray = (docgenSection: any) => {
  const typeSystem = getTypeSystem(docgenSection[0]);
  const createPropDef = getPropDefFactory(typeSystem);

  return docgenSection
    .map((item: any) => {
      const sanitizedItem = {...item, value: item.elements }
      return extractProp(sanitizedItem.name, sanitizedItem, typeSystem, createPropDef)
    })
    .filter(Boolean);
};
2reactions
shilmancommented, Aug 20, 2020

Jiminy cricket!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.0.15 containing PR #12158 that references this issue. Upgrade today to try it out!

Read more comments on GitHub >

github_iconTop Results From Across the Web

ArgTypes - Storybook - JS.ORG
ArgTypes are a first-class feature in Storybook for specifying the behaviour of Args. By specifying the type of an arg, you constrain the...
Read more >
TypeScript with Composition API - Vue.js
Using <script setup> #. When using <script setup> , the defineProps() macro supports inferring the props types based on its argument:.
Read more >
@types/storybook__addon-knobs | Yarn - Package Manager
Important: This documentation covers modern versions of Yarn. For 1.x docs, see classic.yarnpkg.com. Yarn.
Read more >
Props typing in Vue.js 3 with TypeScript - Stack Overflow
<script setup lang="ts"> import { ref,PropType, ... for a (custom) constructor during type checking (links to legacy docs but works the same ...
Read more >
Configuring the Apollo Client cache - Apollo GraphQL Docs
This article describes cache setup and configuration. ... an array of the __typename s of the types that belong to that union or...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found