UNPKG

@storybook/angular

Version:

Storybook for Angular: Develop Angular components in isolation with hot reloading.

124 lines (123 loc) 6.64 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { ChangeDetectorRef, Component, ElementRef, Inject, NgModule, ViewChild, ViewContainerRef, } from '@angular/core'; import { Subject } from 'rxjs'; import { map, skip } from 'rxjs/operators'; import { STORY_PROPS } from './StorybookProvider'; import { getComponentInputsOutputs } from './utils/NgComponentAnalyzer'; import { PropertyExtractor } from './utils/PropertyExtractor'; const getNonInputsOutputsProps = (ngComponentInputsOutputs, props = {}) => { const inputs = ngComponentInputsOutputs.inputs .filter((i) => i.templateName in props) .map((i) => i.templateName); const outputs = ngComponentInputsOutputs.outputs .filter((o) => o.templateName in props) .map((o) => o.templateName); return Object.keys(props).filter((k) => ![...inputs, ...outputs].includes(k)); }; /** Wraps the story template into a component */ export const createStorybookWrapperComponent = ({ selector, template, storyComponent, styles, moduleMetadata, initialProps, analyzedMetadata, }) => { // In ivy, a '' selector is not allowed, therefore we need to just set it to anything if // storyComponent was not provided. const viewChildSelector = storyComponent ?? '__storybook-noop'; const { imports, declarations, providers } = analyzedMetadata; let StorybookComponentModule = class StorybookComponentModule { }; StorybookComponentModule = __decorate([ NgModule({ declarations, imports, exports: [...declarations, ...imports], }) ], StorybookComponentModule); PropertyExtractor.warnImportsModuleWithProviders(analyzedMetadata); let StorybookWrapperComponent = class StorybookWrapperComponent { constructor(storyProps$, changeDetectorRef) { this.storyProps$ = storyProps$; this.changeDetectorRef = changeDetectorRef; // Used in case of a component without selector this.storyComponent = storyComponent ?? ''; } ngOnInit() { // Subscribes to the observable storyProps$ to keep these properties up to date this.storyWrapperPropsSubscription = this.storyProps$.subscribe((storyProps = {}) => { // All props are added as component properties Object.assign(this, storyProps); this.changeDetectorRef.detectChanges(); this.changeDetectorRef.markForCheck(); }); } ngAfterViewInit() { // Bind properties to component, if the story have component if (this.storyComponentElementRef) { const ngComponentInputsOutputs = getComponentInputsOutputs(storyComponent); const initialOtherProps = getNonInputsOutputsProps(ngComponentInputsOutputs, initialProps); // Initializes properties that are not Inputs | Outputs // Allows story props to override local component properties initialOtherProps.forEach((p) => { this.storyComponentElementRef[p] = initialProps[p]; }); // `markForCheck` the component in case this uses changeDetection: OnPush // And then forces the `detectChanges` this.storyComponentViewContainerRef.injector.get(ChangeDetectorRef).markForCheck(); this.changeDetectorRef.detectChanges(); // Once target component has been initialized, the storyProps$ observable keeps target component properties than are not Input|Output up to date this.storyComponentPropsSubscription = this.storyProps$ .pipe(skip(1), map((props) => { const propsKeyToKeep = getNonInputsOutputsProps(ngComponentInputsOutputs, props); return propsKeyToKeep.reduce((acc, p) => ({ ...acc, [p]: props[p] }), {}); })) .subscribe((props) => { // Replace inputs with new ones from props Object.assign(this.storyComponentElementRef, props); // `markForCheck` the component in case this uses changeDetection: OnPush // And then forces the `detectChanges` this.storyComponentViewContainerRef.injector.get(ChangeDetectorRef).markForCheck(); this.changeDetectorRef.detectChanges(); }); } } ngOnDestroy() { if (this.storyComponentPropsSubscription != null) { this.storyComponentPropsSubscription.unsubscribe(); } if (this.storyWrapperPropsSubscription != null) { this.storyWrapperPropsSubscription.unsubscribe(); } } }; __decorate([ ViewChild(viewChildSelector, { static: true }), __metadata("design:type", ElementRef) ], StorybookWrapperComponent.prototype, "storyComponentElementRef", void 0); __decorate([ ViewChild(viewChildSelector, { read: ViewContainerRef, static: true }), __metadata("design:type", ViewContainerRef) ], StorybookWrapperComponent.prototype, "storyComponentViewContainerRef", void 0); StorybookWrapperComponent = __decorate([ Component({ selector, template, standalone: true, imports: [StorybookComponentModule], providers, styles, schemas: moduleMetadata.schemas, }), __param(0, Inject(STORY_PROPS)), __param(1, Inject(ChangeDetectorRef)), __metadata("design:paramtypes", [Subject, ChangeDetectorRef]) ], StorybookWrapperComponent); return StorybookWrapperComponent; };