Angular: implement dynamic snippet

This commit is contained in:
Yngve Bakken-Nilsen 2021-01-26 16:18:39 +01:00
parent 013f2658e3
commit b4f62da0c4
15 changed files with 592 additions and 93 deletions

View File

@ -33,6 +33,22 @@ Object {
"name": "void", "name": "void",
}, },
}, },
"accent": Object {
"defaultValue": undefined,
"description": "<p>Specify the accent-type of the button </p>
",
"name": "accent",
"table": Object {
"category": "inputs",
"type": Object {
"required": true,
"summary": "ButtonAccent",
},
},
"type": Object {
"name": "object",
},
},
"appearance": Object { "appearance": Object {
"defaultValue": "secondary", "defaultValue": "secondary",
"description": "<p>Appearance style of the button. </p> "description": "<p>Appearance style of the button. </p>
@ -274,6 +290,22 @@ Object {
"name": "object", "name": "object",
}, },
}, },
"someDataObject": Object {
"defaultValue": undefined,
"description": "<p>Specifies some arbitrary object </p>
",
"name": "someDataObject",
"table": Object {
"category": "inputs",
"type": Object {
"required": true,
"summary": "ISomeInterface",
},
},
"type": Object {
"name": "object",
},
},
"somethingYouShouldNotUse": Object { "somethingYouShouldNotUse": Object {
"defaultValue": false, "defaultValue": false,
"description": "<p>Some input you shouldn&#39;t use.</p> "description": "<p>Some input you shouldn&#39;t use.</p>

View File

@ -10,7 +10,7 @@ Object {
"getSignature": Object { "getSignature": Object {
"description": "<p>Getter for <code>inputValue</code>. </p> "description": "<p>Getter for <code>inputValue</code>. </p>
", ",
"line": 102, "line": 115,
"name": "inputValue", "name": "inputValue",
"returnType": "", "returnType": "",
"type": "", "type": "",
@ -34,7 +34,7 @@ Object {
"type": "string", "type": "string",
}, },
], ],
"line": 97, "line": 110,
"name": "inputValue", "name": "inputValue",
"returnType": "void", "returnType": "void",
"type": "void", "type": "void",
@ -58,7 +58,7 @@ Object {
"type": "[]", "type": "[]",
}, },
], ],
"line": 182, "line": 195,
"name": "item", "name": "item",
"returnType": "void", "returnType": "void",
"type": "void", "type": "void",
@ -68,7 +68,7 @@ Object {
"getSignature": Object { "getSignature": Object {
"description": "<p>Get the private value. </p> "description": "<p>Get the private value. </p>
", ",
"line": 141, "line": 154,
"name": "value", "name": "value",
"returnType": "string | number", "returnType": "string | number",
"type": "", "type": "",
@ -92,7 +92,7 @@ Object {
"type": "", "type": "",
}, },
], ],
"line": 136, "line": 149,
"name": "value", "name": "value",
"returnType": "void", "returnType": "void",
"type": "void", "type": "void",
@ -113,7 +113,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"hostBindings": Array [ "hostBindings": Array [
Object { Object {
"defaultValue": "false", "defaultValue": "false",
"line": 111, "line": 124,
"name": "class.focused", "name": "class.focused",
}, },
], ],
@ -128,25 +128,32 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"argsDecorator": Array [ "argsDecorator": Array [
"$event.target", "$event.target",
], ],
"line": 107, "line": 120,
"name": "click", "name": "click",
}, },
], ],
"id": "component-InputComponent-14bbde487c28642f97f1f6c94b65ab31", "id": "component-InputComponent-568feeafa68e593b062061c27c4625a9",
"inputs": Array [], "inputs": Array [],
"inputsClass": Array [ "inputsClass": Array [
Object {
"description": "<p>Specify the accent-type of the button </p>
",
"line": 56,
"name": "accent",
"type": "ButtonAccent",
},
Object { Object {
"defaultValue": "'secondary'", "defaultValue": "'secondary'",
"description": "<p>Appearance style of the button. </p> "description": "<p>Appearance style of the button. </p>
", ",
"line": 46, "line": 52,
"name": "appearance", "name": "appearance",
"type": "\\"primary\\" | \\"secondary\\"", "type": "\\"primary\\" | \\"secondary\\"",
}, },
Object { Object {
"description": "<p>Setter for <code>inputValue</code> that is also an <code>@Input</code>. </p> "description": "<p>Setter for <code>inputValue</code> that is also an <code>@Input</code>. </p>
", ",
"line": 97, "line": 110,
"name": "inputValue", "name": "inputValue",
"type": "string", "type": "string",
}, },
@ -154,23 +161,23 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"defaultValue": "false", "defaultValue": "false",
"description": "<p>Sets the button to a disabled state. </p> "description": "<p>Sets the button to a disabled state. </p>
", ",
"line": 50, "line": 60,
"name": "isDisabled", "name": "isDisabled",
}, },
Object { Object {
"line": 182, "line": 195,
"name": "item", "name": "item",
"type": "[]", "type": "[]",
}, },
Object { Object {
"description": "<p>The inner text of the button.</p> "description": "<p>The inner text of the button.</p>
", ",
"line": 58, "line": 68,
"name": "label", "name": "label",
"type": "string", "type": "string",
}, },
Object { Object {
"line": 179, "line": 192,
"name": "showKeyAlias", "name": "showKeyAlias",
"type": "", "type": "",
}, },
@ -178,93 +185,100 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"defaultValue": "'medium'", "defaultValue": "'medium'",
"description": "<p>Size of the button. </p> "description": "<p>Size of the button. </p>
", ",
"line": 62, "line": 72,
"name": "size", "name": "size",
"type": "ButtonSize", "type": "ButtonSize",
}, },
Object {
"description": "<p>Specifies some arbitrary object </p>
",
"line": 75,
"name": "someDataObject",
"type": "ISomeInterface",
},
Object { Object {
"defaultValue": "false", "defaultValue": "false",
"description": "<p>Some input you shouldn&#39;t use.</p> "description": "<p>Some input you shouldn&#39;t use.</p>
", ",
"line": 70, "line": 83,
"name": "somethingYouShouldNotUse", "name": "somethingYouShouldNotUse",
}, },
], ],
"jsdoctags": Array [ "jsdoctags": Array [
Object { Object {
"atToken": Object { "atToken": Object {
"end": 787, "end": 859,
"flags": 0, "flags": 0,
"kind": 57, "kind": 57,
"pos": 786, "pos": 858,
}, },
"comment": "Hello world", "comment": "Hello world",
"end": 794, "end": 866,
"flags": 0, "flags": 0,
"kind": 288, "kind": 288,
"pos": 786, "pos": 858,
"tagName": Object { "tagName": Object {
"end": 793, "end": 865,
"escapedText": "string", "escapedText": "string",
"flags": 0, "flags": 0,
"pos": 787, "pos": 859,
}, },
}, },
Object { Object {
"atToken": Object { "atToken": Object {
"end": 810, "end": 882,
"flags": 0, "flags": 0,
"kind": 57, "kind": 57,
"pos": 809, "pos": 881,
}, },
"comment": "[Example](http://example.com)", "comment": "[Example](http://example.com)",
"end": 815, "end": 887,
"flags": 0, "flags": 0,
"kind": 288, "kind": 288,
"pos": 809, "pos": 881,
"tagName": Object { "tagName": Object {
"end": 814, "end": 886,
"escapedText": "link", "escapedText": "link",
"flags": 0, "flags": 0,
"pos": 810, "pos": 882,
}, },
}, },
Object { Object {
"atToken": Object { "atToken": Object {
"end": 849, "end": 921,
"flags": 0, "flags": 0,
"kind": 57, "kind": 57,
"pos": 848, "pos": 920,
}, },
"comment": "\`ThingThing\`", "comment": "\`ThingThing\`",
"end": 854, "end": 926,
"flags": 0, "flags": 0,
"kind": 288, "kind": 288,
"pos": 848, "pos": 920,
"tagName": Object { "tagName": Object {
"end": 853, "end": 925,
"escapedText": "code", "escapedText": "code",
"flags": 0, "flags": 0,
"pos": 849, "pos": 921,
}, },
}, },
Object { Object {
"atToken": Object { "atToken": Object {
"end": 871, "end": 943,
"flags": 0, "flags": 0,
"kind": 57, "kind": 57,
"pos": 870, "pos": 942,
}, },
"comment": "<span class=\\"badge\\">aaa</span>", "comment": "<span class=\\"badge\\">aaa</span>",
"end": 876, "end": 948,
"flags": 0, "flags": 0,
"kind": 288, "kind": 288,
"pos": 870, "pos": 942,
"tagName": Object { "tagName": Object {
"end": 875, "end": 947,
"escapedText": "html", "escapedText": "html",
"flags": 0, "flags": 0,
"pos": 871, "pos": 943,
}, },
}, },
], ],
@ -287,16 +301,16 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"comment": "<p>Some number you&#39;d like to use.</p> "comment": "<p>Some number you&#39;d like to use.</p>
", ",
"name": Object { "name": Object {
"end": 3220, "end": 3518,
"escapedText": "x", "escapedText": "x",
"flags": 0, "flags": 0,
"pos": 3219, "pos": 3517,
}, },
"tagName": Object { "tagName": Object {
"end": 3218, "end": 3516,
"escapedText": "param", "escapedText": "param",
"flags": 0, "flags": 0,
"pos": 3213, "pos": 3511,
}, },
"type": "number", "type": "number",
}, },
@ -304,21 +318,21 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"comment": "<p>Some other number or string you&#39;d like to use, will have <code>parseInt()</code> applied before calculation.</p> "comment": "<p>Some other number or string you&#39;d like to use, will have <code>parseInt()</code> applied before calculation.</p>
", ",
"name": Object { "name": Object {
"end": 3265, "end": 3563,
"escapedText": "y", "escapedText": "y",
"flags": 0, "flags": 0,
"pos": 3264, "pos": 3562,
}, },
"tagName": Object { "tagName": Object {
"end": 3263, "end": 3561,
"escapedText": "param", "escapedText": "param",
"flags": 0, "flags": 0,
"pos": 3258, "pos": 3556,
}, },
"type": "string | number", "type": "string | number",
}, },
], ],
"line": 151, "line": 164,
"modifierKind": Array [ "modifierKind": Array [
114, 114,
], ],
@ -341,21 +355,21 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"comment": "<p>Some <code>password</code>.</p> "comment": "<p>Some <code>password</code>.</p>
", ",
"name": Object { "name": Object {
"end": 3781, "end": 4079,
"escapedText": "password", "escapedText": "password",
"flags": 0, "flags": 0,
"pos": 3773, "pos": 4071,
}, },
"tagName": Object { "tagName": Object {
"end": 3772, "end": 4070,
"escapedText": "param", "escapedText": "param",
"flags": 0, "flags": 0,
"pos": 3767, "pos": 4065,
}, },
"type": "string", "type": "string",
}, },
], ],
"line": 174, "line": 187,
"modifierKind": Array [ "modifierKind": Array [
112, 112,
], ],
@ -379,22 +393,22 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"comment": "<p>Some <code>id</code>.</p> "comment": "<p>Some <code>id</code>.</p>
", ",
"name": Object { "name": Object {
"end": 3640, "end": 3938,
"escapedText": "id", "escapedText": "id",
"flags": 0, "flags": 0,
"pos": 3638, "pos": 3936,
}, },
"optional": true, "optional": true,
"tagName": Object { "tagName": Object {
"end": 3637, "end": 3935,
"escapedText": "param", "escapedText": "param",
"flags": 0, "flags": 0,
"pos": 3632, "pos": 3930,
}, },
"type": "number", "type": "number",
}, },
], ],
"line": 165, "line": 178,
"modifierKind": Array [ "modifierKind": Array [
113, 113,
], ],
@ -421,7 +435,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"type": "ISomeInterface", "type": "ISomeInterface",
}, },
], ],
"line": 156, "line": 169,
"modifierKind": Array [ "modifierKind": Array [
114, 114,
], ],
@ -439,7 +453,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"description": "<p>Handler to be called when the button is clicked by a user.</p> "description": "<p>Handler to be called when the button is clicked by a user.</p>
<p>Will also block the emission of the event if <code>isDisabled</code> is true.</p> <p>Will also block the emission of the event if <code>isDisabled</code> is true.</p>
", ",
"line": 78, "line": 91,
"name": "onClick", "name": "onClick",
"type": "EventEmitter", "type": "EventEmitter",
}, },
@ -448,7 +462,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
Object { Object {
"defaultValue": "'some value'", "defaultValue": "'some value'",
"description": "", "description": "",
"line": 93, "line": 106,
"modifierKind": Array [ "modifierKind": Array [
112, 112,
], ],
@ -460,7 +474,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"defaultValue": "'Private hello'", "defaultValue": "'Private hello'",
"description": "<p>Private value. </p> "description": "<p>Private value. </p>
", ",
"line": 133, "line": 146,
"modifierKind": Array [ "modifierKind": Array [
112, 112,
], ],
@ -476,7 +490,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
}, },
], ],
"description": "", "description": "",
"line": 42, "line": 48,
"name": "buttonRef", "name": "buttonRef",
"optional": false, "optional": false,
"type": "ElementRef", "type": "ElementRef",
@ -485,7 +499,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
"defaultValue": "'Public hello'", "defaultValue": "'Public hello'",
"description": "<p>Public value. </p> "description": "<p>Public value. </p>
", ",
"line": 130, "line": 143,
"modifierKind": Array [ "modifierKind": Array [
114, 114,
], ],
@ -495,7 +509,7 @@ like <strong>bold</strong>, <em>italic</em>, and <code>inline code</code>.</p>
}, },
Object { Object {
"description": "", "description": "",
"line": 186, "line": 199,
"modifierKind": Array [ "modifierKind": Array [
114, 114,
], ],
@ -514,19 +528,24 @@ like **bold**, _italic_, and \`inline code\`.
"selector": "doc-button", "selector": "doc-button",
"sourceCode": "import { "sourceCode": "import {
Component, Component,
ElementRef,
EventEmitter, EventEmitter,
HostBinding,
HostListener,
Input, Input,
Output, Output,
ViewChild, ViewChild,
HostListener,
HostBinding,
ElementRef,
} from '@angular/core'; } from '@angular/core';
export const exportedConstant = 'An exported constant'; export const exportedConstant = 'An exported constant';
export type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge'; export type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge';
export enum ButtonAccent {
'Normal' = 'Normal',
'High' = 'High',
}
export interface ISomeInterface { export interface ISomeInterface {
one: string; one: string;
two: boolean; two: boolean;
@ -548,6 +567,7 @@ export interface ISomeInterface {
*/ */
@Component({ @Component({
selector: 'doc-button', selector: 'doc-button',
template: '<button>{{ label }}</button>',
}) })
export class InputComponent<T> { export class InputComponent<T> {
@ViewChild('buttonRef', { static: false }) buttonRef: ElementRef; @ViewChild('buttonRef', { static: false }) buttonRef: ElementRef;
@ -556,6 +576,10 @@ export class InputComponent<T> {
@Input() @Input()
public appearance: 'primary' | 'secondary' = 'secondary'; public appearance: 'primary' | 'secondary' = 'secondary';
/** Specify the accent-type of the button */
@Input()
public accent: ButtonAccent;
/** Sets the button to a disabled state. */ /** Sets the button to a disabled state. */
@Input() @Input()
public isDisabled = false; public isDisabled = false;
@ -572,6 +596,9 @@ export class InputComponent<T> {
@Input() @Input()
public size?: ButtonSize = 'medium'; public size?: ButtonSize = 'medium';
/** Specifies some arbitrary object */
@Input() public someDataObject: ISomeInterface;
/** /**
* Some input you shouldn't use. * Some input you shouldn't use.
* *
@ -701,17 +728,18 @@ export class InputComponent<T> {
"styleUrlsData": "", "styleUrlsData": "",
"styles": Array [], "styles": Array [],
"stylesData": "", "stylesData": "",
"template": "<button>{{ label }}</button>",
"templateUrl": Array [], "templateUrl": Array [],
"type": "component", "type": "component",
"viewProviders": Array [], "viewProviders": Array [],
}, },
], ],
"coverage": Object { "coverage": Object {
"count": 22, "count": 23,
"files": Array [ "files": Array [
Object { Object {
"coverageCount": "14/21", "coverageCount": "16/23",
"coveragePercent": 66, "coveragePercent": 69,
"filePath": "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts", "filePath": "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts",
"linktype": "component", "linktype": "component",
"name": "InputComponent", "name": "InputComponent",
@ -745,7 +773,7 @@ export class InputComponent<T> {
"interfaces": Array [ "interfaces": Array [
Object { Object {
"file": "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts", "file": "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts",
"id": "interface-ISomeInterface-14bbde487c28642f97f1f6c94b65ab31", "id": "interface-ISomeInterface-568feeafa68e593b062061c27c4625a9",
"indexSignatures": Array [], "indexSignatures": Array [],
"kind": 150, "kind": 150,
"methods": Array [], "methods": Array [],
@ -753,21 +781,21 @@ export class InputComponent<T> {
"properties": Array [ "properties": Array [
Object { Object {
"description": "", "description": "",
"line": 20, "line": 25,
"name": "one", "name": "one",
"optional": false, "optional": false,
"type": "string", "type": "string",
}, },
Object { Object {
"description": "", "description": "",
"line": 22, "line": 27,
"name": "three", "name": "three",
"optional": false, "optional": false,
"type": "any[]", "type": "any[]",
}, },
Object { Object {
"description": "", "description": "",
"line": 21, "line": 26,
"name": "two", "name": "two",
"optional": false, "optional": false,
"type": "boolean", "type": "boolean",
@ -775,19 +803,24 @@ export class InputComponent<T> {
], ],
"sourceCode": "import { "sourceCode": "import {
Component, Component,
ElementRef,
EventEmitter, EventEmitter,
HostBinding,
HostListener,
Input, Input,
Output, Output,
ViewChild, ViewChild,
HostListener,
HostBinding,
ElementRef,
} from '@angular/core'; } from '@angular/core';
export const exportedConstant = 'An exported constant'; export const exportedConstant = 'An exported constant';
export type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge'; export type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge';
export enum ButtonAccent {
'Normal' = 'Normal',
'High' = 'High',
}
export interface ISomeInterface { export interface ISomeInterface {
one: string; one: string;
two: boolean; two: boolean;
@ -809,6 +842,7 @@ export interface ISomeInterface {
*/ */
@Component({ @Component({
selector: 'doc-button', selector: 'doc-button',
template: '<button>{{ label }}</button>',
}) })
export class InputComponent<T> { export class InputComponent<T> {
@ViewChild('buttonRef', { static: false }) buttonRef: ElementRef; @ViewChild('buttonRef', { static: false }) buttonRef: ElementRef;
@ -817,6 +851,10 @@ export class InputComponent<T> {
@Input() @Input()
public appearance: 'primary' | 'secondary' = 'secondary'; public appearance: 'primary' | 'secondary' = 'secondary';
/** Specify the accent-type of the button */
@Input()
public accent: ButtonAccent;
/** Sets the button to a disabled state. */ /** Sets the button to a disabled state. */
@Input() @Input()
public isDisabled = false; public isDisabled = false;
@ -833,6 +871,9 @@ export class InputComponent<T> {
@Input() @Input()
public size?: ButtonSize = 'medium'; public size?: ButtonSize = 'medium';
/** Specifies some arbitrary object */
@Input() public someDataObject: ISomeInterface;
/** /**
* Some input you shouldn't use. * Some input you shouldn't use.
* *
@ -962,9 +1003,47 @@ export class InputComponent<T> {
}, },
], ],
"miscellaneous": Object { "miscellaneous": Object {
"enumerations": Array [], "enumerations": Array [
Object {
"childs": Array [
Object {
"name": "Normal",
"value": "Normal",
},
Object {
"name": "High",
"value": "High",
},
],
"ctype": "miscellaneous",
"description": "",
"file": "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts",
"name": "ButtonAccent",
"subtype": "enum",
},
],
"functions": Array [], "functions": Array [],
"groupedEnumerations": Object {}, "groupedEnumerations": Object {
"addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts": Array [
Object {
"childs": Array [
Object {
"name": "Normal",
"value": "Normal",
},
Object {
"name": "High",
"value": "High",
},
],
"ctype": "miscellaneous",
"description": "",
"file": "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts",
"name": "ButtonAccent",
"subtype": "enum",
},
],
},
"groupedFunctions": Object {}, "groupedFunctions": Object {},
"groupedTypeAliases": Object { "groupedTypeAliases": Object {
"addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts": Array [ "addons/docs/src/frameworks/angular/__testfixtures__/doc-button/input.ts": Array [

View File

@ -3,19 +3,24 @@
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
import { import {
Component, Component,
ElementRef,
EventEmitter, EventEmitter,
HostBinding,
HostListener,
Input, Input,
Output, Output,
ViewChild, ViewChild,
HostListener,
HostBinding,
ElementRef,
} from '@angular/core'; } from '@angular/core';
export const exportedConstant = 'An exported constant'; export const exportedConstant = 'An exported constant';
export type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge'; export type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge';
export enum ButtonAccent {
'Normal' = 'Normal',
'High' = 'High',
}
export interface ISomeInterface { export interface ISomeInterface {
one: string; one: string;
two: boolean; two: boolean;
@ -37,6 +42,7 @@ export interface ISomeInterface {
*/ */
@Component({ @Component({
selector: 'doc-button', selector: 'doc-button',
template: '<button>{{ label }}</button>',
}) })
export class InputComponent<T> { export class InputComponent<T> {
@ViewChild('buttonRef', { static: false }) buttonRef: ElementRef; @ViewChild('buttonRef', { static: false }) buttonRef: ElementRef;
@ -45,6 +51,10 @@ export class InputComponent<T> {
@Input() @Input()
public appearance: 'primary' | 'secondary' = 'secondary'; public appearance: 'primary' | 'secondary' = 'secondary';
/** Specify the accent-type of the button */
@Input()
public accent: ButtonAccent;
/** Sets the button to a disabled state. */ /** Sets the button to a disabled state. */
@Input() @Input()
public isDisabled = false; public isDisabled = false;
@ -61,6 +71,9 @@ export class InputComponent<T> {
@Input() @Input()
public size?: ButtonSize = 'medium'; public size?: ButtonSize = 'medium';
/** Specifies some arbitrary object */
@Input() public someDataObject: ISomeInterface;
/** /**
* Some input you shouldn't use. * Some input you shouldn't use.
* *

View File

@ -1,8 +1,15 @@
import { SourceType } from '../../shared';
import { extractArgTypes, extractComponentDescription } from './compodoc'; import { extractArgTypes, extractComponentDescription } from './compodoc';
import { sourceDecorator } from './sourceDecorator';
export const parameters = { export const parameters = {
docs: { docs: {
extractArgTypes, extractArgTypes,
extractComponentDescription, extractComponentDescription,
source: {
type: SourceType.DYNAMIC,
},
}, },
}; };
export const decorators = [sourceDecorator];

View File

@ -0,0 +1,62 @@
import { addons, StoryContext, StoryFn } from '@storybook/addons';
import { IStory } from '@storybook/angular';
import { computesTemplateSourceFromComponent } from '@storybook/angular/renderer';
import prettierHtml from 'prettier/parser-html';
import prettier from 'prettier/standalone';
import { SNIPPET_RENDERED, SourceType } from '../../shared';
export const skipSourceRender = (context: StoryContext) => {
const sourceParams = context?.parameters.docs?.source;
// always render if the user forces it
if (sourceParams?.type === SourceType.DYNAMIC) {
return false;
}
// never render if the user is forcing the block to render code, or
// if the user provides code
return sourceParams?.code || sourceParams?.type === SourceType.CODE;
};
const prettyUp = (source: string) => {
return prettier.format(source, {
parser: 'angular',
plugins: [prettierHtml],
htmlWhitespaceSensitivity: 'ignore',
});
};
/**
* Svelte source decorator.
* @param storyFn Fn
* @param context StoryContext
*/
export const sourceDecorator = (storyFn: StoryFn<IStory>, context: StoryContext) => {
const story = storyFn();
if (skipSourceRender(context)) {
return story;
}
const channel = addons.getChannel();
const { props, template } = story;
const {
parameters: { component, argTypes },
} = context;
if (component) {
const source = computesTemplateSourceFromComponent(component, props, argTypes);
// We might have a story with a Directive or Service defined as the component
// In these cases there might exist a template, even if we aren't able to create source from component
if (source || template) {
channel.emit(SNIPPET_RENDERED, context.id, prettyUp(source || template));
}
return story;
}
if (template) {
channel.emit(SNIPPET_RENDERED, context.id, prettyUp(template));
return story;
}
return story;
};

View File

@ -44,6 +44,7 @@
"@storybook/addons": "6.2.0-alpha.24", "@storybook/addons": "6.2.0-alpha.24",
"@storybook/core": "6.2.0-alpha.24", "@storybook/core": "6.2.0-alpha.24",
"@storybook/node-logger": "6.2.0-alpha.24", "@storybook/node-logger": "6.2.0-alpha.24",
"@storybook/api": "^6.2.0-alpha.24",
"@types/webpack-env": "^1.16.0", "@types/webpack-env": "^1.16.0",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
"core-js": "^3.8.2", "core-js": "^3.8.2",

View File

@ -0,0 +1,144 @@
import { ArgTypes } from '@storybook/api';
import { computesTemplateSourceFromComponent } from './ComputesTemplateFromComponent';
import { ButtonAccent, InputComponent, ISomeInterface } from './__testfixtures__/input.component';
describe('angular source decorator', () => {
it('With no props should generate simple tag', () => {
const component = InputComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual('<doc-button></doc-button>');
});
describe('no argTypes', () => {
it('should generate tag-only template with no props', () => {
const component = InputComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<doc-button></doc-button>`);
});
it('With props should generate tag with properties', () => {
const component = InputComponent;
const props = {
isDisabled: true,
label: 'Hello world',
accent: ButtonAccent.High,
counter: 4,
};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(
`<doc-button [counter]="4" accent="High" [isDisabled]="true" label="Hello world"></doc-button>`
);
});
it('With props should generate tag with outputs', () => {
const component = InputComponent;
const props = {
isDisabled: true,
label: 'Hello world',
onClick: ($event: any) => {},
};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(
`<doc-button [isDisabled]="true" label="Hello world" (onClick)="onClick($event)"></doc-button>`
);
});
it('should generate correct property for overridden name for Input', () => {
const component = InputComponent;
const props = {
color: '#ffffff',
};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<doc-button color="#ffffff"></doc-button>`);
});
});
describe('with argTypes (from compodoc)', () => {
it('Should handle enum as strongly typed enum', () => {
const component = InputComponent;
const props = {
isDisabled: false,
label: 'Hello world',
accent: ButtonAccent.High,
};
const argTypes: ArgTypes = {
accent: {
control: {
options: ['Normal', 'High'],
type: 'radio',
},
defaultValue: undefined,
table: {
category: 'inputs',
},
type: {
name: 'enum',
required: true,
summary: 'ButtonAccent',
},
},
};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(
`<doc-button [accent]="ButtonAccent.High" [isDisabled]="false" label="Hello world"></doc-button>`
);
});
it('Should handle enum without values as string', () => {
const component = InputComponent;
const props = {
isDisabled: false,
label: 'Hello world',
accent: ButtonAccent.High,
};
const argTypes: ArgTypes = {
accent: {
control: {
options: ['Normal', 'High'],
type: 'radio',
},
defaultValue: undefined,
table: {
category: 'inputs',
},
type: {
name: 'object',
required: true,
},
},
};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(
`<doc-button accent="High" [isDisabled]="false" label="Hello world"></doc-button>`
);
});
it('Should handle objects correctly', () => {
const component = InputComponent;
const someDataObject: ISomeInterface = {
one: 'Hello world',
two: true,
three: ['One', 'Two', 'Three'],
};
const props = {
isDisabled: false,
label: 'Hello world',
someDataObject,
};
const source = computesTemplateSourceFromComponent(component, props, null);
// Ideally we should stringify the object, but that could cause the story to break because of unescaped values in the JSON object.
// This will have to do for now
expect(source).toEqual(
`<doc-button [isDisabled]="false" label="Hello world" [someDataObject]="someDataObject"></doc-button>`
);
});
});
});

View File

@ -1,4 +1,5 @@
import { Type } from '@angular/core'; import { Type } from '@angular/core';
import { ArgType, ArgTypes } from '@storybook/api';
import { ICollection } from '../types'; import { ICollection } from '../types';
import { import {
ComponentInputsOutputs, ComponentInputsOutputs,
@ -25,7 +26,7 @@ const separateInputsOutputsAttributes = (
}; };
/** /**
* Converted a component into a template with inputs/outputs present in initial props * Converts a component into a template with inputs/outputs present in initial props
* @param component * @param component
* @param initialProps * @param initialProps
* @param innerTemplate * @param innerTemplate
@ -52,3 +53,68 @@ export const computesTemplateFromComponent = (
return `<${ngComponentMetadata.selector}${templateInputs}${templateOutputs}>${innerTemplate}</${ngComponentMetadata.selector}>`; return `<${ngComponentMetadata.selector}${templateInputs}${templateOutputs}>${innerTemplate}</${ngComponentMetadata.selector}>`;
}; };
const createAngularInputProperty = ({
propertyName,
value,
argType,
}: {
propertyName: string;
value: any;
argType?: ArgType;
}) => {
const { name: type = null, summary = null } = argType?.type || {};
let templateValue = type === 'enum' && !!summary ? `${summary}.${value}` : value;
const actualType = type === 'enum' && summary ? 'enum' : typeof value;
const requiresBrackets = ['object', 'any', 'boolean', 'enum', 'number'].includes(actualType);
if (typeof value === 'object') {
templateValue = propertyName;
}
return `${requiresBrackets ? '[' : ''}${propertyName}${
requiresBrackets ? ']' : ''
}="${templateValue}"`;
};
/**
* Converts a component into a template with inputs/outputs present in initial props
* @param component
* @param initialProps
* @param innerTemplate
*/
export const computesTemplateSourceFromComponent = (
component: Type<unknown>,
initialProps?: ICollection,
argTypes?: ArgTypes
) => {
const ngComponentMetadata = getComponentDecoratorMetadata(component);
if (!ngComponentMetadata) {
return null;
}
const ngComponentInputsOutputs = getComponentInputsOutputs(component);
const { inputs: initialInputs, outputs: initialOutputs } = separateInputsOutputsAttributes(
ngComponentInputsOutputs,
initialProps
);
const templateInputs =
initialInputs.length > 0
? ` ${initialInputs
.map((propertyName) =>
createAngularInputProperty({
propertyName,
value: initialProps[propertyName],
argType: argTypes?.[propertyName],
})
)
.join(' ')}`
: '';
const templateOutputs =
initialOutputs.length > 0
? ` ${initialOutputs.map((i) => `(${i})="${i}($event)"`).join(' ')}`
: '';
return `<${ngComponentMetadata.selector}${templateInputs}${templateOutputs}></${ngComponentMetadata.selector}>`;
};

View File

@ -0,0 +1,48 @@
// @ts-nocheck
import { Component, EventEmitter, Input, Output } from '@angular/core';
export const exportedConstant = 'An exported constant';
export enum ButtonAccent {
'Normal' = 'Normal',
'High' = 'High',
}
export interface ISomeInterface {
one: string;
two: boolean;
three: any[];
}
@Component({
selector: 'doc-button',
template: '<button>{{ label }}</button>',
})
export class InputComponent<T> {
/** Appearance style of the button. */
@Input()
public appearance: 'primary' | 'secondary' = 'secondary';
@Input()
public counter: number;
/** Specify the accent-type of the button */
@Input()
public accent: ButtonAccent;
/** To test source-generation with overridden propertyname */
@Input('color') public foregroundColor: string;
/** Sets the button to a disabled state. */
@Input()
public isDisabled = false;
@Input()
public label: string;
/** Specifies some arbitrary object */
@Input() public someDataObject: ISomeInterface;
@Output()
public onClick = new EventEmitter<Event>();
}

View File

@ -1,2 +1,3 @@
export { computesTemplateSourceFromComponent } from './client/preview/angular-beta/ComputesTemplateFromComponent';
export { RendererService } from './client/preview/angular-beta/RendererService'; export { RendererService } from './client/preview/angular-beta/RendererService';
export { getStorybookModuleMetadata } from './client/preview/angular-beta/StorybookModule'; export { getStorybookModuleMetadata } from './client/preview/angular-beta/StorybookModule';

View File

@ -1 +0,0 @@
{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":1,"numPassedTests":3,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":3,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1609324654578,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":["AppComponent"],"failureMessages":[],"fullName":"AppComponent should create the app","location":null,"status":"passed","title":"should create the app"},{"ancestorTitles":["AppComponent"],"failureMessages":[],"fullName":"AppComponent should have as title 'app'","location":null,"status":"passed","title":"should have as title 'app'"},{"ancestorTitles":["AppComponent"],"failureMessages":[],"fullName":"AppComponent should render title in a h1 tag","location":null,"status":"passed","title":"should render title in a h1 tag"}],"endTime":1609324657249,"message":"","name":"/Users/tavenier/Perso/storybook/examples/angular-cli/src/app/app.component.spec.ts","startTime":1609324655682,"status":"passed","summary":""}],"wasInterrupted":false}

View File

@ -5,6 +5,7 @@ exports[`Storyshots Addon/Controls Basic 1`] = `
<my-button <my-button
ng-reflect-is-disabled="false" ng-reflect-is-disabled="false"
ng-reflect-label="Args test" ng-reflect-label="Args test"
ng-reflect-some-data-object="[object Object]"
> >
<button <button
class="btn-secondary btn-medium" class="btn-secondary btn-medium"
@ -42,3 +43,24 @@ exports[`Storyshots Addon/Controls Disabled 1`] = `
</my-button> </my-button>
</storybook-wrapper> </storybook-wrapper>
`; `;
exports[`Storyshots Addon/Controls No Template 1`] = `
<storybook-wrapper>
<my-button
ng-reflect-is-disabled="false"
ng-reflect-label="No template"
>
<button
class="btn-secondary btn-medium"
ng-reflect-ng-class="btn-secondary,btn-medium"
>
<img
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo="
width="100"
/>
No template
</button>
</my-button>
</storybook-wrapper>
`;

View File

@ -1,5 +1,5 @@
import { Story, Meta } from '@storybook/angular/types-6-0'; import { Meta, Story } from '@storybook/angular/types-6-0';
import { DocButtonComponent } from './addons/docs/doc-button/doc-button.component'; import { DocButtonComponent, ISomeInterface } from './addons/docs/doc-button/doc-button.component';
export default { export default {
title: 'Addon/Controls', title: 'Addon/Controls',
@ -10,8 +10,18 @@ const Template: Story = (args) => ({
props: args, props: args,
}); });
const someDataObject: ISomeInterface = {
one: 'Hello world',
two: true,
three: ['One', 'Two', 'Three'],
};
export const Basic = Template.bind({}); export const Basic = Template.bind({});
Basic.args = { label: 'Args test', isDisabled: false }; Basic.args = { label: 'Args test', isDisabled: false, someDataObject };
export const Disabled = Template.bind({}); export const Disabled = Template.bind({});
Disabled.args = { label: 'Disabled', isDisabled: true }; Disabled.args = { label: 'Disabled', isDisabled: true };
export const NoTemplate = () => ({
props: { label: 'No template', isDisabled: false },
});

View File

@ -2,13 +2,13 @@
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
import { import {
Component, Component,
ElementRef,
EventEmitter, EventEmitter,
HostBinding,
HostListener,
Input, Input,
Output, Output,
ViewChild, ViewChild,
HostListener,
HostBinding,
ElementRef,
} from '@angular/core'; } from '@angular/core';
export const exportedConstant = 'An exported constant'; export const exportedConstant = 'An exported constant';
@ -21,6 +21,11 @@ export interface ISomeInterface {
three: any[]; three: any[];
} }
export enum ButtonAccent {
'Normal' = 'Normal',
'High' = 'High',
}
/** /**
* This is a simple button that demonstrates various JSDoc handling in Storybook Docs for Angular. * This is a simple button that demonstrates various JSDoc handling in Storybook Docs for Angular.
* *
@ -50,6 +55,13 @@ export class DocButtonComponent<T> {
@Input() @Input()
public isDisabled = false; public isDisabled = false;
/** Specify the accent-type of the button */
@Input()
public accent: ButtonAccent = ButtonAccent.Normal;
/** Specifies some arbitrary object */
@Input() public someDataObject: ISomeInterface;
/** /**
* The inner text of the button. * The inner text of the button.
* *

View File

@ -1,4 +1,4 @@
import { Story, Meta, ArgsTable } from '@storybook/addon-docs/blocks'; import { Story, Meta, ArgsTable, Source } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/angular/demo'; import { Button } from '@storybook/angular/demo';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
@ -26,4 +26,7 @@ import { action } from '@storybook/addon-actions';
})} })}
</Story> </Story>
<Source story="with text" />
<ArgsTable story="with text" /> <ArgsTable story="with text" />