-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
[Bug]: storybook 7 ignores Angular default values #22222
Comments
Duplicate of #21300 |
Thanks for the reproduction @nshimiye. My TLDR here is I don't know how this is happening. The object that's passed into Angular's storybook/code/frameworks/angular/src/client/render.ts Lines 21 to 26 in 03b85f4
I just don't know enough about Angular or SB's angular renderer to explain how it doesn't end up falling through to the default props. Is it because your template looks like this?:
If |
I just want to mention that the same template and inputs with undefined value in SB 6 was working properly and default values were taken from component even if they were not set in story props |
@miszol1 yes, because in SB6 we were doing our "own" default value setting (ie. args would start with the values that we inferred as the defaults). This was bad for a few reasons:
|
So if I understood well, in SB7 with angular I won't be able to use default values in stories? |
Hmm, well I'm not 100% sure about it but I think it's just that you need to be careful not to use templates that assume the args get set to the default values. |
This solution does not make any sense as story needs controls to be changed by user either components need default values. In my case i pass formGroups through inputs to components because it is not possible to do it without template - formGroup in angular is a circular structure and can't be passed through storybook's control. |
@miszol1 sorry I am not quite understanding what you mean there. Could you provide a simple example of what you are trying to do? There may be another way to do it. |
Sorry that I explained it imprecisely. I mean that in my case I can't use non-template stories because of other blockers. Does it mean I am not able to use default values in SB7, right? |
@miszol1 I'm not sure. I don't know a whole lot about angular templates but there may be a way to write them that doesn't rely on the default value being passed. Something like:
Simply writing the first half is assuming (given the way that templates + defaults work) that |
here I'm having a non-template story and the defaults do not work anyway:
@tmeasday If I understand your point correctly, you think the problem there are no default values- is because @miszol1 used custom template, such as <storybook-button [label]="label">, in his storybook. The code and screenshot I've posted may confirm this is not the reason, since the default values are not set properly even if there is no custom template in the story file. |
@marek-aguita can you upload your example somewhere so we can take a look? You can use https://storybook.new to create a simple reproduction.. |
@tmeasday similar examples are provided in the original reproduction SB7 above. |
@tmeasday of course, here is the example: as we can see, the control 'color' does not have the default value 'warn' set, despite the fact the component itself has it set properly: |
@marek-aguita - OK, that's not a bug, that's what's intended to happen! We are seeing what happens when you don't provide a value for I think if there is an issue here, it's the radio control perhaps could make it clearer that "this arg has no value" rather than right now where it arguably looks broken (although I could argue the other way, that it conveys that information well, 🤷). cc @cdedreuille. I'll note additionally that there is another column in the table called "default value" that's visible in docs mode and can be enabled in the addons panel that would provide the information you wanted. |
@tmeasday but I am providing a value for color, maybe the stackblitz was not saved? Could you please verify you can see this code in the stackblitz? I would expect that if the color property has explicitly set a valid value, in this case 'warn', the corresponding radio button will get selected by default, when the storybook page is opened or am I wrong? |
This looks like a React way of using component and its properties in html but not like in angular! |
Had the same problem today. This really should be fixed in an upcoming release. For now I'm using the following workaround: function attributes(object: object): string {
return Object.entries(object).reduce(
(attributes, [key, value]) => attributes + ` ${key}="${value}"`,
''
);
}
const meta: Meta<LinkComponent> = {
component: LinkComponent,
render: (args) => ({
props: args,
template: `<a me-link ${attributes(args)}>Label</a>`,
}),
}; I'm using I've moved the attributes function into stories/utils/index.ts and exported it from there. |
You are providing a value yes, but not an arg value. The control displays the value of the arg (the input to the component), not the component's internal state. The idea is to show the user what would happen when the component is used with that input. In this case there's no input -- so it's actually helpful to show the user "if you don't supply the
I don't think this "bug" will get fixed. The reason we changed the behaviour was that it caused a lot of problems as it is all based on inferring the default value via static analysis tools (such as compodoc in this case). Those tools are imperfect and get the value wrong in some cases. That causes real problems in peoples' storybook's as components would error when passed the wrong value for an arg. We didn't want to commit to 3rd party inference tools not having such bugs, and as you can see by my argument above, we also think this way makes more sense anyway. It's unfortunate that (a) angular templates don't make it easy to have optional inputs (sort of begs the question why have default values in the first place?) (b) y'all have a lot of stories that relied on this behaviour. Perhaps we can find a workaround? Are the default values available at runtime? Could you do something like: export default: {
component: LinkComponent,
args: {
...LinkComponent.defaults
}
} If that works, you might even be able to the above automatically using an args enhancer. Or you could pull the statically extracted value |
@tmeasday so what you are basically saying in order to show the default value to the users of our Storybook we need to have both default value in the component, such as:
and also set the same value in args:
If yes, I also feel this is a downgrade since now I have to manually set args for all component properties that have some defaults. In SB6 this was really working well. I do hear your argument that compodoc is buggy and that is why this feature was dropped.
Well, not really: I want to show to the user what will happen if they do not provide any value to the I am interested in the workaround with |
@marek-aguita has explained it really well. The new behavior is really dangerous. It totally defeats the confidence that snapshot testing using e.g. chromatic could give you if you heavily rely on default values. That's because you must ensure that component default values and story default args are in sync at all times. That's why I've created my little workaround. However, I don't like that I have to do this. I also think, that the old behavior was much better. |
I think this issue's comments are mixing various different related issues. The way inputs are handled, at the moment, is how I think it should be done, but I do see an issue explained below. If we add the default value to If there is a value in props then Storybook is going to add that input to the generated snippet in "Show Code", but why show inputs being set to their default value? It isn't wrong to do that, but I don't want my developers copying that snippet and setting the input everywhere, because then there isn't really a point in having a default, since everyone is setting it to what the docs showed at the time. I like how it currently works and would not want it to change, unless the functionality can be toggled, because I was actually frustrated by my defaults being in args for no reason. I think the radio Control does show what I would probably consider a bug. The following screenshot is similar to what @marek-aguita posted above. Another problem, which @hettiger referred to, where providing a template does not make it simple to optionally add inputs to the component. He solved it by adding an |
Thanks for commenting @Marklb. You are right there are two issues and I think they are getting mixed up a bit. We can discuss them both but let's be clear about what we are talking about: SB not showing default value in input controlLet's be really clear. There are two things:
The control shows the arg's value. The arg does not automatically get set to the default value at any stage, so it doesn't make sense to put the default value in the input. This is behaviour that is non-Angular specific and is the same across the entirety of Storybook. You can find tickets where React/etc users complain about the same thing, but this is the behaviour we have settled on.
I agree with this, it is inconsistent that other unset/
This is where we disagree. Again, by the argument above, the arg is not getting the default value, so setting it to the default value is confusing. It might make sense to show a placeholder on the input with the default value? That won't work for the radio however. This is a good time for me to mention again, it's possible to show the default value in a separate column, using the Angular templates behaving "badly" when an expected input is not passedIt sounds like maybe this is something @Marklb is looking at. @valentinpalkovic do you know anything about this? This is the area I am less sure about, so I won't stick my foot in it and comment further :) In response to some other questions:
What do you mean by "show"? If you mean render in the component, then no, just don't set the arg and the component will render the default value (modolo the template issue of course). If you mean show in the controls addon/in docs, that's why we have the "default value" column, see above.
This sounds like the template issue above? Like I said, I think this is a different problem with likely a different solution. We can discuss here or on a separate ticket.
Again, is this due to the templating problem? In my mind, setting the arg to the default value automatically (and not actually exercising the default value at all) via an unreliable tool like compodoc would give me less confidence than just not setting the value at all and letting the default apply. Don't you agree?
So my first question is do we have runtime information from Angular about what the defaults are? If so it'd be better to use that than the compodoc-inferred defaults ( Either way, once you have a value to use as the default, you'd use an argsEnhancer to set the arg from the default value (if unset I guess). Something like (in export const argsEnhancers = [({ args, argTypes }) =>
Object.fromEntries(
Object.entries(argTypes).map(([argName, { table: { defaultValue: { summary } } } ]) =>
[argName, args[argName] ?? summary]
)
}),
}); |
@tmeasday I was thinking the default values in props was more specific to Angular than it is. So, based on that clarification, I agree that my expectation about the default populating, when clicking the "Set ..." button, is wrong. |
As @tmeasday already mentioned, let's focus on the Angular templates behaving "badly" when an expected input is not passedThe reason why this feels "bad" is how Angular deals with passed There is a feature request that tries to implement a new kind of data binding, where indeed, the default is taken every time Another way is to handle the undefined value in ngOnChanges to fallback to your default value. But I don't think that the Angular community wants to configure WorkaroundThis all means, that in Storybook we are not allowed to bind a value, if the value is actually function mapArgsToBindings(args: Record<string, any>) {
return Object.entries(args)
.flatMap(([key, value]) => {
if (value !== undefined) {
return [`[${key}]="${key}"`];
} else {
return [];
}
})
.join(' ');
}
export const Template: Story = {
render: (args) => ({
props: args,
template: `<app-example ${mapArgsToBindings(args)}></app-example>`,
}),
args: {
label2: 'story label2',
},
}; Every time an argument is Possible fixesIn Storybook, we could provide the users a "magic string" slice like export const Template: Story = {
render: (args) => ({
props: args,
template: `<app-example $args></app-example>`,
}),
args: {
label2: 'story label2',
},
}; In the background, we replace @tmeasday WDYT? |
Seems sensible to me. Would people sometimes want to pass some of the args to one place and some to another? |
I assume there are some scenarios where this is the case. But I would say that in principle, |
I think the proposed I'm not really sure what the props are in this context. I guess the props define the context / available variables for the template? So these probably have the same problem:
Maybe Storybook could just provide a few exported functions:
Then you can just do something like this:
The magic string solution could also be offered. It could simply be the equivalent of:
If you really want to get fancy you could add parameters to the magic string. E.g. |
Yes
Totally agree. Solving the templating problem is key here I think. |
I like this suggestion as well! If we want to support these additional two use cases, we could also provide one helper function via a callback argument. Like this: const meta: Meta<LinkComponent> = {
component: LinkComponent,
render: (args, {attr}) => ({
props: args,
template: `<a me-link ${attr()}>Label</a>`,
}),
};
interface Props<K> {
include?: Array<K>;
exclude?: Array<K>;
}
function attr<K extends string[]>(props: Props<K>) {
...
} So we could even define some nice types for We wouldn't need to pass |
This is the CSF-defined |
@hettiger, @Marklb, @marek-aguita I have released a snapshot release of this PR: I have introduced a new utility function, The usage is pretty simple:
As a second argument, After talking to @shilman and @ndelangen we have concluded to not provide the utility function via context parameter within the |
Thank you @valentinpalkovic this seems to work great for me so far. If I run into any issues I'll let you know. |
Describe the bug
Consider sb@6 story below
The storybook ui displays buttons with default label and controls with default values applied (
label=Button
andsize=medium
)sb6 screenshot
If this story is translated into sb@7 equivalent,
The ui displays buttons with no default values. The control panel also indicates that these default values are not being used (i.e.
label=undefined
andsize=undefined
)sb7 screenshot
Is this difference expected?
To Reproduce
System
Additional context
I was able to get around the issue by using an
enhancer
.However, not sure if this is reliable, since I had to dig into Storybook codebase to figure out how to use it.
Moreover, it feels like if Storybook team wanted consumers to use
argsEnhancers
api, they would document it and probably provide sample enhancersThe text was updated successfully, but these errors were encountered: