storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.73k stars 9.33k forks source link

Storybook and angular library: Cannot read property 'selector' of undefined #14828

Open krbaio3 opened 3 years ago

krbaio3 commented 3 years ago

Describe the bug When I generate a component in an angular library (ng11), and I am going to use it in a storybook, it shows me the error of:

Cannot read property 'selector' of undefined.

To Reproduce https://github.com/krbaio3/sb-lib-issue

System System: OS: macOS 11.3.1 CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz Binaries: Node: 15.14.0 - ~/.nvm/versions/node/v15.14.0/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 7.11.2 - ~/.nvm/versions/node/v15.14.0/bin/npm Browsers: Chrome: 90.0.4430.93 Firefox: 88.0 Safari: 14.1 npmPackages: @storybook/addon-actions: ^6.2.9 => 6.2.9 @storybook/addon-essentials: ^6.2.9 => 6.2.9 @storybook/addon-links: ^6.2.9 => 6.2.9 @storybook/angular: ^6.2.9 => 6.2.9

Additional context

Create an angular project: ng new design-system --create-application=false --prefix=ds --style=scss

Create a angular-lib: ng generate library pattern-lib --prefix=pl

Change the name of design-system/projects/pattern-lib/package.json to @css/pattern-lib

Change the path property of design-system/tsconfig.json from pattern-lib to @css/pattern-lib

Generate the component ng generate component button --project=pattern-lib

My button component is:

import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'pl-button',
  template: `<button [attr.is-pink]="pink" [ngClass]="{'make-pink': pink}">{{label ? "😎 " + label : "No Label provided
  🧐"}}</button>`,
  style: [`
button {
  background: blue;
  padding: 1rem 2rem;
  border-radius: 3px;
  appearance: none;
  border: 0;
  -webkit-appearance: none;
  -moz-appearance: none;
  font-size: 1.5rem;
  letter-spacing: 1px;
  color: white;
  box-shadow: 0 4px 10px rgba(55, 55, 55, 0.3),
    0 6px 35px rgba(55, 55, 200, 0.7);
  cursor: pointer;

  &.make-pink {
    background: #ff00a2;
    box-shadow: 0 4px 10px rgba(55, 55, 55, 0.3),
      0 6px 35px rgb(200 55 150 / 70%);
  }
}`
]
})
export class ButtonComponent {

  @Input('label') label: string | null;
  @Input('pink') pink: boolean;

}

Change tsconfig properties of design-system/projects/pattern-lib/tsconfig.lib.json. Add this property:

{
"angularCompilerOptions": {
    "enableIvy": false,
    "skipTemplateCodegen": true,
    "strictMetadataEmit": true,
    "enableResourceInlining": true
  },
}

Generate the lib dist: ng build

Init the storybook with npx -p @storybook/cli sb init --type angular

Generate my custom story: MyButton.stories.ts

import {Meta, Story} from '@storybook/angular/types-6-0'

// import { ButtonComponent } from '@css/pattern-lib';

import { ButtonComponent } from 'projects/pattern-lib/src/public-api';

export default {
  title: 'Custom/Buttons',
  component: ButtonComponent,
  argTypes: {
    label: {
      control: 'text'
    }
  }
} as Meta;

const Template: Story<ButtonComponent> = (args: ButtonComponent) => ({
  component: ButtonComponent,
  props: args
});

export const FancyBlueButton = Template.bind({});

FancyBlueButton.args = {
  label: 'Button',
};

export const FancyPinkButton = Template.bind({});

FancyBlueButton.args = {
  label: 'Pink version',
  pink: true,
}

I run: npm run storybook

If I run with this code, (import { ButtonComponent } from 'projects/pattern-lib/src/public-api';) the storybook works.

But, if I change the import (commented) import { ButtonComponent } from '@css/pattern-lib'; the storybook fails, with the error Cannot read property 'selector' of undefined

image

shilman commented 1 year ago

I don't know the current state of this issue, but I do know that we no longer support Angular 11. If somebody is experiencing this problem with Angular 14, 15, or 16 and Storybook 7.0+, please open an issue with a reproduction and we'd be happy to take a look at it. To create a reproduction, go to https://storybook.new or see repro docs. Thank you! 🙏

computergnome99 commented 1 year ago

Hey @shilman - This issue is still being experienced on Angular 14, 15, and 16 with Storybook 7+ when using npm 7+. I have created a reproduction based on @John-Zimmer's instructions here: Reproduction Repository.

If you'd prefer I create a new issue with this reproduction, I can also do that instead.

If additional information is needed, please let me know.

tlaukkanen commented 1 year ago

@computergnome99 did you create a new issue for this? Seems still valid as we are also still seeing this with Angular 15.2.0, Storybook 7.0.6, NPM 8.19.3 and Windows.

image

shilman commented 1 year ago

thanks @computergnome99 @tlaukkanen !!!

jesse-schein commented 1 year ago

I generally never comment on GitHub but thought to help get this issue raised.

Using: NodeJs: 16.20.0 Npm: 8.19.4 Angular: ^16.0.4 (takes 16.1.1) storybook: ~7.0.22

I got the error listed in the main post during running the storybook website trying to render a component.

After deleting all package lock and node modules folder, and doing npm install —legacy-peer-reps it fixed all my issues after running storybook.

computergnome99 commented 1 year ago

@jesse-schein Thanks for the confirmation!

DanielAlcaraz commented 1 year ago

@jesse-schein solution works with npm. But in our monorepo we are using pnpm and I cannot find a way to make it work. We are using the last versions of Angular and Storybook with Nx.

yayassa-tootelo commented 1 year ago

Hello, I'm facing the same issue with Nx, I tried to use storybook in the libs folder.

Angular: 16.1.0 Storybook: 7.1.0 Nodejs: 16.16.0

atropo commented 1 year ago

I can confirm the problem using storybook inside an angular-workspace with more projects, the workaround of @John-Zimmer in https://github.com/storybookjs/storybook/issues/14828#issuecomment-1523875596 works for me.

In my case I don't need to add flags to npm, but just removing the node_modules from the project directory and doing again a fresh npm install in the workspace root, fixes the problem. Maybe this is related to my version of npm being a little older.

My environment is:

Storybook 7.3.2 
Angular CLI: 15.2.9
Node: 16.14.2
Package Manager: npm 8.5.0
OS: linux x64

I did some debugging and in my case the problem is in the function computesTemplateFromComponent: the ngComponentMetadata is undefined.

 const ngComponentMetadata = getComponentDecoratorMetadata(component);  //undefined for me

Debugging a little more inside the getComponentDecoratorMetadata function:

export const getComponentDecoratorMetadata = (component: any): Component | undefined => {
  const decorators = reflectionCapabilities.annotations(component); //correctly populated

  return decorators.reverse().find((d) => d instanceof Component); //failing  on the test function
};

I see the decorators const correctly populated with a decorator object, but then the subsequent find() returns undefined.

I hope this could help identify the problem.

EliezerB123 commented 1 year ago

I can confirm this error, but I have a bit more information.

We started having this error after moving from Angular 12 to Angular 15, but, well, we ALSO decided to migrate to Storybook v7 at the same time.

So I'm in the unique position to confirm that this happens in both 6.5.16, AND 7.3.2, in the same project, after migrating to Angular 15.

Angular CLI: 15.2.8 Node: 16.20.2 Package Manager: npm 8.19.4 OS: win32 x64

Storybook: Both 6.5.16 AND 7.3.2, (But 7.3.2 only has this issue in a builded PROD, not dev. While 6.5.16 has this issue in DEV too.)

Running npm install --legacy-peer-deps didn't work for me. Any chance someone could post their package.json?

manuelkroiss commented 1 year ago

Did anyone successfully solve this issue?

I am facing the same problem as @EliezerB123.

Storybook Version: 7.4.5 Node Version: 18.17.0 npm Version: 8.3.0

Starting storybook in dev mode works perfectly fine. Running a production build and deploying the result files leads to the error.

However, I encountered the following:

When I use a story with the component attribute, the error occurs. Example:

export Default {
   component: ButtonComponent
} as Meta;

Stories where I used a template instead of the component reference work as expected. Example:

const Template: StoryFn<ButtonComponent> = (args: ButtonComponent) => ({
  props: {
    ...args,
  },
  template: '<ic-one-button></ic-one-button>'
});
EliezerB123 commented 1 year ago

@atropo

I did some debugging and in my case the problem is in the function computesTemplateFromComponent: the ngComponentMetadata is undefined.

 const ngComponentMetadata = getComponentDecoratorMetadata(component);  //undefined for me

Debugging a little more inside the getComponentDecoratorMetadata function:

export const getComponentDecoratorMetadata = (component: any): Component | undefined => {
  const decorators = reflectionCapabilities.annotations(component); //correctly populated

  return decorators.reverse().find((d) => d instanceof Component); //failing  on the test function
};

I see the decorators const correctly populated with a decorator object, but then the subsequent find() returns undefined.

I hope this could help identify the problem.

From what I can tell, this seems to be the likely source of the bug. As per https://stackoverflow.com/a/63937850/5243309 and https://stackoverflow.com/q/41587865/5243309, it seems that instanceof Component will fail when using two node_modules/@angular folders, or when a library has its own instance of node_modules/@angular because it has Angular listed in dependencies instead of peerDependencies. ( link#3 )

Which explains why npm install --legacy-peer-deps appears to solve the issue for some people.

The solution seems to be to replace all the code using instanceOf with something like this: decorator.constructor.name === factory.prototype.constructor.name.

Alex85651 commented 1 year ago

Hi, i'm facing the same problem. It occurs only when running a production build :'( This error appearred when i migrated from 7.0 to 7.4.6. Here are the versions I use now :

Storybook Version: 7.4.6 Node Version: 18.16.0 npm Version: 9.5.1 Angular 16.2

grankovsky commented 1 year ago

HI, found solution.
Change imports for stand alone at .stories.ts from: import { Component } from '@xm-ngx/components/component'; to : import { Component } from './component.component';

I hope this could help.

valentinpalkovic commented 1 year ago

@atropo

I did some debugging and in my case the problem is in the function computesTemplateFromComponent: the ngComponentMetadata is undefined.

 const ngComponentMetadata = getComponentDecoratorMetadata(component);  //undefined for me

Debugging a little more inside the getComponentDecoratorMetadata function:

export const getComponentDecoratorMetadata = (component: any): Component | undefined => {
  const decorators = reflectionCapabilities.annotations(component); //correctly populated

  return decorators.reverse().find((d) => d instanceof Component); //failing  on the test function
};

I see the decorators const correctly populated with a decorator object, but then the subsequent find() returns undefined. I hope this could help identify the problem.

From what I can tell, this seems to be the likely source of the bug. As per https://stackoverflow.com/a/63937850/5243309 and https://stackoverflow.com/q/41587865/5243309, it seems that instanceof Component will fail when using two node_modules/@angular folders, or when a library has its own instance of node_modules/@angular because it has Angular listed in dependencies instead of peerDependencies. ( link#3 )

Which explains why npm install --legacy-peer-deps appears to solve the issue for some people.

The solution seems to be to replace all the code using instanceOf with something like this: decorator.constructor.name === factory.prototype.constructor.name.

Initially, I thought the same, and I already prepared a PR to not use instanceof checks but instead rely on some type of information, which is appended during compilation to the decorator properties.

I talked to the Angular core team, and they warned me to support this case since having multiple instances of Angular in the application is a quite broken state, and many things are likely to malfunction. The only valid solution, therefore, is to guarantee that the application loads one instance of Angular.

So I thought about the reproduction, which was provided by @John-Zimmer. When creating an Angular workspace with some applications and libraries, each library might have its dependencies, which we must install. Since npm's default behavior is to install peer dependencies, a npm install in a library folder will install @angular/core as well. This is NOT what we want. The whole workspace should refer to one @angular/core instance, not multiple ones. So what happens if we use npm's workspace feature here? Let's go to the root package.json and let's add all project folders as a workspace:

{
 ...
 "workspaces": [
    "projects/*"
  ],
 ...
}

Then, a npm install is only executed at the root level, never in a library folder. Dependencies will automatically be hoisted to the root's node_module folder, and conflicting dependencies will remain in the lib's node_modules. This guarantees that we always have one instance of @angular/core installed and used. Does anyone know, whether there are some arguments against this approach?

NX, on the other side, has its Single Version Policy, where all dependencies are installed in the root folder. Hence, the mentioned issues with having multiple instances of Angular should never occur. But there seem to be issues, and I am curious whether @yayassa-tootelo or @DanielAlcaraz could provide a reproduction with nx. But also, in this case, I suggest using the workspace feature of the used package manager to resolve this issue.

RenaudC5 commented 1 year ago

Since the 7.5.0 storybook release, it is now possible to use the argsToTemplate method for property and event Bindings.

In our case, this has been used as a workaround for the error Cannot read property 'selector' of undefined.

Here is an example of how to use it :

import { argsToTemplate } from '@storybook/angular';

export const ExampleRange: Story = {
  name: 'Default',
  args: {
    stringProp: "Foo",
    objectProp : {foo: "bar"},
    arrayProp: ["Foo", "Bar"]
  },
  render: (args) => ({
    props: args,
    template: `<app-my-input ${argsToTemplate(args)}></app-my-input>`
  })
};

Using this on all of our stories seems to work, and we haven't found any problems at the moment.

Hope this helps.


Versions : Storybook Version: 7.5.0 Node Version: 18.16.0 npm Version: 9.5.1 Angular 16.2.8

leogouveia commented 1 year ago

@atropo

I did some debugging and in my case the problem is in the function computesTemplateFromComponent: the ngComponentMetadata is undefined.

 const ngComponentMetadata = getComponentDecoratorMetadata(component);  //undefined for me

Debugging a little more inside the getComponentDecoratorMetadata function:

export const getComponentDecoratorMetadata = (component: any): Component | undefined => {
  const decorators = reflectionCapabilities.annotations(component); //correctly populated

  return decorators.reverse().find((d) => d instanceof Component); //failing  on the test function
};

I see the decorators const correctly populated with a decorator object, but then the subsequent find() returns undefined. I hope this could help identify the problem.

From what I can tell, this seems to be the likely source of the bug. As per https://stackoverflow.com/a/63937850/5243309 and https://stackoverflow.com/q/41587865/5243309, it seems that instanceof Component will fail when using two node_modules/@angular folders, or when a library has its own instance of node_modules/@angular because it has Angular listed in dependencies instead of peerDependencies. ( link#3 ) Which explains why npm install --legacy-peer-deps appears to solve the issue for some people. The solution seems to be to replace all the code using instanceOf with something like this: decorator.constructor.name === factory.prototype.constructor.name.

Initially, I thought the same, and I already prepared a PR to not use instanceof checks but instead rely on some type of information, which is appended during compilation to the decorator properties.

I talked to the Angular core team, and they warned me to support this case since having multiple instances of Angular in the application is a quite broken state, and many things are likely to malfunction. The only valid solution, therefore, is to guarantee that the application loads one instance of Angular.

So I thought about the reproduction, which was provided by @John-Zimmer. When creating an Angular workspace with some applications and libraries, each library might have its dependencies, which we must install. Since npm's default behavior is to install peer dependencies, a npm install in a library folder will install @angular/core as well. This is NOT what we want. The whole workspace should refer to one @angular/core instance, not multiple ones. So what happens if we use npm's workspace feature here? Let's go to the root package.json and let's add all project folders as a workspace:

{
 ...
 "workspaces": [
    "projects/*"
  ],
 ...
}

Then, an npm install is only executed at the root level, never in a library folder. Dependencies will automatically be hoisted to the root's node_module folder, and conflicting dependencies will remain in the lib's node_modules. This guarantees that we always have one instance of @angular/core installed and used. Does anyone know, whether there are some arguments against this approach?

NX, on the other side, has its Single Version Policy, where all dependencies are installed in the root folder. Hence, the mentioned issues with having multiple instances of Angular should never occur. But there seem to be issues, and I am curious whether @yayassa-tootelo or @DanielAlcaraz could provide a reproduction with nx. But also, in this case, I suggest using the workspace feature of the used package manager to resolve this issue.

Is there a repo or a stackblitz/codesandbox with working code?

AmbrosiaDevelopments commented 11 months ago

The only thing that fixed it for me was switching enableProdMode to false in angular.json. Not amazing but quick work around until this gets figured out for sure. I'm not using a monorepo either.

shaman-apprentice commented 10 months ago

Maybe it helps anyone: I had the same issue. I have a Angular mono repo for libraries and storybook installed at the root. I accidentally run npm i once within a library folder, so that I had a node_modules folder at the root and within the library. Deleting node_modules within the library and only run npm i at root fixed it for me.

antoine-carruana commented 10 months ago

Is a solution in the works ? The only solution working for us actually is to set a template instead of using the component directly, but it's not a solution for long term.

Versions : Storybook Version: 7.6.7 Node Version: 18.18.2 npm Version: 9.8.1 Angular 17.0.8

ghost commented 9 months ago

Screenshot 2024-02-08 104949 This is still happening. Tried a few options Node v18.18.0 Storybook: 7.6.4

  1. Created a Angular library v16.2.0. Added a simple button.
  2. Created a sample app that uses the library from no.1. Added storybook here.
  3. Imported the button via dependencies.
  4. Encountering this issue when deployed

  1. Created a Angular library v16.2.0. Added a simple button.
  2. Added storybook here
  3. Imported the button via file dependencies.
  4. Still encountering this issue when deployed

Another clue: It works in my local for both approach. Error shows up when I deploy this. Update: I've fix it now.

valentinpalkovic commented 9 months ago

@ranceroxas Have you read my comment here: https://github.com/storybookjs/storybook/issues/14828#issuecomment-1770336203. Please make sure that only one instance of Angular is used in the monorepo. Otherwise, disabling prod mode like mentioned in other comments might help as well.

valentinpalkovic commented 6 months ago

@vasa-ae, Can you tell me how your repository structure looks? If you use a mono repo, does this comment help? The main issue is that you likely have multiple Angular instances in your repository in different node_module folders. Ensure that just one instance of @angular dependencies is installed, and then the issue should go away.

valentinpalkovic commented 6 months ago

Thank you for providing all this information.

First, I want to clarify that loading stories from "node_modules" isn't officially supported. We often explicitly exclude running builder plugins/loaders for node_module files because of performance reasons.

Second, could you run npm why @angular/core or yarn why @angular/core, depending of your package manager.

Could you also initialize Storybook in your library @aea/angular-shared-library instead? Does the error disappear if you would do so (also for testing purposes)?

Jintus commented 6 months ago

Hi everyone, I'm facing the same issue using Angular@16, pnpm workspace and StoryBook@8.

I have made a reproductible repo here.

To try it out:

  1. pnpm install on the root workspace
  2. pnpm start

Hope it will help for debugging.

@valentinpalkovic I have make sure that, in the Angular UI lib, angular is a peerDependency and with a pnpm why @angular/core there is only 1 version of angular installed 16.2.12.

I have no error in dev it's only with the builded storybook. (You can try pnpm storybook)

Jintus commented 5 months ago

Hi everyone, I'm facing the same issue using Angular@16, pnpm workspace and StoryBook@8.

I have made a reproductible repo here.

To try it out:

  1. pnpm install on the root workspace
  2. pnpm start

Hope it will help for debugging.

@valentinpalkovic I have make sure that, in the Angular UI lib, angular is a peerDependency and with a pnpm why @angular/core there is only 1 version of angular installed 16.2.12.

I have no error in dev it's only with the builded storybook. (You can try pnpm storybook)

@valentinpalkovic, are this exemple good for debugging what is the error ?

elvinachang commented 1 month ago

Having the same issue here, any workarounds on this? Using storybook 8.2.9 with angular 16. It def look like its the build of storybook that is giving this error for components used within stories.