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

Unable to use decorators when this addon is enabled #40

Open
sagarrchauhan opened this issue Jul 28, 2023 · 1 comment
Open

Unable to use decorators when this addon is enabled #40

sagarrchauhan opened this issue Jul 28, 2023 · 1 comment

Comments

@sagarrchauhan
Copy link

sagarrchauhan commented Jul 28, 2023

I am use SDC with Storybook using CL server. When trying to use a decorator to give a wrapper around the story, there seems to be an issue with the modified story. The story comes as undefined.

My layout.stories.js file:

import { html } from 'lit';

export default {
  component: 'layout',
  decorators: [(story) => {
    console.log("Story", story())
    return html `<div class="container">${story()}</div>`
  } ],
};

export const CardList = {
  args: {
    // Some Args
  },
}

Result of console.log on browser
image

I also tried using makeDecorator function but got the same issue

import {makeDecorator} from "@storybook/addons";

export const withCustomLayout = makeDecorator({
  name: 'withCustomLayout',
  parameterName: 'customLayout',
  skipIfNoParametersOrOptions: true,
  wrapper: (getStory, context, { parameters }) => {
    // get a reference to the active story
    const story = getStory(context);
    // modify the story's content as a string
    const decoratedStory = `<div class="container">${ story }</div>`;
    // return the modified story string
    return decoratedStory;
  }
});

Can we pass in the custom user decorators somehow so that there can be some wrappers above Drupal generated code too?

@andy-blum
Copy link
Member

+1 to needing decorators. Since components are often constrained by layouts that are external to the component, this plugin should offer a way to wrap components in additional markup only when previewed in storybook. This would also be useful for allowing storybook to demonstrate how multiple components would look when displayed together, for example a grid of cards.

After looking into this with @pjudge and @jkaeser today and here's what we've learned:

This add-on is blowing away everything storybook renders

fetchStorybookHtml is set as a global server parameter, and when called does the following:

  • Fetches a POST request to Drupal through /_cl_server path with the current story's information
  • Waits for Drupal to render the specified story through the specified theme
    • Drupal renders the requested component wrapped in an element #___cl-wrapper
  • Once the rendered markup is returned, parses the text into an HTMLFragment
  • Replaces the <body> with the return value of createNewBody

createNewBody in turn does the following:

  • Finds the #___cl-wrapper element
  • Finds all scripts on the page
  • Creates a new <body> element and duplicates over all attributes from the original <body> element
  • Copies the innerHTML of #___cl-wrapper into the new body (this removes all the other parts of the rendering theme like menus, blocks, etc)

This configuration essentially bypasses all the rendering & plugin capabilities of storybook, and leaves you with it as simply a way to catalog all the components. Since the "server" parameter in use doesn't ever look for decorators, they will never be applied.

How this plugin could add in this functionality

There are two paths that I can imagine this plugin/cl_server combo taking here:

1. The "storybook" or "javascript" way

In storybook, decorators are functions that wrap a story's markup in additional markup. These can be added globally, per-component, or per-story. While a 1-to-1 implementation probably isn't likely, I could envision a scenario where the fetchStorybookHtml function described above ends it's process by emitting a new event from window that could be listened for and responsed to by components.

export default {
  component: Button,
  decorators: [
    (Story) => {
      window.addEventListener('cl_component_rendered', () => {
        // do some custom DOM manipulation here after Drupal has returned the rendered component
      })
    }
  ],
};

This method has the downside of not being usable with *.stories.yml files since yml doesn't offer a way to define javascript functions

2. The "Drupal" way

Add support for a new decorator.html.twig file that includes then includes the actual component twig file, allowing markup to be rendered for storybook that wouldn't get rendered in normal Drupal theme usage.

<div class="grid">
  {% include 'component.html.twig' %}
</div>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants