mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-17 05:02:23 +08:00
Merge pull request #10946 from storybookjs/10583-controls-for-angular
Addon-controls: Angular support
This commit is contained in:
commit
93658d5fed
@ -3,19 +3,19 @@ import addons, { types } from '@storybook/addons';
|
||||
import { AddonPanel } from '@storybook/components';
|
||||
import { API } from '@storybook/api';
|
||||
import { ControlsPanel } from './components/ControlsPanel';
|
||||
import { ADDON_ID } from './constants';
|
||||
import { ADDON_ID, PARAM_KEY } from './constants';
|
||||
|
||||
addons.register(ADDON_ID, (api: API) => {
|
||||
addons.addPanel(ADDON_ID, {
|
||||
title: 'Controls',
|
||||
type: types.PANEL,
|
||||
render: ({ active }) => {
|
||||
paramKey: PARAM_KEY,
|
||||
render: ({ key, active }) => {
|
||||
if (!active || !api.getCurrentStoryData()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<AddonPanel active={active}>
|
||||
{false}
|
||||
<AddonPanel key={key} active={active}>
|
||||
<ControlsPanel />
|
||||
</AddonPanel>
|
||||
);
|
||||
|
@ -51,6 +51,7 @@
|
||||
"@storybook/addons": "6.0.0-beta.16",
|
||||
"@storybook/api": "6.0.0-beta.16",
|
||||
"@storybook/client-api": "6.0.0-beta.16",
|
||||
"@storybook/client-logger": "6.0.0-beta.16",
|
||||
"@storybook/components": "6.0.0-beta.16",
|
||||
"@storybook/core": "6.0.0-beta.16",
|
||||
"@storybook/core-events": "6.0.0-beta.16",
|
||||
|
@ -3,9 +3,7 @@
|
||||
exports[`angular component properties doc-button 1`] = `
|
||||
Object {
|
||||
"_inputValue": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "'some value'",
|
||||
},
|
||||
"defaultValue": "some value",
|
||||
"description": "",
|
||||
"name": "_inputValue",
|
||||
"table": Object {
|
||||
@ -15,11 +13,12 @@ Object {
|
||||
"summary": "string",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"_value": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "'Private hello'",
|
||||
},
|
||||
"defaultValue": "Private hello",
|
||||
"description": "<p>Private value. </p>
|
||||
",
|
||||
"name": "_value",
|
||||
@ -30,11 +29,12 @@ Object {
|
||||
"summary": "string",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"appearance": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "'secondary'",
|
||||
},
|
||||
"defaultValue": "secondary",
|
||||
"description": "<p>Appearance style of the button. </p>
|
||||
",
|
||||
"name": "appearance",
|
||||
@ -45,11 +45,16 @@ Object {
|
||||
"summary": "\\"primary\\" | \\"secondary\\"",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "enum",
|
||||
"value": Array [
|
||||
"primary",
|
||||
"secondary",
|
||||
],
|
||||
},
|
||||
},
|
||||
"buttonRef": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "",
|
||||
"name": "buttonRef",
|
||||
"table": Object {
|
||||
@ -59,11 +64,12 @@ Object {
|
||||
"summary": "ElementRef",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"calc": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "<p>An internal calculation method which adds <code>x</code> and <code>y</code> together.</p>
|
||||
",
|
||||
"name": "calc",
|
||||
@ -74,11 +80,12 @@ Object {
|
||||
"summary": "(x: number, y: string | number) => number",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"inputValue": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "<p>Setter for <code>inputValue</code> that is also an <code>@Input</code>. </p>
|
||||
",
|
||||
"name": "inputValue",
|
||||
@ -89,11 +96,12 @@ Object {
|
||||
"summary": "string",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "string",
|
||||
},
|
||||
},
|
||||
"internalProperty": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "'Public hello'",
|
||||
},
|
||||
"defaultValue": "Public hello",
|
||||
"description": "<p>Public value. </p>
|
||||
",
|
||||
"name": "internalProperty",
|
||||
@ -104,11 +112,12 @@ Object {
|
||||
"summary": "string",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"isDisabled": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "false",
|
||||
},
|
||||
"defaultValue": false,
|
||||
"description": "<p>Sets the button to a disabled state. </p>
|
||||
",
|
||||
"name": "isDisabled",
|
||||
@ -119,11 +128,12 @@ Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "boolean",
|
||||
},
|
||||
},
|
||||
"item": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": undefined,
|
||||
"name": "item",
|
||||
"table": Object {
|
||||
@ -133,11 +143,12 @@ Object {
|
||||
"summary": "[]",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "object",
|
||||
},
|
||||
},
|
||||
"label": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "<p>The inner text of the button.</p>
|
||||
",
|
||||
"name": "label",
|
||||
@ -148,11 +159,12 @@ Object {
|
||||
"summary": "string",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "string",
|
||||
},
|
||||
},
|
||||
"onClick": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "new EventEmitter<Event>()",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"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>
|
||||
",
|
||||
@ -164,11 +176,12 @@ Object {
|
||||
"summary": "EventEmitter",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"privateMethod": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "<p>A private method.</p>
|
||||
",
|
||||
"name": "privateMethod",
|
||||
@ -179,11 +192,12 @@ Object {
|
||||
"summary": "(password: string) => void",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"processedItem": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "",
|
||||
"name": "processedItem",
|
||||
"table": Object {
|
||||
@ -193,11 +207,12 @@ Object {
|
||||
"summary": "T[]",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"protectedMethod": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "<p>A protected method.</p>
|
||||
",
|
||||
"name": "protectedMethod",
|
||||
@ -208,11 +223,12 @@ Object {
|
||||
"summary": "(id?: number) => void",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"publicMethod": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "<p>A public method using an interface. </p>
|
||||
",
|
||||
"name": "publicMethod",
|
||||
@ -223,11 +239,12 @@ Object {
|
||||
"summary": "(things: ISomeInterface) => void",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"showKeyAlias": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": undefined,
|
||||
"name": "showKeyAlias",
|
||||
"table": Object {
|
||||
@ -237,11 +254,12 @@ Object {
|
||||
"summary": "",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "void",
|
||||
},
|
||||
},
|
||||
"size": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "'medium'",
|
||||
},
|
||||
"defaultValue": "medium",
|
||||
"description": "<p>Size of the button. </p>
|
||||
",
|
||||
"name": "size",
|
||||
@ -252,11 +270,12 @@ Object {
|
||||
"summary": "ButtonSize",
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "object",
|
||||
},
|
||||
},
|
||||
"somethingYouShouldNotUse": Object {
|
||||
"defaultValue": Object {
|
||||
"summary": "false",
|
||||
},
|
||||
"defaultValue": false,
|
||||
"description": "<p>Some input you shouldn't use.</p>
|
||||
",
|
||||
"name": "somethingYouShouldNotUse",
|
||||
@ -267,6 +286,9 @@ Object {
|
||||
"summary": undefined,
|
||||
},
|
||||
},
|
||||
"type": Object {
|
||||
"name": "boolean",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
41
addons/docs/src/frameworks/angular/compodoc.test.ts
Normal file
41
addons/docs/src/frameworks/angular/compodoc.test.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { extractType } from './compodoc';
|
||||
import { Decorator } from './types';
|
||||
|
||||
const makeProperty = (compodocType?: string) => ({
|
||||
type: compodocType,
|
||||
name: 'dummy',
|
||||
decorators: [] as Decorator[],
|
||||
optional: true,
|
||||
});
|
||||
|
||||
describe('extractType', () => {
|
||||
describe('with compodoc type', () => {
|
||||
it.each([
|
||||
['string', { name: 'string' }],
|
||||
['boolean', { name: 'boolean' }],
|
||||
['number', { name: 'number' }],
|
||||
['object', { name: 'object' }],
|
||||
['foo', { name: 'object' }],
|
||||
[null, { name: 'void' }],
|
||||
[undefined, { name: 'void' }],
|
||||
['T[]', { name: 'object' }],
|
||||
['[]', { name: 'object' }],
|
||||
['"primary" | "secondary"', { name: 'enum', value: ['primary', 'secondary'] }],
|
||||
])('%s', (compodocType, expected) => {
|
||||
expect(extractType(makeProperty(compodocType), null)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('without compodoc type', () => {
|
||||
it.each([
|
||||
['string', { name: 'string' }],
|
||||
[false, { name: 'boolean' }],
|
||||
[10, { name: 'number' }],
|
||||
[['abc'], { name: 'object' }],
|
||||
[{ foo: 1 }, { name: 'object' }],
|
||||
[undefined, { name: 'void' }],
|
||||
])('%s', (defaultValue, expected) => {
|
||||
expect(extractType(makeProperty(null), defaultValue)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
@ -3,6 +3,8 @@
|
||||
|
||||
import { PropDef } from '@storybook/components';
|
||||
import { ArgType, ArgTypes } from '@storybook/api';
|
||||
import { logger } from '@storybook/client-logger';
|
||||
import { string } from 'prop-types';
|
||||
import { Argument, CompodocJson, Component, Method, Property, Directive } from './types';
|
||||
|
||||
type Sections = Record<string, PropDef[]>;
|
||||
@ -90,6 +92,46 @@ const displaySignature = (item: Method): string => {
|
||||
return `(${args.join(', ')}) => ${item.returnType}`;
|
||||
};
|
||||
|
||||
const extractTypeFromValue = (defaultValue: any) => {
|
||||
const valueType = typeof defaultValue;
|
||||
return defaultValue || valueType === 'boolean' ? valueType : null;
|
||||
};
|
||||
|
||||
const extractEnumValues = (compodocType: any) => {
|
||||
if (typeof compodocType !== 'string' || compodocType.indexOf('|') === -1) {
|
||||
return null;
|
||||
}
|
||||
return compodocType.split('|').map((value) => JSON.parse(value));
|
||||
};
|
||||
|
||||
export const extractType = (property: Property, defaultValue: any) => {
|
||||
const compodocType = property.type || extractTypeFromValue(defaultValue);
|
||||
switch (compodocType) {
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
return { name: compodocType };
|
||||
case undefined:
|
||||
case null:
|
||||
return { name: 'void' };
|
||||
default: {
|
||||
const enumValues = extractEnumValues(compodocType);
|
||||
return enumValues ? { name: 'enum', value: enumValues } : { name: 'object' };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const extractDefaultValue = (property: Property) => {
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
const value = eval(property.defaultValue);
|
||||
return value;
|
||||
} catch (err) {
|
||||
logger.info(`Error extracting ${property.name}: $ {property.defaultValue}`);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const extractArgTypesFromData = (componentData: Directive) => {
|
||||
const sectionToItems: Record<string, ArgType[]> = {};
|
||||
const compodocClasses = ['propertiesClass', 'methodsClass', 'inputsClass', 'outputsClass'];
|
||||
@ -99,10 +141,16 @@ export const extractArgTypesFromData = (componentData: Directive) => {
|
||||
const data = componentData[key] || [];
|
||||
data.forEach((item: Method | Property) => {
|
||||
const section = mapItemToSection(key, item);
|
||||
const defaultValue = isMethod(item) ? undefined : extractDefaultValue(item as Property);
|
||||
const type =
|
||||
isMethod(item) || section !== 'inputs'
|
||||
? { name: 'void' }
|
||||
: extractType(item as Property, defaultValue);
|
||||
const argType = {
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
defaultValue: { summary: isMethod(item) ? '' : item.defaultValue },
|
||||
defaultValue,
|
||||
type,
|
||||
table: {
|
||||
category: section,
|
||||
type: {
|
||||
|
@ -2,6 +2,7 @@ module.exports = {
|
||||
stories: ['../src/stories/**/*.stories.(ts|mdx)'],
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-controls',
|
||||
'@storybook/addon-storysource',
|
||||
'@storybook/addon-actions',
|
||||
'@storybook/addon-links',
|
||||
|
@ -40,6 +40,7 @@
|
||||
"@storybook/addon-a11y": "6.0.0-beta.16",
|
||||
"@storybook/addon-actions": "6.0.0-beta.16",
|
||||
"@storybook/addon-backgrounds": "6.0.0-beta.16",
|
||||
"@storybook/addon-controls": "6.0.0-beta.16",
|
||||
"@storybook/addon-docs": "6.0.0-beta.16",
|
||||
"@storybook/addon-jest": "6.0.0-beta.16",
|
||||
"@storybook/addon-knobs": "6.0.0-beta.16",
|
||||
|
@ -15,7 +15,7 @@ exports[`Storyshots DocButton Basic 1`] = `
|
||||
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo="
|
||||
width="100"
|
||||
/>
|
||||
Docs Test
|
||||
Args test
|
||||
|
||||
</button>
|
||||
</my-button>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<button [ngClass]="classes" #buttonRef>
|
||||
<button [disabled]="isDisabled" [ngClass]="classes" #buttonRef>
|
||||
<img
|
||||
width="100"
|
||||
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo="
|
||||
|
@ -6,9 +6,8 @@ export default {
|
||||
parameters: { docs: { iframeHeight: 120 } },
|
||||
};
|
||||
|
||||
export const Basic = () => ({
|
||||
export const Basic = (args) => ({
|
||||
component: ButtonComponent,
|
||||
props: {
|
||||
label: 'Docs Test',
|
||||
},
|
||||
props: args,
|
||||
});
|
||||
Basic.args = { label: 'Args test', isDisabled: false };
|
||||
|
@ -1,5 +1,4 @@
|
||||
export const parameters = {
|
||||
passArgsFirst: true,
|
||||
docs: {
|
||||
iframeHeight: '60px',
|
||||
},
|
||||
|
@ -7,7 +7,6 @@ Vue.component('my-button', MyButton);
|
||||
Vue.use(Vuex);
|
||||
|
||||
export const parameters = {
|
||||
passArgsFirst: true,
|
||||
docs: {
|
||||
iframeHeight: '60px',
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user