Skip to content

Commit

Permalink
Merge pull request #13525 from storybookjs/angular-inline-docs
Browse files Browse the repository at this point in the history
Addon-docs/Angular: Inline rendering support with angular-elements
  • Loading branch information
shilman authored Jan 28, 2021
2 parents 5fc5141 + 53248ca commit 8958e84
Show file tree
Hide file tree
Showing 48 changed files with 578 additions and 120 deletions.
4 changes: 2 additions & 2 deletions addons/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ Storybook Docs supports all view layers that Storybook supports except for React
| Source | + | + | + | + | + | + | + | + | + | + | + |
| Notes / Info | + | + | + | + | + | + | + | + | + | + | + |
| Props table | + | + | + | + | + | | | | | | |
| Props controls | + | + | | | | | | | | | |
| Props controls | + | + | + | | | | | | | | |
| Description | + | + | + | + | + | | | | | | |
| Inline stories | + | + | | | + | + | | | | | |
| Inline stories | + | + | + | | + | + | | | | | |

**Note:** `#` = WIP support

Expand Down
24 changes: 24 additions & 0 deletions addons/docs/angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,30 @@ And for `MDX` you can modify it as an attribute on the `Story` element:
<Story name='basic' height='400px'>{...}</Story>
```

## Inline Stories

Storybook Docs renders all Angular stories inside IFrames by default. But it is possible to use an inline rendering:

To get this, you'll first need to install Angular elements:

```sh
yarn add -D @angular/elements @webcomponents/custom-elements
```

Then update `.storybook/preview.js`:

```js
import { addParameters } from '@storybook/angular';
import { prepareForInline } from '@storybook/addon-docs/angular/inline';

addParameters({
docs: {
inlineStories: true,
prepareForInline,
},
});
```

## More resources

Want to learn more? Here are some more articles on Storybook Docs:
Expand Down
1 change: 1 addition & 0 deletions addons/docs/angular/inline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../dist/esm/frameworks/angular/prepareForInline');
5 changes: 5 additions & 0 deletions addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"@babel/core": "^7.12.10",
"@emotion/core": "^10.1.1",
"@emotion/styled": "^10.0.27",
"@storybook/angular": "6.2.0-alpha.18",
"@storybook/react": "6.2.0-alpha.18",
"@storybook/vue": "6.2.0-alpha.18",
"@storybook/web-components": "6.2.0-alpha.18",
Expand Down Expand Up @@ -123,6 +124,7 @@
},
"peerDependencies": {
"@babel/core": "^7.11.5",
"@storybook/angular": "6.2.0-alpha.18",
"@storybook/vue": "6.2.0-alpha.18",
"babel-loader": "^8.0.0",
"react": "^16.8.0 || ^17.0.0",
Expand All @@ -132,6 +134,9 @@
"webpack": ">=4"
},
"peerDependenciesMeta": {
"@storybook/angular": {
"optional": true
},
"@storybook/vue": {
"optional": true
},
Expand Down
47 changes: 47 additions & 0 deletions addons/docs/src/frameworks/angular/prepareForInline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { IStory, StoryContext } from '@storybook/angular';
import { ElementRendererService } from '@storybook/angular/element-renderer';
import { StoryFn } from '@storybook/addons';

const customElementsVersions: Record<string, number> = {};

/**
* Uses angular element to generate on-the-fly web components and reference it with `customElements`
* then it is added into react
*/
export const prepareForInline = (storyFn: StoryFn<IStory>, { id, parameters }: StoryContext) => {
// Upgrade story version in order that the next defined component has a unique key
customElementsVersions[id] =
customElementsVersions[id] !== undefined ? customElementsVersions[id] + 1 : 0;

const customElementsName = `${id}_${customElementsVersions[id]}`;

const Story = class Story extends React.Component {
wrapperRef: React.RefObject<unknown>;

elementName: string;

constructor(props: any) {
super(props);
this.wrapperRef = React.createRef();
}

async componentDidMount() {
// eslint-disable-next-line no-undef
customElements.define(
customElementsName,
await new ElementRendererService().renderAngularElement({
storyFnAngular: storyFn(),
parameters,
})
);
}

render() {
return React.createElement(customElementsName, {
ref: this.wrapperRef,
});
}
};
return React.createElement(Story);
};
1 change: 1 addition & 0 deletions app/angular/element-renderer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dist/ts3.9/element-renderer.d';
1 change: 1 addition & 0 deletions app/angular/element-renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/ts3.9/element-renderer');
10 changes: 10 additions & 0 deletions app/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@
"@angular/compiler": "^11.1.0",
"@angular/compiler-cli": "^11.1.0",
"@angular/core": "^11.1.0",
"@angular/elements": "^11.1.0",
"@angular/forms": "^11.1.0",
"@angular/platform-browser": "^11.1.0",
"@angular/platform-browser-dynamic": "^11.1.0",
"@nrwl/workspace": "^11.1.5",
"@types/autoprefixer": "^9.7.2",
"@types/jest": "^25.2.3",
"@webcomponents/custom-elements": "^1.4.3",
"jest": "^26.6.3",
"jest-preset-angular": "^8.3.2",
"ts-jest": "^26.4.4"
Expand All @@ -88,18 +90,26 @@
"@angular/compiler": ">=6.0.0",
"@angular/compiler-cli": ">=6.0.0",
"@angular/core": ">=6.0.0",
"@angular/elements": ">=6.0.0",
"@angular/forms": ">=6.0.0",
"@angular/platform-browser": ">=6.0.0",
"@angular/platform-browser-dynamic": ">=6.0.0",
"@babel/core": "*",
"@nrwl/workspace": ">=11.1.0",
"@webcomponents/custom-elements": ">=1.4.3",
"rxjs": "^6.0.0",
"typescript": "^3.4.0 || >=4.0.0",
"zone.js": "^0.8.29 || ^0.9.0 || ^0.10.0 || ^0.11.0"
},
"peerDependenciesMeta": {
"@angular/elements": {
"optional": true
},
"@nrwl/workspace": {
"optional": true
},
"@webcomponents/custom-elements": {
"optional": true
}
},
"engines": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Should be added first :
// Custom Elements polyfill. Required for browsers that do not natively support Custom Elements.
import '@webcomponents/custom-elements';
// Custom Elements ES5 shim. Required when using ES5 bundles on browsers that natively support
// Custom Elements (either because the browser does not support ES2015 modules or because the app
// is explicitly configured to generate ES5 only bundles).
import '@webcomponents/custom-elements/src/native-shim';

import { Injector, NgModule, Type } from '@angular/core';
import { createCustomElement, NgElementConstructor } from '@angular/elements';

import { BehaviorSubject } from 'rxjs';
import { ICollection, StoryFnAngularReturnType } from '../types';
import { Parameters } from '../types-6-0';
import { getStorybookModuleMetadata } from './StorybookModule';
import { RendererService } from './RendererService';

/**
* Bootstrap angular application to generate a web component with angular element
*/
export class ElementRendererService {
private platform = RendererService.getInstance().platform;

/**
* Returns a custom element generated by Angular elements
*/
public async renderAngularElement({
storyFnAngular,
parameters,
}: {
storyFnAngular: StoryFnAngularReturnType;
parameters: Parameters;
}): Promise<CustomElementConstructor> {
const ngModule = getStorybookModuleMetadata(
{ storyFnAngular, parameters },
new BehaviorSubject<ICollection>(storyFnAngular.props)
);

return this.platform
.bootstrapModule(createElementsModule(ngModule))
.then((m) => m.instance.ngEl);
}
}

const createElementsModule = (ngModule: NgModule): Type<{ ngEl: CustomElementConstructor }> => {
@NgModule({ ...ngModule })
class ElementsModule {
public ngEl: NgElementConstructor<unknown>;

constructor(private injector: Injector) {
this.ngEl = createCustomElement(ngModule.bootstrap[0] as Type<unknown>, {
injector: this.injector,
});
}

ngDoBootstrap() {}
}
return ElementsModule;
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class RendererService {
return RendererService.instance;
}

private platform: PlatformRef;
public platform: PlatformRef;

private staticRoot = document.getElementById('root');

Expand All @@ -43,6 +43,7 @@ export class RendererService {
}
// platform should be set after enableProdMode()
this.platform = platformBrowserDynamic();
this.initAngularBootstrapElement();
}

/**
Expand Down Expand Up @@ -83,7 +84,7 @@ export class RendererService {
);
}

initAngularBootstrapElement() {
private initAngularBootstrapElement() {
// Adds DOM element that angular will use as bootstrap component
const storybookWrapperElement = document.createElement(
RendererService.SELECTOR_STORYBOOK_WRAPPER
Expand Down
5 changes: 4 additions & 1 deletion app/angular/src/client/preview/types-6-0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import {
BaseMeta,
BaseStory,
Parameters as DefaultParameters,
StoryContext as DefaultStoryContext,
} from '@storybook/addons';
import { StoryFnAngularReturnType } from './types';

export { Args, ArgTypes, StoryContext } from '@storybook/addons';
export { Args, ArgTypes } from '@storybook/addons';

type AngularComponent = any;
type AngularReturnType = StoryFnAngularReturnType;
Expand All @@ -33,3 +34,5 @@ export type Parameters = DefaultParameters & {
angularLegacyRendering?: boolean;
component: unknown;
};

export type StoryContext = DefaultStoryContext & { parameters: Parameters };
1 change: 1 addition & 0 deletions app/angular/src/element-renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ElementRendererService } from './client/preview/angular-beta/ElementRendererService';
1 change: 1 addition & 0 deletions examples/angular-cli/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ module.exports = {
'@storybook/addon-jest',
'@storybook/addon-backgrounds',
'@storybook/addon-a11y',
'@storybook/addon-toolbars',
],
};
22 changes: 19 additions & 3 deletions examples/angular-cli/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { addParameters, addDecorator } from '@storybook/angular';
import { addParameters } from '@storybook/angular';
import { setCompodocJson } from '@storybook/addon-docs/angular';
import { prepareForInline } from '@storybook/addon-docs/angular/inline';
import addCssWarning from '../src/cssWarning';

// @ts-ignore
Expand All @@ -18,7 +19,22 @@ addCssWarning();

addParameters({
docs: {
// inlineStories: true,
iframeHeight: '60px',
inlineStories: true,
prepareForInline,
},
});

export const globalTypes = {
theme: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'light',
toolbar: {
icon: 'paintbrush',
items: [
{ value: 'light', title: 'Light theme' },
{ value: 'dark', title: 'Dark theme' },
],
},
},
};
2 changes: 2 additions & 0 deletions examples/angular-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@angular-devkit/core": "^11.1.0",
"@angular/cli": "^11.1.0",
"@angular/compiler-cli": "^11.1.0",
"@angular/elements": "^11.1.0",
"@compodoc/compodoc": "^1.1.11",
"@storybook/addon-a11y": "6.2.0-alpha.18",
"@storybook/addon-actions": "6.2.0-alpha.18",
Expand All @@ -54,6 +55,7 @@
"@types/jest": "^25.2.3",
"@types/node": "^14.14.20",
"@types/webpack-env": "^1.16.0",
"@webcomponents/custom-elements": "^1.4.3",
"babel-plugin-require-context-hook": "^1.0.0",
"global": "^4.4.0",
"jasmine-core": "~3.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
exports[`Storyshots Addon/Links button with link to another story 1`] = `
<storybook-wrapper>
<storybook-button-component
_nghost-a-c8=""
_nghost-a-c6=""
ng-reflect-text="Go to Welcome Story"
>
<button
_ngcontent-a-c8=""
_ngcontent-a-c6=""
>
Go to Welcome Story
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,37 @@
exports[`Storyshots Core/Parameters passed to story 1`] = `
<storybook-wrapper>
<storybook-button-component
_nghost-a-c9=""
_nghost-a-c11=""
ng-reflect-text="Parameters are {
\\"docs\\": {
"
>
<button
_ngcontent-a-c9=""
_ngcontent-a-c11=""
>
Parameters are {
"docs": {
"iframeHeight": "60px"
"inlineStories": true
},
"globalTypes": {
"theme": {
"name": "Theme",
"description": "Global theme for components",
"defaultValue": "light",
"toolbar": {
"icon": "paintbrush",
"items": [
{
"value": "light",
"title": "Light theme"
},
{
"value": "dark",
"title": "Dark theme"
}
]
}
}
},
"globalParameter": "globalParameter",
"framework": "angular",
Expand Down
Loading

0 comments on commit 8958e84

Please sign in to comment.