fix(storybook-angular): add missing applyDecorators to render annotaions#2086
Conversation
✅ Deploy Preview for analog-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-blog ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-app ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
The reproduction environment is here. https://github.com/Bayathy/analog-storybook When running FAIL storybook (chromium) src/stories/page.stories.ts > Logged In
TestingLibraryElementError:
Click to debug the error directly in Storybook: http://localhost:6006/?path=/story/example-page--logged-in&addonPanel=storybook/interactions/panel
Unable to find an accessible element with the role "button" and name `/Log in/i`
There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the `hidden` option to `true`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole
Ignored nodes: comments, script, style
<div
data-sb-story-uid="example-page--logged-in-0"
id="example-page--logged-in"
>
<example-page--logged-in
example-page--logged-in-0=""
ng-version="21.1.5"
>
<div
style="padding: 1rem;"
>
undefined
</div>
</example-page--logged-in>
</div>
❯ getByRole src/stories/page.stories.ts:24:31
22| play: async ({ canvasElement }) => {
23| const canvas = within(canvasElement);
24| const loginButton = canvas.getByRole('button', { name: /Log in/i });
| ^
25| await expect(loginButton).toBeInTheDocument();
26| await userEvent.click(loginButton);
❯ node_modules/.cache/storybook/10.2.11/95209e453a51f1cc97c9c8cfcc07cc086d2a83ebdbfe04ef274ba9013d3e81d4/sb-vitest/deps/@storybook_addon-vitest_internal_test-utils.js?v=8217aa13:126:184The story rendered inside componentWrapperDecorator is becoming undefined. |
|
Thanks @Bayathy! |
|
@Bayathy @brandonroberts I installed 2.3.0-beta.17 and it breaks my storybook interaction test in vitest where i use output-binding. (The test works inside storybook ui and with 2.3.0-beta.16). It looks to me that the render in vitest does not add the component bindings correctly. import { Meta, StoryObj } from '@storybook/angular';
import { expect, fn } from 'storybook/test';
import { DemoComponent } from './demo.component';
const meta = {
component: DemoComponent,
args: { btnClicked: fn() },
} satisfies Meta<DemoComponent>;
export default meta;
type Story = StoryObj<DemoComponent>;
export const Default: Story = {
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByTestId('demo-btn'));
// the mock fn() does not get called, it is not bound correctly
void expect(args.btnClicked).toBeCalled();
},
}; |
|
here is what i get when i log the story in a decorator: componentWrapperDecorator((story) => {
console.log({ story });
return `
<div id="wrapper">
${story}
</div>`;
});2.3.0-beta.17 2.3.0-beta.16 |
|
at the moment i work around the decorator problem with this helper to test, if the story runs inside vitest. Then i don't apply my decorators at all... /**
* Helper method to test if in vitest environment.
* @returns If is vitest environment.
*/
export function isVitestBrowser(): boolean {
return window.__vitest_browser__ ?? false;
}But when this patch will land in the final release, i would not know how to get my tests fixed. @brandonroberts I would prefer to revert this change, if it can not be fixed... |
|
Yes, we will revert this and revisit other options. We at least have a reproduction now |
|
@prclm This issue might be related: import type { Meta, StoryObj } from '@storybook/angular';
import { expect, fn } from 'storybook/test';
import { DemoComponent } from './demo.component';
const meta: Meta<DemoComponent> = {
title: 'Example/Demo',
component: DemoComponent,
+ argTypes: { btnClicked: { action: 'btnClicked' }, },
args: { btnClicked: fn() },
};
export default meta;
type Story = StoryObj<DemoComponent>;
export const Default: Story = {
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByTestId('demo-btn'));
void expect(args.btnClicked).toBeCalled();
},
};With this modification, the story output in my environment looked like this: <app-demo (btnClicked)="btnClicked($event)"></app-demo> |
Thanks @Bayathy, this indeed fixes the tests. Do you think it is possible to inject a console.log somehow to give the developer a hint what is going on? @brandonroberts is it possible to bring this MR changes back in the next version and maybe make a "troubleshooting" note somewhere if people run in this bug, that they need to add the actions as "argTypes"? |
|
@prclm Sure, we can add it to the bottom of the guide |
PR Checklist
Closes #2074
What is the new behavior?
@storybook/angular'scomponentWrapperDecorator()depends onstoryFn().templateinternally.This
storyFn().templateis resolved byprepareMain(), which is called inside Storybook'sdecorateStory(). This function is exported asapplyDecoratorswithin Storybook.However, before this fix,
decorateStory()was not being called at runtime. Instead, Storybook's generic rendererdefaultDecorateStorywas being used. SincedefaultDecorateStorydoes not callprepareMain(),storyFn().templateremained unresolved.This happens because
prepareStory(), whichis called during portable story rendering in the Vitest environment, contains the following:
When applyDecorators is not defined in
projectAnnotations, it falls back todefaultDecorateStory.With this fix,
applyDecoratorsis now included inrenderAnnotationsand passed through toprepareStory(). This causes Angular'sdecorateStory()to be used, which callsprepareMain()and properly resolvesstoryFn().template.As a result,
storyFn().templateinsidecomponentWrapperDecorator()is no longer undefined.Does this PR introduce a breaking change?
Other information
This is my first PR to an OSS project. English is not my first language, so I wrote this description in Japanese first and then had it translated. I apologize if anything is unclear or hard to read.