Skip to content

Commit

Permalink
Merge pull request #17156 from storybookjs/angular/fix-angular-13.1
Browse files Browse the repository at this point in the history
Angular: Use ɵReflectionCapabilities to find component & module metadata
  • Loading branch information
shilman committed Jan 29, 2022
1 parent fc437ad commit b1c8a5b
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 76 deletions.
6 changes: 0 additions & 6 deletions addons/docs/angular/README.md
Expand Up @@ -210,12 +210,6 @@ And for `MDX` you can modify it as an attribute on the `Story` element:

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
Expand Down
10 changes: 0 additions & 10 deletions app/angular/package.json
Expand Up @@ -85,14 +85,12 @@
"@angular/compiler": "^11.2.14",
"@angular/compiler-cli": "^11.2.14",
"@angular/core": "^11.2.14",
"@angular/elements": "^11.2.14",
"@angular/forms": "^11.2.14",
"@angular/platform-browser": "^11.2.14",
"@angular/platform-browser-dynamic": "^11.2.14",
"@nrwl/workspace": "^11.6.3",
"@types/autoprefixer": "^9.7.2",
"@types/jest": "^26.0.16",
"@webcomponents/custom-elements": "^1.4.3",
"jest": "^26.6.3",
"jest-preset-angular": "^8.3.2",
"ts-jest": "^26.4.4"
Expand All @@ -106,13 +104,11 @@
"@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 || ^7.4.0",
"typescript": "^3.4.0 || >=4.0.0",
"zone.js": "^0.8.29 || ^0.9.0 || ^0.10.0 || ^0.11.0"
Expand All @@ -121,14 +117,8 @@
"@angular/cli": {
"optional": true
},
"@angular/elements": {
"optional": true
},
"@nrwl/workspace": {
"optional": true
},
"@webcomponents/custom-elements": {
"optional": true
}
},
"engines": {
Expand Down
Expand Up @@ -137,6 +137,46 @@ describe('getComponentInputsOutputs', () => {
expect(sortByPropName(inputs)).toEqual(sortByPropName(fooComponentFactory.inputs));
expect(sortByPropName(outputs)).toEqual(sortByPropName(fooComponentFactory.outputs));
});

it('should return I/O with extending classes', () => {
@Component({
template: '',
})
class BarComponent {
@Input()
public a: string;

@Input()
public b: string;
}

@Component({
template: '',
})
class FooComponent extends BarComponent {
@Input()
public b: string;

@Input()
public c: string;
}

const fooComponentFactory = resolveComponentFactory(FooComponent);

const { inputs, outputs } = getComponentInputsOutputs(FooComponent);

expect({ inputs, outputs }).toEqual({
inputs: [
{ propName: 'a', templateName: 'a' },
{ propName: 'b', templateName: 'b' },
{ propName: 'c', templateName: 'c' },
],
outputs: [],
});

expect(sortByPropName(inputs)).toEqual(sortByPropName(fooComponentFactory.inputs));
expect(sortByPropName(outputs)).toEqual(sortByPropName(fooComponentFactory.outputs));
});
});

describe('isDeclarable', () => {
Expand Down Expand Up @@ -197,10 +237,27 @@ describe('isComponent', () => {

describe('getComponentDecoratorMetadata', () => {
it('should return Component with a Component', () => {
@Component({})
@Component({ selector: 'foo' })
class FooComponent {}

expect(getComponentDecoratorMetadata(FooComponent)).toBeInstanceOf(Component);
expect(getComponentDecoratorMetadata(FooComponent)).toEqual({
changeDetection: 1,
selector: 'foo',
});
});

it('should return Component with extending classes', () => {
@Component({ selector: 'bar' })
class BarComponent {}
@Component({ selector: 'foo' })
class FooComponent extends BarComponent {}

expect(getComponentDecoratorMetadata(FooComponent)).toBeInstanceOf(Component);
expect(getComponentDecoratorMetadata(FooComponent)).toEqual({
changeDetection: 1,
selector: 'foo',
});
});
});

Expand Down
@@ -1,4 +1,15 @@
import { Component, Directive, Input, Output, Pipe, Type } from '@angular/core';
import {
Component,
Directive,
Input,
Output,
Pipe,
Type,
ɵReflectionCapabilities as ReflectionCapabilities,
ɵCodegenComponentFactoryResolver,
} from '@angular/core';

const reflectionCapabilities = new ReflectionCapabilities();

export type ComponentInputsOutputs = {
inputs: {
Expand Down Expand Up @@ -80,10 +91,7 @@ export const isDeclarable = (component: any): boolean => {
return false;
}

const decoratorKey = '__annotations__';
const decorators: any[] = Reflect.getOwnPropertyDescriptor(component, decoratorKey)
? Reflect.getOwnPropertyDescriptor(component, decoratorKey).value
: component[decoratorKey];
const decorators = reflectionCapabilities.annotations(component);

return !!(decorators || []).find(
(d) => d instanceof Directive || d instanceof Pipe || d instanceof Component
Expand All @@ -95,10 +103,8 @@ export const isComponent = (component: any): component is Type<unknown> => {
return false;
}

const decoratorKey = '__annotations__';
const decorators: any[] = Reflect.getOwnPropertyDescriptor(component, decoratorKey)
? Reflect.getOwnPropertyDescriptor(component, decoratorKey).value
: component[decoratorKey];
const decorators = reflectionCapabilities.annotations(component);

return (decorators || []).some((d) => d instanceof Component);
};

Expand All @@ -107,44 +113,14 @@ export const isComponent = (component: any): component is Type<unknown> => {
* is used to get all `@Input` and `@Output` Decorator
*/
export const getComponentPropsDecoratorMetadata = (component: any) => {
const decoratorKey = '__prop__metadata__';
let propsDecorators: Record<string, (Input | Output)[]> =
Reflect &&
Reflect.getOwnPropertyDescriptor &&
Reflect.getOwnPropertyDescriptor(component, decoratorKey)
? Reflect.getOwnPropertyDescriptor(component, decoratorKey).value
: component[decoratorKey];

const parent = Reflect && Reflect.getPrototypeOf && Reflect.getPrototypeOf(component);

if (parent) {
const parentPropsDecorators = getComponentPropsDecoratorMetadata(parent);
propsDecorators = { ...parentPropsDecorators, ...propsDecorators };
}

return propsDecorators;
return reflectionCapabilities.propMetadata(component);
};

/**
* Returns component decorator `@Component`
*/
export const getComponentDecoratorMetadata = (component: any): Component | undefined => {
const decoratorKey = '__annotations__';
const decorators: any[] =
Reflect &&
Reflect.getOwnPropertyDescriptor &&
Reflect.getOwnPropertyDescriptor(component, decoratorKey)
? Reflect.getOwnPropertyDescriptor(component, decoratorKey).value
: component[decoratorKey];

if (!decorators) {
return (
component.decorators &&
component.decorators[0] &&
component.decorators[0].args &&
component.decorators[0].args[0]
);
}
const decorators = reflectionCapabilities.annotations(component);

return decorators.find((d) => d instanceof Component);
return decorators.reverse().find((d) => d instanceof Component);
};
@@ -1,4 +1,6 @@
import { NgModule } from '@angular/core';
import { NgModule, ɵReflectionCapabilities as ReflectionCapabilities } from '@angular/core';

const reflectionCapabilities = new ReflectionCapabilities();

/**
* Avoid component redeclaration
Expand Down Expand Up @@ -37,13 +39,7 @@ export const isComponentAlreadyDeclaredInModules = (

const extractNgModuleMetadata = (importItem: any): NgModule => {
const target = importItem && importItem.ngModule ? importItem.ngModule : importItem;
const decoratorKey = '__annotations__';
const decorators: any[] =
Reflect &&
Reflect.getOwnPropertyDescriptor &&
Reflect.getOwnPropertyDescriptor(target, decoratorKey)
? Reflect.getOwnPropertyDescriptor(target, decoratorKey).value
: target[decoratorKey];
const decorators = reflectionCapabilities.annotations(target);

if (!decorators || decorators.length === 0) {
return null;
Expand Down
2 changes: 1 addition & 1 deletion lib/cli/src/generators/ANGULAR/index.ts
Expand Up @@ -40,7 +40,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => {
const updatedOptions = isWebpack5 ? { ...options, builder: CoreBuilder.Webpack5 } : options;

baseGenerator(packageManager, npmOptions, updatedOptions, 'angular', {
extraPackages: ['@compodoc/compodoc', '@angular/elements', '@webcomponents/custom-elements'],
extraPackages: ['@compodoc/compodoc'],
addScripts: false,
});
copyTemplate(__dirname);
Expand Down
8 changes: 0 additions & 8 deletions yarn.lock
Expand Up @@ -7959,7 +7959,6 @@ __metadata:
"@angular/compiler": ^11.2.14
"@angular/compiler-cli": ^11.2.14
"@angular/core": ^11.2.14
"@angular/elements": ^11.2.14
"@angular/forms": ^11.2.14
"@angular/platform-browser": ^11.2.14
"@angular/platform-browser-dynamic": ^11.2.14
Expand All @@ -7976,7 +7975,6 @@ __metadata:
"@types/autoprefixer": ^9.7.2
"@types/jest": ^26.0.16
"@types/webpack-env": ^1.16.0
"@webcomponents/custom-elements": ^1.4.3
autoprefixer: ^9.8.6
core-js: ^3.8.2
find-up: ^5.0.0
Expand Down Expand Up @@ -8009,25 +8007,19 @@ __metadata:
"@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 || ^7.4.0
typescript: ^3.4.0 || >=4.0.0
zone.js: ^0.8.29 || ^0.9.0 || ^0.10.0 || ^0.11.0
peerDependenciesMeta:
"@angular/cli":
optional: true
"@angular/elements":
optional: true
"@nrwl/workspace":
optional: true
"@webcomponents/custom-elements":
optional: true
bin:
build-storybook: ./bin/build.js
start-storybook: ./bin/index.js
Expand Down

0 comments on commit b1c8a5b

Please sign in to comment.