storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
83.49k stars 9.13k forks source link

Addon-docs: DocsPage Controls don't update iframe stories #11908

Open shilman opened 3 years ago

shilman commented 3 years ago

Controls don't update iframed stories on the Docs tab, but do update on the Canvas tab.

controls-bug

For frameworks that support inline rendering (react, vue, web-components, etc.), making the docs stories render inline is a workaround.

michaelbayday commented 3 years ago

i was experiencing something similar to this with react

petermikitsh commented 3 years ago

I've been observing controls not updating the render on the canvas tab, with react

shilman commented 3 years ago

@michaelbayday @petermikitsh do either of you have a repro you can share? haven't seen that yet.

blowsie commented 3 years ago

@shilman

9WZt9d1cgN

reproduced in vue, here https://github.com/storybookjs/storybook/issues/11990

blowsie commented 3 years ago

Reproduction is now showing the controls and showing the issue https://github.com/blowsie/storybook-vue

shilman commented 3 years ago

The problem is the story:

https://github.com/blowsie/storybook-vue/blob/master/src/TestComponent.stories.js#L11

Try:

const Template = (args, { argTypes }) => ({
blowsie commented 3 years ago

Fixed my issue, thanks!

petermikitsh commented 3 years ago

@shilman No repro; my observation was a hiccup on my upgrade from 5 to 6. My apologies!

flobiber commented 3 years ago

So for me i also have this issue with angular -> here is an example repo: https://github.com/flobiber/storybook-angular

manuelmeister commented 3 years ago

Can be observed here as well: https://storybookjs.netlify.app/angular-cli/?path=/docs/docbutton--basic

jamesjenkinsjr commented 3 years ago

Scouring around, this workaround got my Docs page re-rendering with changes to controls (for React implementations, but perhaps could also work for Vue?):

import { parameters } from "@storybook/addon-docs/dist/frameworks/react/config";
import { addParameters } from '@storybook/react';
import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks';

addParameters({
  docs: {
    ...parameters.docs,
    container: DocsContainer,
    page: DocsPage,
  },
  controls: {
    expanded: true,
  }
});

Note the import and spread of parameters.docs

shilman commented 3 years ago

@jamesjenkinsjr if you needed to do this it's due to a configuration problem and not related to the iframe issue here. I just want to clarify for anybody who stumbles across this to avoid confusion.

ThibaudAV commented 3 years ago

with this PR it will work https://github.com/storybookjs/storybook/pull/13525

lucas-labs commented 2 years ago

Hi! I'm experiencing this issue with Angular + iframes. I see #13525 was already merged and it should've fixed this. There's no one else having this problem any more? Maybe it's something on my side?

shilman commented 2 years ago

@lucas-labs you need to opt into inline rendering by configuring .storybook/preview.js:

export const parameters = { docs:  { inlineStories: true } };
dmartinjs commented 2 years ago

@lucas-labs I have this problem too.

@shilman but if we have a component taking the whole page as popin for example ? with that I can't render inline and controls are still not working with iframes.

lucas-labs commented 2 years ago

@dmartinjs that's kind of my situation too. In my case, the component is not taking the whole page, but it's a static header stuck to the top, and since I have several stories, using inline components causes all my stories to render one on top of each other taking the same space in the docs page.

RenanRossiDias commented 2 years ago

Same problem in Vue. :/

dmartinjs commented 2 years ago

I found a partial solution to deal with this by disabling iframe and using decorators to set a height and use the transform: scale(1) trick to have the component only render in the story.

So for example in my Story (in web component context):

export default {
  title: 'Indicators/Toast',
  decorators: [(story) => html`<div style="height: 100vh;transform: scale(1);">${story()}</div>`],
}
qortex commented 2 years ago

@lucas-labs you need to opt into inline rendering by configuring .storybook/preview.js:

export const parameters = { docs:  { inlineStories: true } };

Stumbled upon this too (docs not updating on input changes, but canvas does), and this fixes the issue with import { addParameters } from '@storybook/angular';.

Shouldn't it be made the default for Angular (and/or other frameworks?)? What are the drawbacks?

tannguyenad commented 2 years ago

Im facing the same issue. Not only the controls but also the global context does NOT work properly in Docs page.

EliezerB123 commented 1 year ago

@lucas-labs you need to opt into inline rendering by configuring .storybook/preview.js:

export const parameters = { docs:  { inlineStories: true } };

Shouldn't it be made the default for Angular (and/or other frameworks?)? What are the drawbacks?

@qortex Apparently it's deliberately set to false by default for Angular. Not sure what are the drawbacks, though... https://github.com/storybookjs/storybook/pull/14447

SushkaVlad commented 1 year ago

Hello, everybody. I'm facing the same issue using parameter - docs: { page: DocsPage, inlineStories: false, iframeHeight: 500 } (Framework - React). Am i right that the only option is to switch inlineStories to true. And how can we solve that if we need to render component in iframe. @shilman

shilman commented 1 year ago

@SushkaVlad you're correct that's the only option for now

@tmeasday will any of the recent docs rearchitecture address this limitation of addon-docs custom useArgs hook for iframed stories?

SushkaVlad commented 1 year ago

@shilman Thanks a lot)

tmeasday commented 1 year ago

@shilman I don't think we've changed the implementation, but then again it uses the channel so it's not clear to me why it wouldn't work. Probably something we should look at while we revisit the ArgsTable component cc @JReinhold

tmeasday commented 1 year ago

Oh, is the issue that we have a preview embedded inside a preview and messages aren't making it three layers deep? I guess that's probably the problem, scratch what I just said above.

I don't think anything we are doing will influence this problem. It's sort of orthogonal to everything else.

quantizor commented 1 year ago

Hi, I saw that @shilman closed my bug ticket in #22148, is a fix being planned?

tmeasday commented 1 year ago

@probablyup it's not something that's actively being worked on. But would be happy to support someone working on it.

quantizor commented 1 year ago

I could take a crack at it with a bit of guidance

tmeasday commented 1 year ago

Sure thing @probablyup. So here goes. When you render a story non-inline in docs, you end up with the following scenario:

 ┌─────────────────────────────┐
 │ manager                     │
 │     ┌────────────────────┐  │
 │     │  canvas/docs       │  │
 │     │       ┌─────────┐  │  │
 │     │       │ story   │  │  │
 │     │       └─────────┘  │  │
 │     │       ┌─────────┐  │  │
 │     │       │ story   │  │  │
 │     │       └─────────┘  │  │
 │     └────────────────────┘  │
 └─────────────────────────────┘

The canvas and the storys in the above picture are all instances of a "preview" -- each instance is relatively clueless about the others.

Each of the manager, canvas and storys sets up a post message channel (manager, preview), which will:

So in terms of message flow, it goes like:

  manager◄─────┐canvas◄─────story
         └─────►

Note as well that any context can listen to its own messages.

You can get a sense for all this by turning on logLevel: 'debug' in main.js and closely looking at the emitted messages.


OK, so that's the context.

The specific issue here is that each of the previews (canvas, storys`) has their own story store, which has internal state containing the current value of the args. So we need to take steps to keep them in sync[2].

Currently the main canvas's args are updated by the UPDATE_STORY_ARGS event which can be emitted when you change controls in the manager or in the canvas itself.

So the simplest solution to the problem would be for the canvas to simply proxy every message it receives into each child story. I don't think the canvas should similarly proxy messages from the story up to the manager, as that will confuse the manager as to what is selected, etc.

I think the simplest way to do that would be to augment @storybook/channel-postmessage to simply do that "forwarding to subframes" when this.config.page === 'preview'. Does that make sense?

WDYT about all this @ndelangen ?

[1] there can be >1 in the case of composition, not relevant here. [2] I don't know if it's possible to only have a single store, but that would be a much bigger and more complex change. The solution I'm proposing here will definitely have some quirks as a result of this but I think is a reasonable tradeoff, unless someone has a better idea.

valentinpalkovic commented 1 year ago

I am closing this because it works appropriately in Storybook 7. Please feel free to reopen it with a reproduction on Stackblitz (https://storybook.new)

quantizor commented 1 year ago

I am closing this because it works appropriately in Storybook 7.

It doesn't, at least in storystore v6 mode

quantizor commented 1 year ago

I think the simplest way to do that would be to augment @storybook/channel-postmessage to simply do that "forwarding to subframes" when this.config.page === 'preview'. Does that make sense?

This makes sense to me... perhaps it should only be to the primary story, since that's what the Controls block is typically affecting?

Edit: unless the message already has the story ID encoded so the story itself knows to not update if the message sent doesn't match

valentinpalkovic commented 1 year ago

@probablyup could you provide a simple reproduction on stackblitz? https://storybook.new

quantizor commented 1 year ago

@probablyup could you provide a simple reproduction on stackblitz? https://storybook.new

Here you go: https://stackblitz.com/edit/github-yxuht3?file=src%2Fstories%2FButton.tsx,src%2Fstories%2FButton.stories.ts&preset=node

If you go to the Buttons "Docs" story you can see that I added the following to the meta config:

parameters: {
  docs: {
    story: {
      inline: false,
    },
  },
},

After adding this, changing the controls for the primary story has no effect.

tmeasday commented 1 year ago

@valentinpalkovic I believe this issue affects v7 mode also.

tmeasday commented 1 year ago

I think the simplest way to do that would be to augment @storybook/channel-postmessage to simply do that "forwarding to subframes" when this.config.page === 'preview'. Does that make sense?

This makes sense to me... perhaps it should only be to the primary story, since that's what the Controls block is typically affecting?

I don't think we can or should be that specific in the forwarding, I think we should just forward every message on from the manager -> sub previews. Otherwise it'll be confusing (and harder to do!).

Edit: unless the message already has the story ID encoded so the story itself knows to not update if the message sent doesn't match

That's correct. The storyId is encoded in the message.

ndelangen commented 1 year ago

I think the simplest way to do that would be to augment @storybook/channel-postmessage to simply do that "forwarding to subframes" when this.config.page === 'preview'. Does that make sense?

Yes, that makes sense @tmeasday

seriouz commented 12 months ago

We also ran into the problem today. Is a quick fix available?

ndelangen commented 11 months ago

No quick fix yet @seriouz

asso1985 commented 7 months ago

Any news on this? Facing same issue.

jyang81 commented 7 months ago

I'm also facing this issue. I need to set the iframe height for my stories, but because of the inline: false the controls do not update the story.

parameters: {
    docs: {
      story: {
        inline: false,
        iframeHeight: 300
      }
    }
  }

Is there any other way to set the iframe height?

mojabyte commented 6 months ago

I'm also facing this issue. I need to set the iframe height for my stories, but because of the inline: false the controls do not update the story.

parameters: {
    docs: {
      story: {
        inline: false,
        iframeHeight: 300
      }
    }
  }

Is there any other way to set the iframe height?

If you want to set the height of the story preview only without setting the inline: false, you can use the docs.story.height parameter:

{
  parameters: {
    docs: {
      story: {
        height: '300px'
      }
    }
  }
}
kerryj89 commented 6 months ago

I noticed Storybook styles colliding into the inline stories, so I then thought to use the iframe mode to prevent that. And now I'm faced with the realization that controls do not working with iframe stories in docs page.

I've created a PR that would let users know of this current iframe stories + docs + controls limitation in the docs https://github.com/storybookjs/storybook/pull/25593