-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Angular] Can't seem to generate a story with ng-content and knob-passed props #10272
Comments
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! |
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.
|
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. |
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.
2: As for providing 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 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 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 Storybook creates the Story component here with 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 1️⃣What do you think about adding a dedicated property for this case? export const dark = () => ({
component: ButtonComponent,
componentTemplate: `Button content`
props: {
types: array('class', ['dark']),
}
});
[Edit] no longer seems very relevant with #13383 2️⃣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 ) 👨💻The 1st one seems to me quite simple and quick to set up. The 2nd one is longer and more complex. @storybookjs/angular what do you think about it? |
I am building a button component with I am planning to create an icon component that can be used inside the button component. For this it would be ideal to pass the icon component with ng-content like this:
Would this use case be supported by the decorator solution? |
What is in development changes a little from what I said above but not by a lot. With what is currently in development you can define in see -> if you have any other ideas or feedback on this pr, please do not hesitate =) |
@ThibaudAV So then in a story I would be able to do this for nested components and have automatic controls for the main component? That would be awesome. import IconComponent from '...';
...
export const WithButton = () => ({
moduleMetadata: {
declarations: [IconComponent],
},
template: '<app-icon type="arrow-right"/> Click me',
}); |
normally yes |
@mhombach I now have this working for changing the values with the controls on the page. Still looking into getting the actions getting logged though. const Template: Story<ButtonComponent> = (args: ButtonComponent) => ({
component: ButtonComponent,
moduleMetadata: {
declarations: [ButtonComponent],
},
props: args,
argTypes: { onClick: { action: 'clicked' } },
template: `<app-button [type]="type" [size]="size" [icon]="icon">Click me</app-button>`,
});
export const Playground = Template.bind({});
Playground.args = {
'type': 'primary',
}; |
Some examples are missing in doc https://storybook.js.org/docs/angular/essentials/actions I don't know if there is an issue but we can complete them. const Template: Story<ButtonComponent> = (args: ButtonComponent) => ({
component: ButtonComponent, // <- also declare component in angular if necessary // <- this is deprecated should only be specified in `export default`
// moduleMetadata: {
// declarations: [ButtonComponent],
// },
props: args,
argTypes: { onClick: { action: 'clicked' } },
template: `<app-button [type]="type" [size]="size" [icon]="icon" (onClick)="onClick($event)">Click me</app-button>`,
// if you use template you have to manually add the output binding
// but you might not add template and let storybook guess it
});
export const Playground = Template.bind({});
Playground.args = {
'type': 'primary',
}; Complete Exempleimport { action } from '@storybook/addon-actions';
import { Button } from '@storybook/angular/demo';
export default {
component: Button,
title: 'Addon/Actions',
};
export const ActionOnly = () => ({
props: {
text: 'Action only',
onClick: action('log 1'),
},
});
ActionOnly.storyName = 'Action only';
export const ActionAndMethod = () => ({
props: {
text: 'Action and Method',
onClick: (e) => {
console.log(e);
e.preventDefault();
action('log2')(e.target);
},
},
});
ActionAndMethod.storyName = 'Action and method';
export const ActionWithTemplate = () => ({
template:
'<storybook-button-component [text]="text" (onClick)="onClick($event)"></storybook-button-component>',
props: {
text: 'Action and Method',
onClick: action('log 1'),
},
});
ActionWithTemplate.storyName = 'Action with template';
export const ActionWithControl = (args) => ({
props: args,
});
ActionWithControl.args = { text: 'Click' };
ActionWithControl.argTypes = { onClick: { action: 'clicked' } };
ActionWithControl.storyName = 'Action with args'; |
@ThibaudAV i have the missing angular snippets almost ready. The only reason why they are not up on a pull request is based on a small item that i need to take up with @shilman that is applied to both angular and Vue. Once i get the ok will push them up. Sounds good? Stay safe |
@ThibaudAV Thank you so much, I was looking for this. |
Ta-da!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.2.0-alpha.13 containing PR #13507 that references this issue. Upgrade today to the
Closing this issue. Please re-open if you think there's still more to do. |
@shilman I tried out the latest alpha with your PR but I am not exactly sure how to create a component that contains multiple components:
|
cc @ThibaudAV |
@MickL There are several ways to do this. You can look at the examples of #13507 but in your case you will have to use a template to add the content :
of the component I hope this will help you :) |
I have no idea if this will be helpful to someone today, but I managed to get a component using
If you're an Angular noob like I currently am, you'll need to ensure your component has |
The
I don't understand why this worked out for you actually... Now hold your horses; For me this solution, with expressions, is still not working, unfortunately. Wasn't it better to just expose/create a property like |
Any update on this? |
Still this issue is not fixed. This is the smallest workaround I created: import { componentWrapperDecorator, Meta, Story } from '@storybook/angular';
import { ButtonComponent } from './button.component';
export default {
title: 'Components/Button',
component: ButtonComponent,
decorators: [
componentWrapperDecorator((story) => {
return story.replace('><', '>Button<');
}),
],
} as Meta;
const Template: Story<ButtonComponent> = (args: ButtonComponent) => ({
props: args,
});
export const Primary = Template.bind({});
Primary.args = {
variant: 'primary',
}; In this example the content is provided to the component and all the controls are working properly. Not sure about import { componentWrapperDecorator } from '@storybook/angular';
export const componentContentDecorator = (content: string) => {
return componentWrapperDecorator((story) => {
return story.replace('><', `>${content}<`);
});
}; and then use it in decorators like that: ...
decorators: [
componentContentDecorator('Button'),
],
... |
I can not confirm that this is broken. It worked just fine for me. For any future readers: Example Component <div>
<h1>
Heading:
<ng-content select="[heading]"></ng-content>
</h1>
<h5>Body: <ng-content select="[body]"></ng-content></h5>
</div> Example story: import { Meta, StoryFn, moduleMetadata } from '@storybook/angular';
import { ExampleComponent } from "./example.component";
export default {
title: 'DesignSystem/Molecules/ExampleComponent',
component: ExampleComponent,
decorators: [
moduleMetadata({
imports: [
],
}),
],
args: {
},
} as Meta<ExampleComponent>;
const Template: StoryFn<ExampleComponent> = (args: ExampleComponent) => ({
props: {
...args,
},
template: `
<app-example>
<ng-container heading> Test Heading </ng-container>
<ng-container body> Test Body </ng-container>
</app-example>
`
});
export const Default = Template.bind({});
Default.args = {} So I'm not sure what the issues are that others are seeing. |
Is there any straightforward way to work with content projection in storybook for angular components ButtonComponent looks like below
how to write story for this using storybook 7.x? const meta: Meta<ButtonComponent> = {
title: 'DS/Button',
component: ButtonComponent,
tags: ['autodocs'],
render: (args: ButtonComponent) => ({
props: {
...args,
},
}),
argTypes: {
},
}; |
I am very curious if there was an easy way to set the content of a component, since this might be a common task.
|
@shilman This issue isn't fixed.
That works, but if you want to customise args they are not added to the template as inputs. |
I've written a workaround that works for my use case and I think should probably work for more complicated cases, just a little function that takes args and returns an array with the attributes const argsToAttrs = (args: ButtonComponent) => {
let attrs = '';
for (const key in args) {
if (Object.prototype.hasOwnProperty.call(args, key)) {
attrs += `${key}="${args[key]}" `;
}
}
return attrs.trim();
}
const Template: Story<ButtonComponent> = (args: ButtonComponent) => {
const attrs: string = argsToAttrs(args);
return {
template: `<test-button ${attrs}>Button</test-button>`,
props: args,
}
}; |
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
button.component.html
button.component.stories.ts
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.
The text was updated successfully, but these errors were encountered: