@storybook/angular 9.0.0-beta.6 → 9.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/builders/build-storybook/index.mjs +78 -0
  2. package/dist/builders/build-storybook/index.spec.mjs +187 -0
  3. package/dist/builders/start-storybook/index.mjs +99 -0
  4. package/dist/builders/start-storybook/index.spec.mjs +186 -0
  5. package/dist/builders/utils/error-handler.mjs +33 -0
  6. package/dist/builders/utils/run-compodoc.mjs +31 -0
  7. package/dist/builders/utils/run-compodoc.spec.mjs +74 -0
  8. package/dist/builders/utils/standalone-options.mjs +1 -0
  9. package/dist/client/angular-beta/AbstractRenderer.mjs +164 -0
  10. package/dist/client/angular-beta/CanvasRenderer.mjs +9 -0
  11. package/dist/client/angular-beta/ComputesTemplateFromComponent.mjs +154 -0
  12. package/dist/client/angular-beta/ComputesTemplateFromComponent.test.mjs +728 -0
  13. package/dist/client/angular-beta/DocsRenderer.mjs +35 -0
  14. package/dist/client/angular-beta/RendererFactory.mjs +50 -0
  15. package/dist/client/angular-beta/RendererFactory.test.mjs +233 -0
  16. package/dist/client/angular-beta/StorybookModule.mjs +23 -0
  17. package/dist/client/angular-beta/StorybookModule.test.mjs +319 -0
  18. package/dist/client/angular-beta/StorybookProvider.mjs +22 -0
  19. package/dist/client/angular-beta/StorybookWrapperComponent.mjs +123 -0
  20. package/dist/client/angular-beta/__testfixtures__/input.component.mjs +73 -0
  21. package/dist/client/angular-beta/__testfixtures__/test.module.mjs +17 -0
  22. package/dist/client/angular-beta/utils/BootstrapQueue.mjs +49 -0
  23. package/dist/client/angular-beta/utils/BootstrapQueue.test.mjs +162 -0
  24. package/dist/client/angular-beta/utils/NgComponentAnalyzer.mjs +84 -0
  25. package/dist/client/angular-beta/utils/NgComponentAnalyzer.test.mjs +386 -0
  26. package/dist/client/angular-beta/utils/NgModulesAnalyzer.mjs +37 -0
  27. package/dist/client/angular-beta/utils/NgModulesAnalyzer.test.mjs +22 -0
  28. package/dist/client/angular-beta/utils/PropertyExtractor.mjs +158 -0
  29. package/dist/client/angular-beta/utils/PropertyExtractor.test.mjs +175 -0
  30. package/dist/client/angular-beta/utils/StoryUID.mjs +38 -0
  31. package/dist/client/argsToTemplate.mjs +55 -0
  32. package/dist/client/argsToTemplate.test.mjs +100 -0
  33. package/dist/client/config.mjs +4 -0
  34. package/dist/client/decorateStory.mjs +45 -0
  35. package/dist/client/decorateStory.test.mjs +301 -0
  36. package/dist/client/decorators.mjs +63 -0
  37. package/dist/client/decorators.test.mjs +157 -0
  38. package/dist/client/docs/__testfixtures__/doc-button/input.mjs +201 -0
  39. package/dist/client/docs/angular-properties.test.mjs +34 -0
  40. package/dist/client/docs/compodoc.mjs +244 -0
  41. package/dist/client/docs/compodoc.test.mjs +130 -0
  42. package/dist/client/docs/config.mjs +16 -0
  43. package/dist/client/docs/index.mjs +1 -0
  44. package/dist/client/docs/sourceDecorator.mjs +48 -0
  45. package/dist/client/docs/types.mjs +1 -0
  46. package/dist/client/globals.mjs +31 -0
  47. package/dist/client/index.mjs +9 -0
  48. package/dist/client/portable-stories.mjs +26 -0
  49. package/dist/client/preview-prod.mjs +2 -0
  50. package/dist/client/public-types.mjs +1 -0
  51. package/dist/client/render.mjs +14 -0
  52. package/dist/client/types.mjs +1 -0
  53. package/dist/node/index.mjs +3 -0
  54. package/dist/server/__mocks-ng-workspace__/minimal-config/src/main.mjs +2 -0
  55. package/dist/server/__mocks-ng-workspace__/some-config/src/main.mjs +2 -0
  56. package/dist/server/__mocks-ng-workspace__/with-angularBrowserTarget/src/main.mjs +2 -0
  57. package/dist/server/__mocks-ng-workspace__/with-lib/projects/pattern-lib/src/main.mjs +2 -0
  58. package/dist/server/__mocks-ng-workspace__/with-nx/src/main.mjs +2 -0
  59. package/dist/server/__mocks-ng-workspace__/with-nx-workspace/src/main.mjs +2 -0
  60. package/dist/server/__mocks-ng-workspace__/with-options-styles/src/main.mjs +2 -0
  61. package/dist/server/__mocks-ng-workspace__/without-projects-entry/projects/pattern-lib/src/main.mjs +2 -0
  62. package/dist/server/__mocks-ng-workspace__/without-tsConfig/src/main.mjs +2 -0
  63. package/dist/server/angular-cli-webpack.mjs +80 -0
  64. package/dist/server/framework-preset-angular-cli.mjs +81 -0
  65. package/dist/server/framework-preset-angular-docs.mjs +6 -0
  66. package/dist/server/framework-preset-angular-ivy.mjs +56 -0
  67. package/dist/server/plugins/storybook-normalize-angular-entry-plugin.mjs +52 -0
  68. package/dist/server/preset-options.mjs +1 -0
  69. package/dist/server/utils/filter-out-styling-rules.mjs +13 -0
  70. package/dist/server/utils/module-is-available.mjs +9 -0
  71. package/package.json +4 -4
@@ -0,0 +1,201 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
4
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6
+ 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;
7
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
8
+ };
9
+ var __metadata = (this && this.__metadata) || function (k, v) {
10
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
11
+ };
12
+ import { Component, EventEmitter, HostBinding, HostListener, Input, Output, ViewChild, ElementRef, } from '@angular/core';
13
+ export const exportedConstant = 'An exported constant';
14
+ export var ButtonAccent;
15
+ (function (ButtonAccent) {
16
+ ButtonAccent["Normal"] = "Normal";
17
+ ButtonAccent["High"] = "High";
18
+ })(ButtonAccent || (ButtonAccent = {}));
19
+ /**
20
+ * This is a simple button that demonstrates various JSDoc handling in Storybook Docs for Angular.
21
+ *
22
+ * It supports [markdown](https://en.wikipedia.org/wiki/Markdown), so you can embed formatted text,
23
+ * like **bold**, _italic_, and `inline code`.> How you like dem apples?! It's never been easier to
24
+ * document all your components.
25
+ *
26
+ * @string Hello world
27
+ * @link [Example](http://example.com)
28
+ * @code `ThingThing`
29
+ * @html <span class="badge">aaa</span>
30
+ */
31
+ let InputComponent = class InputComponent {
32
+ constructor() {
33
+ /** Appearance style of the button. */
34
+ this.appearance = 'secondary';
35
+ /** Sets the button to a disabled state. */
36
+ this.isDisabled = false;
37
+ /** Size of the button. */
38
+ this.size = 'medium';
39
+ /**
40
+ * Some input you shouldn't use.
41
+ *
42
+ * @deprecated
43
+ */
44
+ this.somethingYouShouldNotUse = false;
45
+ /**
46
+ * Handler to be called when the button is clicked by a user.
47
+ *
48
+ * Will also block the emission of the event if `isDisabled` is true.
49
+ */
50
+ this.onClick = new EventEmitter();
51
+ this._inputValue = 'some value';
52
+ this.focus = false;
53
+ /** @ignore */
54
+ this.ignoredProperty = 'Ignore me';
55
+ /** Public value. */
56
+ this.internalProperty = 'Public hello';
57
+ /** Private value. */
58
+ this._value = 'Private hello';
59
+ }
60
+ /**
61
+ * This is an internal method that we don't want to document and have added the `ignore`
62
+ * annotation to.
63
+ *
64
+ * @ignore
65
+ */
66
+ handleClick(event) {
67
+ event.stopPropagation();
68
+ if (!this.isDisabled) {
69
+ this.onClick.emit(event);
70
+ }
71
+ }
72
+ /** Setter for `inputValue` that is also an `@Input`. */
73
+ set inputValue(value) {
74
+ this._inputValue = value;
75
+ }
76
+ /** Getter for `inputValue`. */
77
+ get inputValue() {
78
+ return this._inputValue;
79
+ }
80
+ onClickListener(btn) {
81
+ console.log('button', btn);
82
+ }
83
+ /**
84
+ * Returns all the CSS classes for the button.
85
+ *
86
+ * @ignore
87
+ */
88
+ get classes() {
89
+ return [this.appearance, this.size]
90
+ .filter((_class) => !!_class)
91
+ .map((_class) => `btn-${_class}`);
92
+ }
93
+ /** Set the private value. */
94
+ set value(value) {
95
+ this._value = `${value}`;
96
+ }
97
+ /** Get the private value. */
98
+ get value() {
99
+ return this._value;
100
+ }
101
+ /**
102
+ * An internal calculation method which adds `x` and `y` together.
103
+ *
104
+ * @param x Some number you'd like to use.
105
+ * @param y Some other number or string you'd like to use, will have `parseInt()` applied before
106
+ * calculation.
107
+ */
108
+ calc(x, y) {
109
+ return x + parseInt(`${y}`, 10);
110
+ }
111
+ /** A public method using an interface. */
112
+ publicMethod(things) {
113
+ console.log(things);
114
+ }
115
+ /**
116
+ * A protected method.
117
+ *
118
+ * @param id Some `id`.
119
+ */
120
+ protectedMethod(id) {
121
+ console.log(id);
122
+ }
123
+ /**
124
+ * A private method.
125
+ *
126
+ * @param password Some `password`.
127
+ */
128
+ privateMethod(password) {
129
+ console.log(password);
130
+ }
131
+ set item(item) {
132
+ this.processedItem = item;
133
+ }
134
+ };
135
+ __decorate([
136
+ ViewChild('buttonRef', { static: false }),
137
+ __metadata("design:type", ElementRef)
138
+ ], InputComponent.prototype, "buttonRef", void 0);
139
+ __decorate([
140
+ Input(),
141
+ __metadata("design:type", String)
142
+ ], InputComponent.prototype, "appearance", void 0);
143
+ __decorate([
144
+ Input(),
145
+ __metadata("design:type", String)
146
+ ], InputComponent.prototype, "accent", void 0);
147
+ __decorate([
148
+ Input(),
149
+ __metadata("design:type", Object)
150
+ ], InputComponent.prototype, "isDisabled", void 0);
151
+ __decorate([
152
+ Input(),
153
+ __metadata("design:type", String)
154
+ ], InputComponent.prototype, "label", void 0);
155
+ __decorate([
156
+ Input(),
157
+ __metadata("design:type", String)
158
+ ], InputComponent.prototype, "size", void 0);
159
+ __decorate([
160
+ Input(),
161
+ __metadata("design:type", Object)
162
+ ], InputComponent.prototype, "someDataObject", void 0);
163
+ __decorate([
164
+ Input(),
165
+ __metadata("design:type", Object)
166
+ ], InputComponent.prototype, "somethingYouShouldNotUse", void 0);
167
+ __decorate([
168
+ Output(),
169
+ __metadata("design:type", Object)
170
+ ], InputComponent.prototype, "onClick", void 0);
171
+ __decorate([
172
+ Input(),
173
+ __metadata("design:type", String),
174
+ __metadata("design:paramtypes", [String])
175
+ ], InputComponent.prototype, "inputValue", null);
176
+ __decorate([
177
+ HostListener('click', ['$event.target']),
178
+ __metadata("design:type", Function),
179
+ __metadata("design:paramtypes", [Object]),
180
+ __metadata("design:returntype", void 0)
181
+ ], InputComponent.prototype, "onClickListener", null);
182
+ __decorate([
183
+ HostBinding('class.focused'),
184
+ __metadata("design:type", Object)
185
+ ], InputComponent.prototype, "focus", void 0);
186
+ __decorate([
187
+ Input('showKeyAlias'),
188
+ __metadata("design:type", Object)
189
+ ], InputComponent.prototype, "showKey", void 0);
190
+ __decorate([
191
+ Input(),
192
+ __metadata("design:type", Array),
193
+ __metadata("design:paramtypes", [Array])
194
+ ], InputComponent.prototype, "item", null);
195
+ InputComponent = __decorate([
196
+ Component({
197
+ selector: 'doc-button',
198
+ template: '<button>{{ label }}</button>',
199
+ })
200
+ ], InputComponent);
201
+ export { InputComponent };
@@ -0,0 +1,34 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ // File hierarchy: __testfixtures__ / some-test-case / input.*
5
+ const inputRegExp = /^input\..*$/;
6
+ describe('angular component properties', () => {
7
+ const fixturesDir = join(__dirname, '__testfixtures__');
8
+ readdirSync(fixturesDir, { withFileTypes: true }).forEach((testEntry) => {
9
+ if (testEntry.isDirectory()) {
10
+ const testDir = join(fixturesDir, testEntry.name);
11
+ const testFile = readdirSync(testDir).find((fileName) => inputRegExp.test(fileName));
12
+ if (testFile) {
13
+ // TODO: Remove this as soon as the real test is fixed
14
+ it('true', () => {
15
+ expect(true).toEqual(true);
16
+ });
17
+ // TODO: Fix this test
18
+ // it(`${testEntry.name}`, async () => {
19
+ // const inputPath = join(testDir, testFile);
20
+ // // snapshot the output of compodoc
21
+ // const compodocOutput = runCompodoc(inputPath);
22
+ // const compodocJson = JSON.parse(compodocOutput);
23
+ // await expect(compodocJson).toMatchFileSnapshot(
24
+ // join(testDir, `compodoc-${SNAPSHOT_OS}.snapshot`)
25
+ // );
26
+ // // snapshot the output of addon-docs angular-properties
27
+ // const componentData = findComponentByName('InputComponent', compodocJson);
28
+ // const argTypes = extractArgTypesFromData(componentData);
29
+ // await expect(argTypes).toMatchFileSnapshot(join(testDir, 'argtypes.snapshot'));
30
+ // });
31
+ }
32
+ }
33
+ });
34
+ });
@@ -0,0 +1,244 @@
1
+ import { logger } from 'storybook/internal/client-logger';
2
+ import { global } from '@storybook/global';
3
+ export const isMethod = (methodOrProp) => {
4
+ return methodOrProp.args !== undefined;
5
+ };
6
+ export const setCompodocJson = (compodocJson) => {
7
+ global.__STORYBOOK_COMPODOC_JSON__ = compodocJson;
8
+ };
9
+ export const getCompodocJson = () => global.__STORYBOOK_COMPODOC_JSON__;
10
+ export const checkValidComponentOrDirective = (component) => {
11
+ if (!component.name) {
12
+ throw new Error(`Invalid component ${JSON.stringify(component)}`);
13
+ }
14
+ };
15
+ export const checkValidCompodocJson = (compodocJson) => {
16
+ if (!compodocJson || !compodocJson.components) {
17
+ throw new Error('Invalid compodoc JSON');
18
+ }
19
+ };
20
+ const hasDecorator = (item, decoratorName) => item.decorators && item.decorators.find((x) => x.name === decoratorName);
21
+ const mapPropertyToSection = (item) => {
22
+ if (hasDecorator(item, 'ViewChild')) {
23
+ return 'view child';
24
+ }
25
+ if (hasDecorator(item, 'ViewChildren')) {
26
+ return 'view children';
27
+ }
28
+ if (hasDecorator(item, 'ContentChild')) {
29
+ return 'content child';
30
+ }
31
+ if (hasDecorator(item, 'ContentChildren')) {
32
+ return 'content children';
33
+ }
34
+ return 'properties';
35
+ };
36
+ const mapItemToSection = (key, item) => {
37
+ switch (key) {
38
+ case 'methods':
39
+ case 'methodsClass':
40
+ return 'methods';
41
+ case 'inputsClass':
42
+ return 'inputs';
43
+ case 'outputsClass':
44
+ return 'outputs';
45
+ case 'properties':
46
+ case 'propertiesClass':
47
+ if (isMethod(item)) {
48
+ throw new Error("Cannot be of type Method if key === 'propertiesClass'");
49
+ }
50
+ return mapPropertyToSection(item);
51
+ default:
52
+ throw new Error(`Unknown key: ${key}`);
53
+ }
54
+ };
55
+ export const findComponentByName = (name, compodocJson) => compodocJson.components.find((c) => c.name === name) ||
56
+ compodocJson.directives.find((c) => c.name === name) ||
57
+ compodocJson.pipes.find((c) => c.name === name) ||
58
+ compodocJson.injectables.find((c) => c.name === name) ||
59
+ compodocJson.classes.find((c) => c.name === name);
60
+ const getComponentData = (component) => {
61
+ if (!component) {
62
+ return null;
63
+ }
64
+ checkValidComponentOrDirective(component);
65
+ const compodocJson = getCompodocJson();
66
+ if (!compodocJson) {
67
+ return null;
68
+ }
69
+ checkValidCompodocJson(compodocJson);
70
+ const { name } = component;
71
+ const metadata = findComponentByName(name, compodocJson);
72
+ if (!metadata) {
73
+ logger.warn(`Component not found in compodoc JSON: '${name}'`);
74
+ }
75
+ return metadata;
76
+ };
77
+ const displaySignature = (item) => {
78
+ const args = item.args.map((arg) => `${arg.name}${arg.optional ? '?' : ''}: ${arg.type}`);
79
+ return `(${args.join(', ')}) => ${item.returnType}`;
80
+ };
81
+ const extractTypeFromValue = (defaultValue) => {
82
+ const valueType = typeof defaultValue;
83
+ return defaultValue || valueType === 'number' || valueType === 'boolean' || valueType === 'string'
84
+ ? valueType
85
+ : null;
86
+ };
87
+ const extractEnumValues = (compodocType) => {
88
+ const compodocJson = getCompodocJson();
89
+ const enumType = compodocJson?.miscellaneous?.enumerations?.find((x) => x.name === compodocType);
90
+ if (enumType?.childs.every((x) => x.value)) {
91
+ return enumType.childs.map((x) => x.value);
92
+ }
93
+ if (typeof compodocType !== 'string' || compodocType.indexOf('|') === -1) {
94
+ return null;
95
+ }
96
+ try {
97
+ return compodocType.split('|').map((value) => JSON.parse(value));
98
+ }
99
+ catch (e) {
100
+ return null;
101
+ }
102
+ };
103
+ export const extractType = (property, defaultValue) => {
104
+ const compodocType = property.type || extractTypeFromValue(defaultValue);
105
+ switch (compodocType) {
106
+ case 'string':
107
+ case 'boolean':
108
+ case 'number':
109
+ return { name: compodocType };
110
+ case undefined:
111
+ case null:
112
+ return { name: 'other', value: 'void' };
113
+ default: {
114
+ const resolvedType = resolveTypealias(compodocType);
115
+ const enumValues = extractEnumValues(resolvedType);
116
+ return enumValues
117
+ ? { name: 'enum', value: enumValues }
118
+ : { name: 'other', value: 'empty-enum' };
119
+ }
120
+ }
121
+ };
122
+ const castDefaultValue = (property, defaultValue) => {
123
+ const compodocType = property.type;
124
+ // All these checks are necessary as compodoc does not always set the type ie. @HostBinding have empty types.
125
+ // null and undefined also have 'any' type
126
+ if (['boolean', 'number', 'string', 'EventEmitter'].includes(compodocType)) {
127
+ switch (compodocType) {
128
+ case 'boolean':
129
+ return defaultValue === 'true';
130
+ case 'number':
131
+ return Number(defaultValue);
132
+ case 'EventEmitter':
133
+ return undefined;
134
+ default:
135
+ return defaultValue;
136
+ }
137
+ }
138
+ else {
139
+ switch (defaultValue) {
140
+ case 'true':
141
+ return true;
142
+ case 'false':
143
+ return false;
144
+ case 'null':
145
+ return null;
146
+ case 'undefined':
147
+ return undefined;
148
+ default:
149
+ return defaultValue;
150
+ }
151
+ }
152
+ };
153
+ const extractDefaultValueFromComments = (property, value) => {
154
+ let commentValue = value;
155
+ property.jsdoctags.forEach((tag) => {
156
+ if (['default', 'defaultvalue'].includes(tag.tagName.escapedText)) {
157
+ const dom = new global.DOMParser().parseFromString(tag.comment, 'text/html');
158
+ commentValue = dom.body.textContent;
159
+ }
160
+ });
161
+ return commentValue;
162
+ };
163
+ const extractDefaultValue = (property) => {
164
+ try {
165
+ let value = property.defaultValue?.replace(/^'(.*)'$/, '$1');
166
+ value = castDefaultValue(property, value);
167
+ if (value == null && property.jsdoctags?.length > 0) {
168
+ value = extractDefaultValueFromComments(property, value);
169
+ }
170
+ return value;
171
+ }
172
+ catch (err) {
173
+ logger.debug(`Error extracting ${property.name}: ${property.defaultValue}`);
174
+ return undefined;
175
+ }
176
+ };
177
+ const resolveTypealias = (compodocType) => {
178
+ const compodocJson = getCompodocJson();
179
+ const typeAlias = compodocJson?.miscellaneous?.typealiases?.find((x) => x.name === compodocType);
180
+ return typeAlias ? resolveTypealias(typeAlias.rawtype) : compodocType;
181
+ };
182
+ export const extractArgTypesFromData = (componentData) => {
183
+ const sectionToItems = {};
184
+ const compodocClasses = ['component', 'directive'].includes(componentData.type)
185
+ ? ['propertiesClass', 'methodsClass', 'inputsClass', 'outputsClass']
186
+ : ['properties', 'methods'];
187
+ compodocClasses.forEach((key) => {
188
+ const data = componentData[key] || [];
189
+ data.forEach((item) => {
190
+ const section = mapItemToSection(key, item);
191
+ const defaultValue = isMethod(item) ? undefined : extractDefaultValue(item);
192
+ const type = isMethod(item) || (section !== 'inputs' && section !== 'properties')
193
+ ? { name: 'other', value: 'void' }
194
+ : extractType(item, defaultValue);
195
+ const action = section === 'outputs' ? { action: item.name } : {};
196
+ const argType = {
197
+ name: item.name,
198
+ description: item.rawdescription || item.description,
199
+ type,
200
+ ...action,
201
+ table: {
202
+ category: section,
203
+ type: {
204
+ summary: isMethod(item) ? displaySignature(item) : item.type,
205
+ required: isMethod(item) ? false : !item.optional,
206
+ },
207
+ defaultValue: { summary: defaultValue },
208
+ },
209
+ };
210
+ if (!sectionToItems[section]) {
211
+ sectionToItems[section] = [];
212
+ }
213
+ sectionToItems[section].push(argType);
214
+ });
215
+ });
216
+ const SECTIONS = [
217
+ 'properties',
218
+ 'inputs',
219
+ 'outputs',
220
+ 'methods',
221
+ 'view child',
222
+ 'view children',
223
+ 'content child',
224
+ 'content children',
225
+ ];
226
+ const argTypes = {};
227
+ SECTIONS.forEach((section) => {
228
+ const items = sectionToItems[section];
229
+ if (items) {
230
+ items.forEach((argType) => {
231
+ argTypes[argType.name] = argType;
232
+ });
233
+ }
234
+ });
235
+ return argTypes;
236
+ };
237
+ export const extractArgTypes = (component) => {
238
+ const componentData = getComponentData(component);
239
+ return componentData && extractArgTypesFromData(componentData);
240
+ };
241
+ export const extractComponentDescription = (component) => {
242
+ const componentData = getComponentData(component);
243
+ return componentData && (componentData.rawdescription || componentData.description);
244
+ };
@@ -0,0 +1,130 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { extractType, setCompodocJson } from './compodoc';
3
+ const makeProperty = (compodocType) => ({
4
+ type: compodocType,
5
+ name: 'dummy',
6
+ decorators: [],
7
+ optional: true,
8
+ });
9
+ const getDummyCompodocJson = () => {
10
+ return {
11
+ miscellaneous: {
12
+ typealiases: [
13
+ {
14
+ name: 'EnumAlias',
15
+ ctype: 'miscellaneous',
16
+ subtype: 'typealias',
17
+ rawtype: 'EnumNumeric',
18
+ file: 'src/stories/component-with-enums/enums.component.ts',
19
+ description: '',
20
+ kind: 161,
21
+ },
22
+ {
23
+ name: 'TypeAlias',
24
+ ctype: 'miscellaneous',
25
+ subtype: 'typealias',
26
+ rawtype: '"Type Alias 1" | "Type Alias 2" | "Type Alias 3"',
27
+ file: 'src/stories/component-with-enums/enums.component.ts',
28
+ description: '',
29
+ kind: 168,
30
+ },
31
+ ],
32
+ enumerations: [
33
+ {
34
+ name: 'EnumNumeric',
35
+ childs: [
36
+ {
37
+ name: 'FIRST',
38
+ },
39
+ {
40
+ name: 'SECOND',
41
+ },
42
+ {
43
+ name: 'THIRD',
44
+ },
45
+ ],
46
+ ctype: 'miscellaneous',
47
+ subtype: 'enum',
48
+ description: '<p>Button Priority</p>\n',
49
+ file: 'src/stories/component-with-enums/enums.component.ts',
50
+ },
51
+ {
52
+ name: 'EnumNumericInitial',
53
+ childs: [
54
+ {
55
+ name: 'UNO',
56
+ value: '1',
57
+ },
58
+ {
59
+ name: 'DOS',
60
+ },
61
+ {
62
+ name: 'TRES',
63
+ },
64
+ ],
65
+ ctype: 'miscellaneous',
66
+ subtype: 'enum',
67
+ description: '',
68
+ file: 'src/stories/component-with-enums/enums.component.ts',
69
+ },
70
+ {
71
+ name: 'EnumStringValues',
72
+ childs: [
73
+ {
74
+ name: 'PRIMARY',
75
+ value: 'PRIMARY',
76
+ },
77
+ {
78
+ name: 'SECONDARY',
79
+ value: 'SECONDARY',
80
+ },
81
+ {
82
+ name: 'TERTIARY',
83
+ value: 'TERTIARY',
84
+ },
85
+ ],
86
+ ctype: 'miscellaneous',
87
+ subtype: 'enum',
88
+ description: '',
89
+ file: 'src/stories/component-with-enums/enums.component.ts',
90
+ },
91
+ ],
92
+ },
93
+ };
94
+ };
95
+ describe('extractType', () => {
96
+ describe('with compodoc type', () => {
97
+ setCompodocJson(getDummyCompodocJson());
98
+ it.each([
99
+ ['string', { name: 'string' }],
100
+ ['boolean', { name: 'boolean' }],
101
+ ['number', { name: 'number' }],
102
+ // ['object', { name: 'object' }], // seems to be wrong | TODO: REVISIT
103
+ // ['foo', { name: 'other', value: 'empty-enum' }], // seems to be wrong | TODO: REVISIT
104
+ [null, { name: 'other', value: 'void' }],
105
+ [undefined, { name: 'other', value: 'void' }],
106
+ // ['T[]', { name: 'other', value: 'empty-enum' }], // seems to be wrong | TODO: REVISIT
107
+ ['[]', { name: 'other', value: 'empty-enum' }],
108
+ ['"primary" | "secondary"', { name: 'enum', value: ['primary', 'secondary'] }],
109
+ ['TypeAlias', { name: 'enum', value: ['Type Alias 1', 'Type Alias 2', 'Type Alias 3'] }],
110
+ // ['EnumNumeric', { name: 'other', value: 'empty-enum' }], // seems to be wrong | TODO: REVISIT
111
+ // ['EnumNumericInitial', { name: 'other', value: 'empty-enum' }], // seems to be wrong | TODO: REVISIT
112
+ ['EnumStringValues', { name: 'enum', value: ['PRIMARY', 'SECONDARY', 'TERTIARY'] }],
113
+ ])('%s', (compodocType, expected) => {
114
+ expect(extractType(makeProperty(compodocType), null)).toEqual(expected);
115
+ });
116
+ });
117
+ describe('without compodoc type', () => {
118
+ it.each([
119
+ ['string', { name: 'string' }],
120
+ ['', { name: 'string' }],
121
+ [false, { name: 'boolean' }],
122
+ [10, { name: 'number' }],
123
+ // [['abc'], { name: 'object' }], // seems to be wrong | TODO: REVISIT
124
+ // [{ foo: 1 }, { name: 'other', value: 'empty-enum' }], // seems to be wrong | TODO: REVISIT
125
+ [undefined, { name: 'other', value: 'void' }],
126
+ ])('%s', (defaultValue, expected) => {
127
+ expect(extractType(makeProperty(null), defaultValue)).toEqual(expected);
128
+ });
129
+ });
130
+ });
@@ -0,0 +1,16 @@
1
+ import { SourceType, enhanceArgTypes } from 'storybook/internal/docs-tools';
2
+ import { extractArgTypes, extractComponentDescription } from './compodoc';
3
+ import { sourceDecorator } from './sourceDecorator';
4
+ export const parameters = {
5
+ docs: {
6
+ story: { inline: true },
7
+ extractArgTypes,
8
+ extractComponentDescription,
9
+ source: {
10
+ type: SourceType.DYNAMIC,
11
+ language: 'html',
12
+ },
13
+ },
14
+ };
15
+ export const decorators = [sourceDecorator];
16
+ export const argTypesEnhancers = [enhanceArgTypes];
@@ -0,0 +1 @@
1
+ export * from './compodoc';
@@ -0,0 +1,48 @@
1
+ import { SourceType } from 'storybook/internal/docs-tools';
2
+ import { useRef, emitTransformCode, useEffect } from 'storybook/preview-api';
3
+ import { computesTemplateSourceFromComponent } from '../../renderer';
4
+ export const skipSourceRender = (context) => {
5
+ const sourceParams = context?.parameters.docs?.source;
6
+ // always render if the user forces it
7
+ if (sourceParams?.type === SourceType.DYNAMIC) {
8
+ return false;
9
+ }
10
+ // never render if the user is forcing the block to render code, or
11
+ // if the user provides code
12
+ return sourceParams?.code || sourceParams?.type === SourceType.CODE;
13
+ };
14
+ /**
15
+ * Angular source decorator.
16
+ *
17
+ * @param storyFn Fn
18
+ * @param context StoryContext
19
+ */
20
+ export const sourceDecorator = (storyFn, context) => {
21
+ const story = storyFn();
22
+ const source = useRef(undefined);
23
+ useEffect(() => {
24
+ if (skipSourceRender(context)) {
25
+ return;
26
+ }
27
+ const { props, userDefinedTemplate } = story;
28
+ const { component, argTypes, parameters } = context;
29
+ const template = parameters.docs?.source?.excludeDecorators
30
+ ? context.originalStoryFn(context.args, context).template
31
+ : story.template;
32
+ if (component && !userDefinedTemplate) {
33
+ const sourceFromComponent = computesTemplateSourceFromComponent(component, props, argTypes);
34
+ // We might have a story with a Directive or Service defined as the component
35
+ // In these cases there might exist a template, even if we aren't able to create source from component
36
+ const newSource = sourceFromComponent || template;
37
+ if (newSource && newSource !== source.current) {
38
+ emitTransformCode(newSource, context);
39
+ source.current = newSource;
40
+ }
41
+ }
42
+ else if (template && template !== source.current) {
43
+ emitTransformCode(template, context);
44
+ source.current = template;
45
+ }
46
+ });
47
+ return story;
48
+ };
@@ -0,0 +1 @@
1
+ export {};