Skip to content
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

Documentation: angular decorator not working without template #12749

Closed
alexan opened this issue Oct 13, 2020 · 6 comments
Closed

Documentation: angular decorator not working without template #12749

alexan opened this issue Oct 13, 2020 · 6 comments

Comments

@alexan
Copy link
Contributor

alexan commented Oct 13, 2020

the angular decorator documentation has several not working examples (https://storybook.js.org/docs/angular/writing-stories/decorators)

https://storybook.js.org/docs/angular/writing-stories/decorators#wrap-stories-with-extra-markup
is only working if the story is having an template property.

// your-component.stories.ts

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

export default {
  component: YourComponent,
  // this is missing template: '<selector></selector>'
  decorators: [
    (storyFunc) => {
      const story = storyFunc();

      return {
        ...story,
        template: `<div style="margin: 3em">${story.template}</div>`,
      };
    },
  ],
} as Meta;

If only a component is present the template will be undefined and you will get undefined wrapped.
How can I wrap stories which only have component without a template?
I need a way to consitently wrap all stories with and without template in use.
I found this old addon which also have special handling for stories with templates and without, But I don't know how to apply this to a decorator.
https://github.com/storybookjs/storybook/blob/v5.3.21/addons/centered/src/angular.ts

https://storybook.js.org/docs/angular/writing-stories/decorators#context-for-mocking

// .storybook/preview.js

import { ThemeProvider } from 'styled-components';

export const decorators = [
  (Story) => (
    <ThemeProvider theme="default">
      <Story />
    </ThemeProvider>
  ),
];

is an react example not working in an angular project

@shilman
Copy link
Member

shilman commented Oct 15, 2020

have you looked at this? #12656

@alexan
Copy link
Contributor Author

alexan commented Oct 15, 2020

@shilman yes this is the documentation I was refering to. I did't know this is so new.
It will only work if your write all your stories with templates. Stories with only components (which is the current default of writing angular stories) are not working.

I want to know if there is a method of writing wrapping decorators without a template in the story.

As a side note:
I think the current situation with the two ways of writing angular stories with and without template very confusing and also think this is the problem with multiple changes don't accounting for one of them. I think this should be streamlined somehow.
See for example:

@Marklb
Copy link
Member

Marklb commented Oct 16, 2020

For supporting child content, without a template, I think a starting point would be to make this issue work: #10272

@alexan I looked at that centered addon code you linked and that would partially work. It would allow you to render the component, but none of the props would be applied to the component in the template, unless you also implemented something to add them to the template it creates. One of my functions I posted here could partially add them, but it isn't at a point that you could just use it anywhere yet.

Here is how you could use the code from that addon, but I don't it is worth doing as it, since none of the inputs/outputs will be applied.

// your-component.stories.ts

import { Meta, Story } from '@storybook/angular';

function getComponentSelector(component: any) {
  return component.__annotations__[0].selector;
}

function componentAsTemplate(component: any) {
  const selector = getComponentSelector(component)
  return `<${selector}></${selector}>`
}

export default {
  component: YourComponent,
  decorators: [
	// This is needed, because the decorator we are adding makes the StoryFnReturnType have a `template`
	// and Storybook assumes anything used in the `template` has already been declared.
	moduleMetadata({ declarations: [ YourComponent] }),
    (storyFunc) => {
      const story = storyFunc();

      return {
        ...story,
        template: `<div style="margin: 3em">${componentAsTemplate(story.component)}</div>`,
      };
    },
  ],
} as Meta;

export const Example: Story = (args) => ({
  component: YourComponent,
  props: args,
})

@alexan
Copy link
Contributor Author

alexan commented Oct 19, 2020

thank you @Marklb
I improved on your example. Maybe it helps someone.
I think the handling of missing declarations is not completly finished as it is not searching if the component might be defined in some imported module.

here is my attempt:

import { Meta, Story } from '@storybook/angular';

function getComponentSelector(component: any) {
  return component.__annotations__[0].selector;
}

function getBindings(props: {}): string {
  return Object.entries(props).map(([key, value]) => {
    let binding = '';
    if (typeof value === 'function') {
      binding = `(${key})="${key}($event)"`
    } else {
      binding = `[${key}]="${key}"`
    }

    return binding;
  }).join(' ')
}

function componentAsTemplate(component: any, props?: {}) {
  const selector = getComponentSelector(component)
  return `<${selector} ${props ? getBindings(props): ''}></${selector}>`
}

export default {
  component: YourComponent,
  decorators: [
    (storyFunc) => {
    const story = storyFunc();

    let { template } = story;
    const { component, props } = story
    if(!template) {
      template = componentAsTemplate(component, props);

      if(!story.moduleMetadata.declarations) {
        story.moduleMetadata.declarations = []
      }
      if(!story.moduleMetadata.declarations.includes(component)) {
        story.moduleMetadata.declarations.push(component)
      }
    }

    return {
      ...story,
      template: `<div style="margin: 3em">${template}</div>`,
    };
  ],
} as Meta;

export const Example: Story = (args) => ({
  component: YourComponent,
  props: args,
})

this code could also be usefull for: #10617

@stale
Copy link

stale bot commented Dec 25, 2020

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!

@stale stale bot added the inactive label Dec 25, 2020
@shilman
Copy link
Member

shilman commented Jan 13, 2021

w00t!! 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 @next NPM tag to try it out!

npx sb upgrade --prerelease

Closing this issue. Please re-open if you think there's still more to do.

@shilman shilman closed this as completed Jan 13, 2021
@stale stale bot removed the inactive label Jan 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants