I'm building out an Angular app with Storybook. I want my stories to have controllable knobs, but some of these components take ng-content.
I'm having trouble getting these two to work together, because, from what I've found, passing content into a component using Storybook involves setting a template on the story. Unfortunately, template seems to essentially overwrite Storybook's knob-passed props.
Here's the example:
button.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'ui-button',
templateUrl: './button.component.html',
styleUrls: ['./button.component.scss']
})
export class ButtonComponent implements OnInit {
types: String[] = [];
constructor() {}
ngOnInit(): void {}
typeClasses(): String[] {
return this.types.map(t => `button--${t}`);
}
}
button.component.html
<a class="button" [ngClass]="typeClasses()">
<ng-content></ng-content>
</a>
button.component.stories.ts
import { text, array } from '@storybook/addon-knobs';
import { ButtonComponent } from './button.component';
export default {
title: 'ButtonComponent'
};
export const dark = () => ({
moduleMetadata: {
declarations: [ButtonComponent], // Not needed when not using template
imports: []
},
// component: ButtonComponent, replaced with the below because of ng-content
template: `<ui-button>Button content</ui-button>`, // Needed to pass arbitrary child content
props: {
types: array('class', ['dark']), // Ignored, because it's not in template
}
});
Am I missing a better way to pass content in? Because I have to give a full template, it seems that any props not passed in that template aren't injected into the component, and so the knobs are rendered useless. This seems to mean that I should just get rid of props on all my component stories, and instead just pass them in through the template, but that would render them non-configurable in the served Storybook and defeat much of the point.
Am I doing this wrong? Is there a way to both A) pass content, and B) allow for props? The Angular Storybook guide doesn't seem to address this.
Any news on whether or not this is possible with Angular Storybook at the moment? Also created a Stack Overflow post and asked on the Storybook Discord, and haven't gotten a sense of whether this is possible.
@sashafklein I was just introduced to Storybook and also ran into this roadblock pretty quickly.
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
Hi gang, We’ve just released addon-controls in 6.0-beta!
Controls are portable, auto-generated knobs that are intended to replace addon-knobs long term.
Please upgrade and try them out today. Thanks for your help and support getting this stable for release!
For anybody who is interested in Controls but don't know where to start, I've created a quick & dirty step-by-step walkthrough to go from a fresh CRA project to a working demo. Check it out:
=> Storybook Controls w/ CRA & TypeScript
There are also some "knobs to controls" migration docs in the Controls README:
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
Automention: Hey @Armanio @MaximSagan @kroeder @leoyli, you've been tagged! Can you give a hand here?
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
Any news?
LE: This seems to be working, discovered by pure luck. You can to be able to use inputs declared under props object.
export const Default = () => ({
moduleMetadata: {
declarations: [AppComponent],
},
props: {
propInput: {
foo: 1,
bar: {
baz: ["zxc"]
}
}
},
template: `<app-component [componentInput]="propInput"> Hello World </app-component>`,
});
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
I also came across this problem just now.
I tried using the method @ruspaull mentioned but it doesn't work well. Some things like the action-events are not displayed and even though default-values are set, they are not changing when i use the knobs/inputs on the page.
I would really appreciate if we would have just another parameter for passing in some text into the content of the element.
There seems to be multiple points being brought up in this issue, but I think I get what it is mainly asking. I will try and break it down into what I think the different points are, but correct me if I am wrong.
1: I don't think this is about knobs really. More about the props that are either applied to the component inputs/outputs or the template context.
component property of the Story, the inputs/outputs in the props property of the Story will get set when rendering the component for the story.template property of the Story, the props will be available in the template context, but you have to set where the go in the template yourself. I do think this is inconvenient, but below I will try to explain why I don't know what the right way to improve this is.2: As for providing ng-content to the component, when not using template for the story, I don't think there is a way to do that currently. From what I can remember, Angular doesn't provide a way to dynamically set ng-content outside of a template, without some limitations, but I am not positive about that.
I was going to describe some solutions, but decided to just start an issue focused on improving the way args are passed to the component. That way it doesn't get mixed with bug solutions. #12438
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
Fundamentally, what's trying to be accomplished here with supporting ng-content is to do the same thing that React storybook supports with the React's native prop children
Controls vs Knobs is irrelevant if the template isn't being re-parsed when a storybook user changes the input values in their browser
It looks like ViewContainerRef.createComponent has a projectableNodes parameter that can be used to support ng-content. https://angular.io/api/core/ViewContainerRef#createcomponent
I don't know when I would be able to attempt this, since I am busy with work currently and have some other things I am already looking into in this project when I get time. I will put some info on what I think will work below, if anyone wants to try it, but I don't know if it will work or is how it should be done.
Since, I have never used the projectableNodes parameter, I don't know much about it. It's type is any[][] and there doesn't seem to really be any documentation about it, so I assume it will not be as simple as just passing a component to that parameter. From a few examples I saw when doing a quick search, it seems to take html nodes.
Storybook creates the Story component here with createComponent, so the content components could probably be created before that and their nativeElement passed to the projectableNodes parameter of their parent.
One thing I could see being a little difficult would be making sure change detection is triggered on the children correctly. There may be some library or examples for this that I didn't find with my fairly quick search though.
I have 2 ideas for this issues
What do you think about adding a dedicated property for this case?
something like componentTemplate ?
export const dark = () => ({
component: ButtonComponent,
componentTemplate: `Button content`
props: {
types: array('class', ['dark']),
}
});
componentTemplate is only taken into account if template is empty.
<ui-button ...input ...output></ui-button> and declare component in Angular modulecomponentTemplate will allow to add internal content to the componentcomponentTemplate would be ignored. [Edit] no longer seems very relevant with https://github.com/storybookjs/storybook/pull/13383
use the Decorators? It is not yet taken into account by angular but I think it is possible (I am thinking of opening a PR for this subject)
if it could work the way I think it would. It could work something like this :
export const dark = () => ({
template: `Button content`,
});
dark.decorators = [() => ({ component: ButtonComponent })];
(only theory 🙈 ) (several decorators can be nested whether, it is a template or a component )
would solve other issues than this one
The 1st one seems to me quite simple and quick to set up. The 2nd one is longer and more complex.
I think both could be considered and that it could be complementary. 🤔
@storybookjs/angular what do you think about it?