From 59c7bc8ed8f679a2e38075ff422a8aeae046688c Mon Sep 17 00:00:00 2001 From: ThibaudAv Date: Wed, 17 Feb 2021 12:26:05 +0100 Subject: [PATCH 1/2] feat(angular): handle angular components without selector --- .../ComputesTemplateFromComponent.test.ts | 19 +++ .../ComputesTemplateFromComponent.ts | 11 ++ .../angular-beta/StorybookModule.test.ts | 37 ++++- .../angular-beta/StorybookWrapperComponent.ts | 3 + .../without-selector.stories.storyshot | 49 +++++++ .../without-selector.stories.ts | 131 ++++++++++++++++++ 6 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts diff --git a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts index 14c518407a9a..27eab0b03bc2 100644 --- a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts +++ b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts @@ -1,3 +1,4 @@ +import { Component } from '@angular/core'; import { ArgTypes } from '@storybook/api'; import { computesTemplateSourceFromComponent } from './ComputesTemplateFromComponent'; import { ButtonAccent, InputComponent, ISomeInterface } from './__testfixtures__/input.component'; @@ -10,6 +11,24 @@ describe('angular source decorator', () => { const source = computesTemplateSourceFromComponent(component, props, argTypes); expect(source).toEqual(''); }); + + describe('with component without selector', () => { + @Component({ + template: `The content`, + }) + class WithoutSelectorComponent {} + + it('should add component ng-container', async () => { + const component = WithoutSelectorComponent; + const props = {}; + const argTypes: ArgTypes = {}; + const source = computesTemplateSourceFromComponent(component, props, argTypes); + expect(source).toEqual( + `` + ); + }); + }); + describe('no argTypes', () => { it('should generate tag-only template with no props', () => { const component = InputComponent; diff --git a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts index 11cd6660a8cb..d1379d402132 100644 --- a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts +++ b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts @@ -39,6 +39,11 @@ export const computesTemplateFromComponent = ( const ngComponentMetadata = getComponentDecoratorMetadata(component); const ngComponentInputsOutputs = getComponentInputsOutputs(component); + if (!ngComponentMetadata.selector) { + // Allow to add renderer component when NgComponent selector is undefined + return ``; + } + const { inputs: initialInputs, outputs: initialOutputs } = separateInputsOutputsAttributes( ngComponentInputsOutputs, initialProps @@ -93,6 +98,12 @@ export const computesTemplateSourceFromComponent = ( if (!ngComponentMetadata) { return null; } + + if (!ngComponentMetadata.selector) { + // Allow to add renderer component when NgComponent selector is undefined + return ``; + } + const ngComponentInputsOutputs = getComponentInputsOutputs(component); const { inputs: initialInputs, outputs: initialOutputs } = separateInputsOutputsAttributes( ngComponentInputsOutputs, diff --git a/app/angular/src/client/preview/angular-beta/StorybookModule.test.ts b/app/angular/src/client/preview/angular-beta/StorybookModule.test.ts index 900f2d1b7a4c..d5e134f2df3a 100644 --- a/app/angular/src/client/preview/angular-beta/StorybookModule.test.ts +++ b/app/angular/src/client/preview/angular-beta/StorybookModule.test.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, Input, NgModule, Output, Type } from '@angular/core'; import { TestBed } from '@angular/core/testing'; +import { BrowserModule } from '@angular/platform-browser'; import { BehaviorSubject } from 'rxjs'; import { ICollection } from '../types'; import { getStorybookModuleMetadata } from './StorybookModule'; @@ -208,13 +209,47 @@ describe('StorybookModule', () => { expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(newProps.input); }); }); + + describe('with component without selector', () => { + @Component({ + template: `The content`, + }) + class WithoutSelectorComponent {} + + it('should display the component', async () => { + const props = {}; + + const ngModule = getStorybookModuleMetadata( + { + storyFnAngular: { + props, + moduleMetadata: { entryComponents: [WithoutSelectorComponent] }, + }, + parameters: { component: WithoutSelectorComponent }, + }, + new BehaviorSubject(props) + ); + + const { fixture } = await configureTestingModule(ngModule); + fixture.detectChanges(); + + expect(fixture.nativeElement.innerHTML).toContain('The content'); + }); + }); }); async function configureTestingModule(ngModule: NgModule) { await TestBed.configureTestingModule({ declarations: ngModule.declarations, providers: ngModule.providers, - }).compileComponents(); + }) + .overrideModule(BrowserModule, { + set: { + entryComponents: [...ngModule.entryComponents], + }, + }) + .compileComponents(); + const fixture = TestBed.createComponent(ngModule.bootstrap[0] as Type); return { diff --git a/app/angular/src/client/preview/angular-beta/StorybookWrapperComponent.ts b/app/angular/src/client/preview/angular-beta/StorybookWrapperComponent.ts index 0e3d850ea4f6..d4784a95c901 100644 --- a/app/angular/src/client/preview/angular-beta/StorybookWrapperComponent.ts +++ b/app/angular/src/client/preview/angular-beta/StorybookWrapperComponent.ts @@ -57,6 +57,9 @@ export const createStorybookWrapperComponent = ( @ViewChild(storyComponent ?? '', { read: ViewContainerRef, static: true }) storyComponentViewContainerRef: ViewContainerRef; + // Used in case of a component without selector + storyComponent = storyComponent ?? ''; + // eslint-disable-next-line no-useless-constructor constructor( @Inject(STORY_PROPS) private storyProps$: Subject, diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot new file mode 100644 index 000000000000..bd223dbc85f5 --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots Basics / Component / without selector Simple Component 1`] = ` + + + My name in color : +
+ Joe Bar +
+
+
+`; + +exports[`Storyshots Basics / Component / without selector With Custom Ng Component Outlet Wrapper 1`] = ` + + + + + My name in color : +
+ Dixie Normous +
+ Ng-content : Inspired by + https://angular.io/api/common/NgComponentOutlet +
+
+
+`; + +exports[`Storyshots Basics / Component / without selector With Injection Token And Args 1`] = ` + + + My name in color : +
+ Dixie Normous +
+
+
+`; diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts new file mode 100644 index 000000000000..638e7711e863 --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts @@ -0,0 +1,131 @@ +import { + Component, + Inject, + InjectionToken, + Injector, + Input, + OnInit, + Optional, + Type, +} from '@angular/core'; +import { componentWrapperDecorator, moduleMetadata, Story, Meta } from '@storybook/angular'; + +const WITHOUT_SELECTOR_DATA = new InjectionToken<{ color: string; name: string }>( + 'WITHOUT_SELECTOR_DATA' +); + +@Component({ + template: `My name in color : +
{{ name }}
+ `, +}) +class WithoutSelectorComponent { + color = '#1e88e5'; + + name = 'Joe Bar'; + + constructor( + @Inject(WITHOUT_SELECTOR_DATA) + @Optional() + data: { + color: string; + name: string; + } | null + ) { + if (data) { + this.color = data.color; + this.name = data.name; + } + } +} + +export default { + title: 'Basics / Component / without selector', + component: WithoutSelectorComponent, + decorators: [ + moduleMetadata({ + entryComponents: [WithoutSelectorComponent], + }), + ], +} as Meta; + +export const SimpleComponent: Story = () => ({}); + +// Live changing of args by controls does not work for now. When changing args storybook does not fully +// reload and therefore does not take into account the change of provider. +export const WithInjectionTokenAndArgs: Story = (args) => ({ + props: args, + moduleMetadata: { + providers: [ + { provide: WITHOUT_SELECTOR_DATA, useValue: { color: args.color, name: args.name } }, + ], + }, +}); +WithInjectionTokenAndArgs.argTypes = { + name: { control: 'text' }, + color: { control: 'color' }, +}; +WithInjectionTokenAndArgs.args = { name: 'Dixie Normous', color: 'red' }; + +// Advanced example with custom *ngComponentOutlet + +@Component({ + selector: 'without-selector-wrapper', + template: ``, +}) +class WithoutSelectorWrapperComponent implements OnInit { + @Input() + componentOutlet: Type; + + @Input() + name: string; + + @Input() + color: string; + + componentInjector: Injector; + + componentContent = [ + // eslint-disable-next-line no-undef + [document.createTextNode('Ng-content : Inspired by ')], + // eslint-disable-next-line no-undef + [document.createTextNode('https://angular.io/api/common/NgComponentOutlet')], + ]; + + // eslint-disable-next-line no-useless-constructor + constructor(private readonly injector: Injector) {} + + ngOnInit(): void { + console.log({ color: this.color, name: this.name }); + + this.componentInjector = Injector.create({ + providers: [ + { provide: WITHOUT_SELECTOR_DATA, useValue: { color: this.color, name: this.name } }, + ], + parent: this.injector, + }); + } +} + +// Live changing of args by controls does not work at the moment. When changing args storybook does not fully +// reload and therefore does not take into account the change of provider. +export const WithCustomNgComponentOutletWrapper: Story = (args) => ({ + props: args, +}); +WithCustomNgComponentOutletWrapper.argTypes = { + name: { control: 'text' }, + color: { control: 'color' }, +}; +WithCustomNgComponentOutletWrapper.args = { name: 'Dixie Normous', color: 'green' }; +WithCustomNgComponentOutletWrapper.decorators = [ + moduleMetadata({ + declarations: [WithoutSelectorWrapperComponent], + }), + componentWrapperDecorator(WithoutSelectorWrapperComponent, (args) => ({ + name: args.name, + color: args.color, + componentOutlet: WithoutSelectorComponent, + })), +]; From 703187f0d3bfa21bc89a67292e8e0b600e403ed2 Mon Sep 17 00:00:00 2001 From: ThibaudAv Date: Thu, 18 Feb 2021 19:16:30 +0100 Subject: [PATCH 2/2] fixup! feat(angular): handle angular components without selector --- ...ctor-ng-component-outlet.stories.storyshot | 23 ++++ ...ctor-ng-factory-resolver.stories.storyshot | 17 +++ .../without-selector.stories.storyshot | 22 ---- ...ut-selector-ng-component-outlet.stories.ts | 75 +++++++++++++ ...ut-selector-ng-factory-resolver.stories.ts | 72 ++++++++++++ .../without-selector.component.ts | 30 +++++ .../without-selector.stories.ts | 105 +----------------- 7 files changed, 219 insertions(+), 125 deletions(-) create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-component-outlet.stories.storyshot create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-factory-resolver.stories.storyshot create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-component-outlet.stories.ts create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-factory-resolver.stories.ts create mode 100644 examples/angular-cli/src/stories/basics/component-without-selector/without-selector.component.ts diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-component-outlet.stories.storyshot b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-component-outlet.stories.storyshot new file mode 100644 index 000000000000..66a392f0cbb4 --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-component-outlet.stories.storyshot @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots Basics / Component / without selector / Custom wrapper *NgComponentOutlet Custom wrapper *NgComponentOutlet 1`] = ` + + + + + My name in color : +
+ Dixie Normous +
+ Ng-content : Inspired by + https://angular.io/api/common/NgComponentOutlet +
+
+
+`; diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-factory-resolver.stories.storyshot b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-factory-resolver.stories.storyshot new file mode 100644 index 000000000000..9bd911768d2c --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector-ng-factory-resolver.stories.storyshot @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots Basics / Component / without selector / Custom wrapper ComponentFactoryResolver Custom wrapper ComponentFactoryResolver 1`] = ` + + + My name in color : +
+ Dixie Normous +
+
+
+`; diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot index bd223dbc85f5..cc9d114e8379 100644 --- a/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot +++ b/examples/angular-cli/src/stories/basics/component-without-selector/__snapshots__/without-selector.stories.storyshot @@ -13,28 +13,6 @@ exports[`Storyshots Basics / Component / without selector Simple Component 1`] = `; -exports[`Storyshots Basics / Component / without selector With Custom Ng Component Outlet Wrapper 1`] = ` - - - - - My name in color : -
- Dixie Normous -
- Ng-content : Inspired by - https://angular.io/api/common/NgComponentOutlet -
-
-
-`; - exports[`Storyshots Basics / Component / without selector With Injection Token And Args 1`] = ` diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-component-outlet.stories.ts b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-component-outlet.stories.ts new file mode 100644 index 000000000000..474d50b36ff1 --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-component-outlet.stories.ts @@ -0,0 +1,75 @@ +import { Component, Injector, Input, OnInit, Type } from '@angular/core'; +import { componentWrapperDecorator, moduleMetadata, Story, Meta } from '@storybook/angular'; +import { WithoutSelectorComponent, WITHOUT_SELECTOR_DATA } from './without-selector.component'; + +export default { + title: 'Basics / Component / without selector / Custom wrapper *NgComponentOutlet', + component: WithoutSelectorComponent, + decorators: [ + moduleMetadata({ + entryComponents: [WithoutSelectorComponent], + }), + ], +} as Meta; + +// Advanced example with custom *ngComponentOutlet + +@Component({ + selector: 'ng-component-outlet-wrapper', + template: ``, +}) +class NgComponentOutletWrapperComponent implements OnInit { + @Input() + componentOutlet: Type; + + @Input() + name: string; + + @Input() + color: string; + + componentInjector: Injector; + + componentContent = [ + // eslint-disable-next-line no-undef + [document.createTextNode('Ng-content : Inspired by ')], + // eslint-disable-next-line no-undef + [document.createTextNode('https://angular.io/api/common/NgComponentOutlet')], + ]; + + // eslint-disable-next-line no-useless-constructor + constructor(private readonly injector: Injector) {} + + ngOnInit(): void { + this.componentInjector = Injector.create({ + providers: [ + { provide: WITHOUT_SELECTOR_DATA, useValue: { color: this.color, name: this.name } }, + ], + parent: this.injector, + }); + } +} + +// Live changing of args by controls does not work at the moment. When changing args storybook does not fully +// reload and therefore does not take into account the change of provider. +export const WithCustomNgComponentOutletWrapper: Story = (args) => ({ + props: args, +}); +WithCustomNgComponentOutletWrapper.storyName = 'Custom wrapper *NgComponentOutlet'; +WithCustomNgComponentOutletWrapper.argTypes = { + name: { control: 'text' }, + color: { control: 'color' }, +}; +WithCustomNgComponentOutletWrapper.args = { name: 'Dixie Normous', color: 'green' }; +WithCustomNgComponentOutletWrapper.decorators = [ + moduleMetadata({ + declarations: [NgComponentOutletWrapperComponent], + }), + componentWrapperDecorator(NgComponentOutletWrapperComponent, (args) => ({ + name: args.name, + color: args.color, + componentOutlet: WithoutSelectorComponent, + })), +]; diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-factory-resolver.stories.ts b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-factory-resolver.stories.ts new file mode 100644 index 000000000000..e583d2735651 --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector-ng-factory-resolver.stories.ts @@ -0,0 +1,72 @@ +import { + AfterViewInit, + Component, + ComponentFactoryResolver, + Input, + Type, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { componentWrapperDecorator, moduleMetadata, Story, Meta } from '@storybook/angular'; + +import { WithoutSelectorComponent } from './without-selector.component'; + +export default { + title: 'Basics / Component / without selector / Custom wrapper ComponentFactoryResolver', + component: WithoutSelectorComponent, + decorators: [ + moduleMetadata({ + entryComponents: [WithoutSelectorComponent], + }), + ], +} as Meta; + +// Advanced example with custom ComponentFactoryResolver + +@Component({ selector: 'component-factory-wrapper', template: '' }) +class ComponentFactoryWrapperComponent implements AfterViewInit { + @ViewChild('dynamicInsert', { read: ViewContainerRef }) dynamicInsert; + + @Input() + componentOutlet: Type; + + @Input() + args: any; + + // eslint-disable-next-line no-useless-constructor + constructor( + private viewContainerRef: ViewContainerRef, + private componentFactoryResolver: ComponentFactoryResolver + ) {} + + ngAfterViewInit() { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory( + this.componentOutlet + ); + const containerRef = this.viewContainerRef; + containerRef.clear(); + const dynamicComponent = containerRef.createComponent(componentFactory); + Object.assign(dynamicComponent.instance, this.args); + } +} + +// Live changing of args by controls does not work at the moment. When changing args storybook does not fully +// reload and therefore does not take into account the change of provider. +export const WithComponentFactoryResolver: Story = (args) => ({ + props: args, +}); +WithComponentFactoryResolver.storyName = 'Custom wrapper ComponentFactoryResolver'; +WithComponentFactoryResolver.argTypes = { + name: { control: 'text' }, + color: { control: 'color' }, +}; +WithComponentFactoryResolver.args = { name: 'Dixie Normous', color: 'chartreuse' }; +WithComponentFactoryResolver.decorators = [ + moduleMetadata({ + declarations: [ComponentFactoryWrapperComponent], + }), + componentWrapperDecorator(ComponentFactoryWrapperComponent, ({ args }) => ({ + args, + componentOutlet: WithoutSelectorComponent, + })), +]; diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.component.ts b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.component.ts new file mode 100644 index 000000000000..d644c76b69f8 --- /dev/null +++ b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.component.ts @@ -0,0 +1,30 @@ +import { Component, Inject, InjectionToken, Optional } from '@angular/core'; + +export const WITHOUT_SELECTOR_DATA = new InjectionToken<{ color: string; name: string }>( + 'WITHOUT_SELECTOR_DATA' +); + +@Component({ + template: `My name in color : +
{{ name }}
+ `, +}) +export class WithoutSelectorComponent { + color = '#1e88e5'; + + name = 'Joe Bar'; + + constructor( + @Inject(WITHOUT_SELECTOR_DATA) + @Optional() + data: { + color: string; + name: string; + } | null + ) { + if (data) { + this.color = data.color; + this.name = data.name; + } + } +} diff --git a/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts index 638e7711e863..148920200322 100644 --- a/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts +++ b/examples/angular-cli/src/stories/basics/component-without-selector/without-selector.stories.ts @@ -1,43 +1,5 @@ -import { - Component, - Inject, - InjectionToken, - Injector, - Input, - OnInit, - Optional, - Type, -} from '@angular/core'; -import { componentWrapperDecorator, moduleMetadata, Story, Meta } from '@storybook/angular'; - -const WITHOUT_SELECTOR_DATA = new InjectionToken<{ color: string; name: string }>( - 'WITHOUT_SELECTOR_DATA' -); - -@Component({ - template: `My name in color : -
{{ name }}
- `, -}) -class WithoutSelectorComponent { - color = '#1e88e5'; - - name = 'Joe Bar'; - - constructor( - @Inject(WITHOUT_SELECTOR_DATA) - @Optional() - data: { - color: string; - name: string; - } | null - ) { - if (data) { - this.color = data.color; - this.name = data.name; - } - } -} +import { moduleMetadata, Story, Meta } from '@storybook/angular'; +import { WithoutSelectorComponent, WITHOUT_SELECTOR_DATA } from './without-selector.component'; export default { title: 'Basics / Component / without selector', @@ -66,66 +28,3 @@ WithInjectionTokenAndArgs.argTypes = { color: { control: 'color' }, }; WithInjectionTokenAndArgs.args = { name: 'Dixie Normous', color: 'red' }; - -// Advanced example with custom *ngComponentOutlet - -@Component({ - selector: 'without-selector-wrapper', - template: ``, -}) -class WithoutSelectorWrapperComponent implements OnInit { - @Input() - componentOutlet: Type; - - @Input() - name: string; - - @Input() - color: string; - - componentInjector: Injector; - - componentContent = [ - // eslint-disable-next-line no-undef - [document.createTextNode('Ng-content : Inspired by ')], - // eslint-disable-next-line no-undef - [document.createTextNode('https://angular.io/api/common/NgComponentOutlet')], - ]; - - // eslint-disable-next-line no-useless-constructor - constructor(private readonly injector: Injector) {} - - ngOnInit(): void { - console.log({ color: this.color, name: this.name }); - - this.componentInjector = Injector.create({ - providers: [ - { provide: WITHOUT_SELECTOR_DATA, useValue: { color: this.color, name: this.name } }, - ], - parent: this.injector, - }); - } -} - -// Live changing of args by controls does not work at the moment. When changing args storybook does not fully -// reload and therefore does not take into account the change of provider. -export const WithCustomNgComponentOutletWrapper: Story = (args) => ({ - props: args, -}); -WithCustomNgComponentOutletWrapper.argTypes = { - name: { control: 'text' }, - color: { control: 'color' }, -}; -WithCustomNgComponentOutletWrapper.args = { name: 'Dixie Normous', color: 'green' }; -WithCustomNgComponentOutletWrapper.decorators = [ - moduleMetadata({ - declarations: [WithoutSelectorWrapperComponent], - }), - componentWrapperDecorator(WithoutSelectorWrapperComponent, (args) => ({ - name: args.name, - color: args.color, - componentOutlet: WithoutSelectorComponent, - })), -];