Skip to content

Commit

Permalink
Merge pull request #2959 from jonrimmer/angular-modulemetadata-decorator
Browse files Browse the repository at this point in the history
Add moduleMetdata decorator for supplying common Angular metadata
  • Loading branch information
Hypnosphi authored Feb 19, 2018
2 parents 4f545e8 + 4f135ec commit 6335274
Show file tree
Hide file tree
Showing 19 changed files with 586 additions and 38 deletions.
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';
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 || {};

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

0 comments on commit 6335274

Please sign in to comment.