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

Angular template support for Storybook #2690

Merged
merged 20 commits into from
Jan 10, 2018
Merged

Conversation

alterx
Copy link
Member

@alterx alterx commented Jan 8, 2018

Issue: #2645

What I did

Initial work to enable writing stories as follows:

storiesOf('Button', module)
  .add('with text', () => ({
    moduleMetadata: {
      entryComponents: [Button],
      declarations: [Button],
    },
    props: {
      text: 'Hello Button',
      onClick: (event) => {
        console.log('some bindings work');
        console.log(event)
      }
    },
    template: `
      <h1> This is a template </h1>
      <button-component [text]="text" (onClick)="onClick($event)"></button-component>
    `
  }))

How to test

yarn bootstrap --reset --core
cd examples/angular-cli
yarn storybook
open http://localhost:9009

alterx added 2 commits January 8, 2018 12:54
Let's use JS for this, don't see a compelling reason to pull lodash. Also, I don't see it in the deps and it's probably working because most projects use it but it could potentially break if it's not there.

Signed-off-by: Carlos Vega <[email protected]>
alterx added 3 commits January 8, 2018 13:41
Let's use JS for this, don't see a compelling reason to pull lodash. Also, I don't see it in the deps and it's probably working because most projects use it but it could potentially break if it's not there.

Signed-off-by: Carlos Vega <[email protected]>
@amcdnl
Copy link
Member

amcdnl commented Jan 8, 2018

@alterx - I don't think template and component should be used at the same time.

Copy link
Member

@amcdnl amcdnl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@alterx
Copy link
Member Author

alterx commented Jan 8, 2018

@amcdnl we currently need the component for:
a) Adding it to our NgModule
b) Getting the metadata

Even if we make the user provide the bindings as a separate object we'd still need the component to add it to our inner module.

@codecov
Copy link

codecov bot commented Jan 8, 2018

Codecov Report

Merging #2690 into master will decrease coverage by <.01%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2690      +/-   ##
==========================================
- Coverage   34.36%   34.35%   -0.01%     
==========================================
  Files         390      390              
  Lines        8771     8772       +1     
  Branches      899      903       +4     
==========================================
  Hits         3014     3014              
+ Misses       5170     5150      -20     
- Partials      587      608      +21
Impacted Files Coverage Δ
addons/knobs/src/angular/helpers.js 0% <0%> (ø) ⬆️
addons/knobs/src/components/types/Array.js 34.14% <0%> (ø) ⬆️
addons/knobs/src/components/types/Select.js 7.93% <0%> (ø) ⬆️
lib/ui/src/modules/ui/containers/layout.js 12.5% <0%> (ø) ⬆️
lib/components/src/table/table.js 83.33% <0%> (ø) ⬆️
...tive/src/preview/components/StoryListView/index.js 0% <0%> (ø) ⬆️
addons/info/src/components/types/Enum.js 0% <0%> (ø) ⬆️
...ents/stories_panel/stories_tree/tree_decorators.js 34.37% <0%> (ø) ⬆️
.../ui/components/stories_panel/stories_tree/index.js 38.73% <0%> (ø) ⬆️
addons/actions/src/lib/types/function/index.js 77.77% <0%> (ø) ⬆️
... and 43 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1e8c6dc...590677f. Read the comment docs.

alterx added 4 commits January 8, 2018 13:54
Let's use JS for this, don't see a compelling reason to pull lodash. Also, I don't see it in the deps and it's probably working because most projects use it but it could potentially break if it's not there.

Signed-off-by: Carlos Vega <[email protected]>
Let's use JS for this, don't see a compelling reason to pull lodash. Also, I don't see it in the deps and it's probably working because most projects use it but it could potentially break if it's not there.

Signed-off-by: Carlos Vega <[email protected]>
@igor-dv
Copy link
Member

igor-dv commented Jan 8, 2018

@alterx , what if a template contains few components? Maybe worth adding them to the moduleMetadata ?
(also we have to check it with v4 🤦‍♂️ )

@alterx
Copy link
Member Author

alterx commented Jan 8, 2018

@igor-dv yeah, in that case we would add them to themoduleMetadata (it does it like that right now) but the part that worries me (and it's not yet resolved) is that we also need the actual metadata from each component so we can provide the prop.

This is an initial shot at the issue, I'd like @amcdnl to provide a real use case to see how we can implement these details

@amcdnl
Copy link
Member

amcdnl commented Jan 8, 2018

@alterx - I think you mis-understood me. I mean't for this example:

storiesOf('Button', module)
  .add('with text', () => ({
    moduleMetadata: {
       imports: [ButtonModule]
    },
    props: {
      text: 'Hello Button',
      onClick: (event) => {
        console.log('some bindings work');
        console.log(event)
      }
    },
    template: `
      <h1> This is a template </h1>
      <button-component [text]="text" (onClick)="onClick($event)"></button-component>
    `
  }))

Your example is confusing to me, seems like that would go in moduleMetadata's declarations in that situation.

@alterx
Copy link
Member Author

alterx commented Jan 8, 2018

@amcdnl yeah, I know you're talking about that example, I didn't misunderstood you. The component is still there because, currently, I'm using it to get the metadata.

@alterx
Copy link
Member Author

alterx commented Jan 8, 2018

This is just a contrived example, a POC. So it currently only supports creating the template using the bindings of that component we provide. That's why it has the label do-not-merge is not ready for primetime. I just submitted the PR for visibility (let people know is being worked on) and, of course, for you to test it.

@amcdnl
Copy link
Member

amcdnl commented Jan 8, 2018

@alterx - Why do you need it? I'm confused on that part I guess.

@alterx
Copy link
Member Author

alterx commented Jan 8, 2018

Because I didn't change the way this works. I just added the template parts on top of it so it still uses the component for props metadata. Hopefully it's not gonna be like that at the end. I just wanted to get it rendering a template.

Long story short: if you run the code as is and remove the component it breaks. Something that I need to fix. Wanted to be sure we can render it before adding more stuff and changing how it works.

@alterx alterx requested review from igor-dv and ralzinov January 10, 2018 01:22
@alterx
Copy link
Member Author

alterx commented Jan 10, 2018

With the use of moduleMetadata and some other changes this PR has also reduced the amount of code needed to bootstrap a component. The angular app is no longer doing manual checks on the metadata and only creates a dynamic component if the story provides a template*. The rest of the work is offloaded to Angular itself. Been wanting to do this for a while :D
As a result, the API remains almost intact (I added the template option) while the code complexity has been reduced.

*Manual metadata checks are still needed inside the knobs addon (which needs to manually deal with the change events that come from the knobs channel and send them to the angular component).

@alterx alterx mentioned this pull request Jan 10, 2018
Copy link
Member

@igor-dv igor-dv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so much easier and cleaner! Did you check it with v4?

const NewModule: any = function() {};
(<IModule>NewModule).annotations = [moduleMeta];
return NewModule;
const DYNAMIC_COMPONENT_SELECTOR = 'storybook-dynamic-component';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you move it up?

const DYNAMIC_COMPONENT_SELECTOR = 'storybook-dynamic-component';
const createComponentFromTemplate = (
template: string,
params?: any[],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params are not used here

import { FormsModule } from '@angular/forms';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './components/app.component';
import { ErrorComponent } from './components/error.component';
import { NoPreviewComponent } from './components/no-preview.component';
import { STORY } from './app.token';
import { getAnnotations, getParameters, getPropMetadata } from './utils';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, utils.ts can be deleted?

@alterx
Copy link
Member Author

alterx commented Jan 10, 2018

Yup, checked in v4 :)

selector?: string
): IComponent => {
const metadata = new Component({
selector: selector || DYNAMIC_COMPONENT_SELECTOR,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to have a selector.

params,
moduleMeta,
template,
} = getComponentMetadata(currentStory(context));

if (!componentMeta) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add some errors like:

  if (!template && (!component || typeof component !== 'function')) {
    throw new Error('No valid component provided');
  }

  if (template && component) {
    throw new Error('Component and template are mutually exclusive.');
  }

@alterx alterx merged commit a1b9231 into master Jan 10, 2018
@alterx alterx deleted the angular-template-support branch January 10, 2018 14:38
@teebszet
Copy link

Should there be documentation on this feature in https://storybook.js.org/basics/guide-angular/#write-your-stories?

@shilman
Copy link
Member

shilman commented Feb 28, 2019

@teebszet Any chance you can contribute some docs on this?

@breadadams
Copy link
Contributor

Docs would be real handy, like Vue for example there are 2 ways of creating a story, whereas Angular shows the same twice.

Once I can wrap my head around this myself I'll be happy to update it 👍

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

Successfully merging this pull request may close these issues.

7 participants