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

[@storybook/angular] [Knobs] forceRerender doesn't trigger Constructor #9288

Closed
EliezerB123 opened this issue Jan 1, 2020 · 13 comments
Closed

Comments

@EliezerB123
Copy link

EliezerB123 commented Jan 1, 2020

There seems to be no ability to force a story to use Knobs to modify the constructor of its component. Various issues have been opened about this, but they all inevitably become stale, then closed without resolution.

To Reproduce
Steps to reproduce the behavior:

  1. Create a component and story with the following code:
@Component({
  selector: 'mybutton',
  template:'<div>Sample</div>',
})
export class ButtonComponent implements OnInit {
  @Input() myText?: string;
  constructor() {
    console.log('I"m under construction!');
    console.log('Text is: ', this.myText);
  }
  ngOnInit() {
    console.log('I"m being initialized!');
    console.log('Text is: ', this.myText);
  }
}

import { FORCE_RE_RENDER } from '@storybook/core-events';
import { addons } from '@storybook/addons';
import { text, withKnobs, button } from '@storybook/addon-knobs';
import { ButtonComponent } from '../somewhere/component.ts';

export default { 
  title: 'Banner',
  decorators: [
    withKnobs
  ]
}
export const Sample = () => ({
  component: ButtonComponent,
  props: {
    myText: text('myText', 'Main Title'),
    button: button('Refresh', () => {
      console.log('Attempting to trigger refresh');
      addons.getChannel().emit(FORCE_RE_RENDER);
    })
  },
});

Expected Result
There should be a console.log with the current value of the Knob input for both the constructor and ngOnInit every time ForceRender is run.

Failing that, there should be some way to trigger the constructor if desired, using the current value of the knobs.

System:
Environment Info:
System:
OS: Windows Server 2016 10.0.14393
CPU: (4) x64 Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz
Binaries:
Node: 10.16.0 - C:\Program Files\nodejs\node.EXE
npm: 6.9.0 - C:\Program Files\nodejs\npm.CMD

Package.json:
"@storybook/addon-actions": "^5.2.8",
"@storybook/addon-links": "^5.2.8",
"@storybook/addon-notes": "^5.2.8",
"@storybook/addons": "^5.2.8",
"@storybook/angular": "^5.2.8",
"@storybook/addon-knobs": "^5.2.8",
"@storybook/cli": "^5.2.8",
Angular CLI: 8.3.20
Angular: 8.2.14
@angular-devkit/architect 0.803.20
@angular-devkit/build-angular 0.803.21
@angular-devkit/build-ng-packagr 0.803.21
@angular-devkit/build-optimizer 0.803.21
@angular-devkit/build-webpack 0.803.21
@angular-devkit/core 8.3.20
@angular-devkit/schematics 8.3.20
@angular/cdk 8.2.3
@angular/cli 8.3.20
@angular/flex-layout 8.0.0-beta.27
@angular/material 8.2.3
@ngtools/webpack 8.3.21
@schematics/angular 8.3.20
@schematics/update 0.803.20
ng-packagr 5.7.1
rxjs 6.4.0
typescript 3.5.3
webpack 4.39.2

Additional context
It seems there has been some discussion about this in the past, but it inevitably ends in a "Stale" tag and the thread being locked.

#7002
#4760

Not a duplicate of:

#8825
#1736

Seems to be directly related to (Or even caused by):
#6094

@shilman
Copy link
Member

shilman commented Jan 2, 2020

@Terrafire123 the issues go stale because nobody is picking them up. Do you want to try to fix this issue?

@dfagan2
Copy link

dfagan2 commented Jan 6, 2020

Running into the same thing

@CodeByAlex
Copy link
Member

CodeByAlex commented Jan 18, 2020

@dfagan2 + @Terrafire123 Quick workaround if you want a full refresh of the iframe:
refresh: button('Refresh', () => { document.location.reload(); })

It works like a charm!

@EliezerB123
Copy link
Author

EliezerB123 commented Jan 20, 2020

@CodeByAlex @shilman

Hey, CodeByAlex.

Thank you for the suggestion. I'm sorry for not properly explaining the issue clearly, and I've modified the "expected result" of the original post to be more clear. Unfortunately, it doesn't resolve the problem, at all.

In short, the expected result is that the page should be re-rendered using the current value of the knobs.

The code you suggested reloads the page, but two problems occur:

  1. Knobs reset to their default values.
  2. Knobs still don't modify the inputs during the constructor or OnInit().

The code you provided unfortunately doesn't resolve the issue, and doesn't appear to be much different than simply hitting the browser refresh button.

@stale
Copy link

stale bot commented Feb 11, 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 Feb 11, 2020
@CodeByAlex
Copy link
Member

@Terrafire123 - I apologize, not sure what I was thinking you meant when I first posted this but your issue clearly defines the problem. I’m not sure of an answer for that at this time.

@stale stale bot removed the inactive label Feb 12, 2020
@stale
Copy link

stale bot commented Mar 4, 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 Mar 4, 2020
@stale
Copy link

stale bot commented Apr 3, 2020

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@stale stale bot closed this as completed Apr 3, 2020
@jose-airspace
Copy link

jose-airspace commented May 5, 2020

@Terrafire123 @CodeByAlex this happens for me when using a react class component. When a knob changes the render function gets called but the constructor does not.

Sample component:

class MyContainer extends React.Component {
  constructor(props) { // called only on first render
    super(props);
    this.store = props.store; // only happens with original value
  }

  render() { // called every time Device ID knob changed
    const { ping } = this.props.store; // works
    //const { ping } = this.store; // does not work (only has original value)

    return (
      <div>
        {ping.device_name}
      </div>
    );
  }
}

Sample story:

export const showingPing = () => {
  const device_name = text('Device ID', 'T84007');
  const ping = { device_name };
  const store = { ping };

  return <MyContainer store={store} /> // called every time Device ID knob changed
};

EDIT

I am realizing that react just works like that but I have not noticed in the past when I have worked with it, sorry to bother!

@tc4rdpp
Copy link

tc4rdpp commented Jun 10, 2020

Did anyone get this forceReRender to work?

@EliezerB123
Copy link
Author

EliezerB123 commented Jun 10, 2020

ForceRerender doesn't work. Or rather, it triggers change detection instead of actually forcing a rerender. But I found a successful work-around for Angular...

Simply put, I created an *ngIf wrapper around every component in storybook, then added a "Refresh" button to toggle the *ngIf, in order to turn off the component, then turn it back on. When it turns back on, it re-triggers the constructor and ngInit.

I THINK this solution will only work if you're using a string template. If your template comes directly from the component, then I don't know if this will work.

This is my code:

/.storybook/preview.js:

import { addDecorator } from '@storybook/angular';
import { withKnobs } from '@storybook/addon-knobs';
addDecorator(withKnobs);

const container = (originalStoryFn) => {
  let story = originalStoryFn();
  console.log('omgz story', story);
  return {
    ...story,
    // The refresh button here forces the component to re-render, triggering the component's constructor and OnInit.
    template: `
      <style>
        .story-container {
          padding: 10px;
        }
      </style>
      <section class="story-container" #open>
        <div style="display: flex;justify-content: center; margin: 10px auto;">
          <button onclick="var button = document.getElementById('refresh-btn'); button.click();setTimeout(button.click(),300)">Refresh</button>
          <button id="refresh-btn" style="display:none" (click)="open.value = !open.value;"></button>
        </div>
        <hr/>
        <br/>
        <div *ngIf="!open.value">
            ${story.template}
        </div>

      </section>
    `
  };
};
addDecorator(container);

@tc4rdpp
Copy link

tc4rdpp commented Jun 10, 2020

Thank you @Terrafire123, I will give it a try!

@shilman
Copy link
Member

shilman commented Jun 11, 2020

FYI, we’ve just released addon-controls in 6.0-beta! Controls are portable, auto-generated knobs that are intended to replace addon-knobs long term.

Please upgrade and try them out today. Thanks for your help and support getting this stable for release!

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

6 participants