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

Add moduleMetdata decorator for supplying common Angular metadata #2959

Merged
merged 17 commits into from
Feb 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/angular/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NgModuleMetadata, ICollection } from './dist/client/preview/angular/types';
export { moduleMetadata } from './dist/client/preview/angular/decorators';

export interface IStorybookStory {
name: string;
Expand Down
2 changes: 2 additions & 0 deletions app/angular/src/client/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { storiesOf, setAddon, addDecorator, configure, getStorybook } from './preview';

export { moduleMetadata } from './preview/angular/decorators';
99 changes: 99 additions & 0 deletions app/angular/src/client/preview/angular/decorators.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { moduleMetadata } from './decorators';
Copy link
Member

@igor-dv igor-dv Feb 13, 2018

Choose a reason for hiding this comment

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

I think we also need to support an arrays concatenation...

for example:

const result = moduleMetadata({
  imports: [FooModule],
})(() => ({
  component: MockComponent,
  moduleMetadata: {
    imports: [BarModule],
  }
}));

I would expect to have both FooModule and BarModule

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have modified it to combine rather than override.

import { addDecorator, storiesOf, clearDecorators, getStorybook } from '..';

class MockModule {}
class MockModuleTwo {}
class MockService {}
class MockComponent {}

describe('moduleMetadata', () => {
it('should add metadata to a story without it', () => {
const result = moduleMetadata({
imports: [MockModule],
providers: [MockService],
})(() => ({
component: MockComponent,
}));

expect(result).toEqual({
component: MockComponent,
moduleMetadata: {
declarations: [],
entryComponents: [],
imports: [MockModule],
schemas: [],
providers: [MockService],
},
});
});

it('should combine with individual metadata on a story', () => {
const result = moduleMetadata({
imports: [MockModule],
})(() => ({
component: MockComponent,
moduleMetadata: {
imports: [MockModuleTwo],
providers: [MockService],
},
}));

expect(result).toEqual({
component: MockComponent,
moduleMetadata: {
declarations: [],
entryComponents: [],
imports: [MockModule, MockModuleTwo],
schemas: [],
providers: [MockService],
},
});
});

it('should return the original metadata if passed null', () => {
const result = moduleMetadata(null)(() => ({
component: MockComponent,
moduleMetadata: {
providers: [MockService],
},
}));

expect(result).toEqual({
component: MockComponent,
moduleMetadata: {
declarations: [],
entryComponents: [],
imports: [],
schemas: [],
providers: [MockService],
},
});
});

it('should work when added globally', () => {
const metadata = {
declarations: [MockComponent],
providers: [MockService],
entryComponents: [MockComponent],
imports: [MockModule],
};

addDecorator(moduleMetadata(metadata));

storiesOf('Test', module).add('Default', () => ({
component: MockComponent,
}));

const [storybook] = getStorybook();

expect(storybook.stories[0].render().moduleMetadata).toEqual({
declarations: [MockComponent],
providers: [MockService],
entryComponents: [MockComponent],
imports: [MockModule],
schemas: [],
});

clearDecorators();
});
});
21 changes: 21 additions & 0 deletions app/angular/src/client/preview/angular/decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NgModuleMetadata } from './types';

export const moduleMetadata = (metadata: Partial<NgModuleMetadata>) => (storyFn: () => any) => {
const story = storyFn();
const storyMetadata = story.moduleMetadata || {};
metadata = metadata || {};
Copy link
Member

Choose a reason for hiding this comment

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

Can the default be introduced in the method signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's how I did it originally, but default params affect undefined or no value, but not null. So I'd need to add a check for null and assignment to {} anyway.


return {
...story,
moduleMetadata: {
declarations: [...(metadata.declarations || []), ...(storyMetadata.declarations || [])],
entryComponents: [
...(metadata.entryComponents || []),
...(storyMetadata.entryComponents || []),
],
imports: [...(metadata.imports || []), ...(storyMetadata.imports || [])],
schemas: [...(metadata.schemas || []), ...(storyMetadata.schemas || [])],
providers: [...(metadata.providers || []), ...(storyMetadata.providers || [])],
},
};
};
6 changes: 6 additions & 0 deletions app/angular/src/client/preview/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function storiesOf(kind: string, module: NodeModule): IApi;
export function setAddon(addon: any): void;
export function addDecorator(decorator: any): IApi;
export function configure(loaders: () => NodeRequire, module: NodeModule): void;
export function getStorybook(): IStoribookSection[];
export function clearDecorators(): void;
52 changes: 52 additions & 0 deletions docs/src/pages/basics/guide-angular/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,55 @@ npm run storybook

Now you can change components and write stories whenever you need to.
You'll get those changes into Storybook in a snap with the help of webpack's HMR API.

## Module Metadata

If your component has dependencies on other Angular directives and modules, these can be supplied using the `moduleMetadata` property on an individual story:

```js
import { CommonModule } from '@angular/common';
import { storiesOf } from '@storybook/angular';
import { MyButtonComponent } from '../src/app/my-button/my-button.component';
import { MyPanelComponent } from '../src/app/my-panel/my-panel.component';
import { MyDataService } from '../src/app/my-data/my-data.service';

storiesOf('My Panel', module)
.add('Default', () => ({
component: MyPanelComponent,
moduleMetadata: {
imports: [CommonModule],
schemas: [],
declarations: [MyButtonComponent],
providers: [MyDataService],
}
}));
```

If you have metadata that is common between your stories, this can configured once using the `moduleMetadata()` decorator:

```js
import { CommonModule } from '@angular/common';
import { storiesOf, moduleMetadata } from '@storybook/angular';
import { MyButtonComponent } from '../src/app/my-button/my-button.component';
import { MyPanelComponent } from '../src/app/my-panel/my-panel.component';
import { MyDataService } from '../src/app/my-data/my-data.service';

storiesOf('My Panel', module)
.addDecorator(
moduleMetadata({
imports: [CommonModule],
schemas: [],
declarations: [MyButtonComponent],
providers: [MyDataService],
})
)
.add('Default', () => ({
component: MyPanelComponent
}))
.add('with a title', () => ({
component: MyPanelComponent,
props: {
title: 'Foo',
}
}));
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Metadata|Combined Combined 1 1`] = `
<storybook-dynamic-app-root
cfr={[Function CodegenComponentFactoryResolver]}
data={[Function Object]}
target={[Function ViewContainerRef_]}
>
<ng-component>
<storybook-simple-token-component
ng-reflect-name="Prop Name"
>


<h3>
Prop Name
</h3>


<p>
Items:
</p>


<ul>



<li>

Joe

</li>
<li>

Jane

</li>


</ul>


</storybook-simple-token-component>
</ng-component>
</storybook-dynamic-app-root>
`;

exports[`Storyshots Metadata|Combined Combined 2 1`] = `
<storybook-dynamic-app-root
cfr={[Function CodegenComponentFactoryResolver]}
data={[Function Object]}
target={[Function ViewContainerRef_]}
>
<ng-component>
<storybook-simple-token-component
ng-reflect-name="CustomPipe: Prop Name"
>


<h3>
CustomPipe: Prop Name
</h3>


<p>
Items:
</p>


<ul>



<li>

Joe

</li>
<li>

Jane

</li>


</ul>


</storybook-simple-token-component>
</ng-component>
</storybook-dynamic-app-root>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Metadata|Individual Individual 1 1`] = `
<storybook-dynamic-app-root
cfr={[Function CodegenComponentFactoryResolver]}
data={[Function Object]}
target={[Function ViewContainerRef_]}
>
<ng-component>
<storybook-simple-token-component />
</ng-component>
</storybook-dynamic-app-root>
`;

exports[`Storyshots Metadata|Individual Individual 2 1`] = `
<storybook-dynamic-app-root
cfr={[Function CodegenComponentFactoryResolver]}
data={[Function Object]}
target={[Function ViewContainerRef_]}
>
<ng-component>
<storybook-simple-token-component>


<h3>
Provider Name
</h3>


<p>
Items:
</p>


<ul>



<li>

Jim

</li>
<li>

Jill

</li>


</ul>


</storybook-simple-token-component>
</ng-component>
</storybook-dynamic-app-root>
`;
Loading