Merge pull request #16178 from jpzwarte/web-components/generate-event-handlers

Web-components: Autogenerate action argTypes for events.
This commit is contained in:
Michael Shilman 2021-10-02 09:01:28 +08:00 committed by GitHub
commit 6fa7fd1a2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 583 additions and 42 deletions

View File

@ -0,0 +1,448 @@
{
"schemaVersion": "1.0.0",
"readme": "",
"modules": [
{
"kind": "javascript-module",
"path": "demo-wc-card.js",
"declarations": [],
"exports": [
{
"kind": "custom-element-definition",
"name": "demo-wc-card",
"declaration": {
"name": "DemoWcCard",
"module": "/src/stories/misc/to-update/DemoWcCard.js"
}
}
]
},
{
"kind": "javascript-module",
"path": "src/typings.d.ts",
"declarations": [],
"exports": []
},
{
"kind": "javascript-module",
"path": "src/components/sb-button.ts",
"declarations": [
{
"kind": "class",
"description": "",
"name": "SbButton",
"cssProperties": [
{
"description": "Controls the color of bar",
"name": "--sb-primary-color",
"default": "#1ea7fd"
}
],
"members": [
{
"kind": "field",
"name": "primary",
"type": {
"text": "boolean"
},
"description": "Set button in primary mode",
"privacy": "public"
},
{
"kind": "field",
"name": "backgroundColor",
"type": {
"text": "string"
},
"privacy": "public"
},
{
"kind": "field",
"name": "size",
"type": {
"text": "'small' | 'medium' | 'large'"
},
"default": "'medium'",
"privacy": "public"
},
{
"kind": "field",
"name": "label",
"default": "''",
"privacy": "public"
},
{
"kind": "method",
"name": "onClick",
"privacy": "private"
}
],
"events": [
{
"name": "sb-button:click",
"type": {
"text": "CustomEvent"
},
"description": "Custom event send when the button is clicked"
}
],
"attributes": [
{
"type": {
"text": "string"
},
"description": "Label of the button",
"name": "label"
},
{
"type": {
"text": "string"
},
"description": "Size of the button, can be \"small\", \"medium\" or \"large\"; default is \"medium\".",
"name": "size"
},
{
"type": {
"text": "string"
},
"description": "Color of the button's background",
"name": "backgroundColor"
},
{
"name": "label",
"fieldName": "label"
},
{
"name": "primary",
"fieldName": "primary"
},
{
"name": "size",
"fieldName": "size"
},
{
"name": "backgroundColor",
"fieldName": "backgroundColor"
}
],
"superclass": {
"name": "LitElement",
"package": "lit"
},
"tagName": "sb-button",
"summary": "This is a simple Storybook Button",
"customElement": true
}
],
"exports": [
{
"kind": "js",
"name": "SbButton",
"declaration": {
"name": "SbButton",
"module": "src/components/sb-button.ts"
}
},
{
"kind": "custom-element-definition",
"name": "sb-button",
"declaration": {
"name": "SbButton",
"module": "src/components/sb-button.ts"
}
}
]
},
{
"kind": "javascript-module",
"path": "src/components/sb-header.ts",
"declarations": [
{
"kind": "class",
"description": "",
"name": "SbHeader",
"members": [
{
"kind": "field",
"name": "user",
"type": {
"text": "{}"
},
"privacy": "public"
},
{
"kind": "method",
"name": "dispatchCustomEvent",
"privacy": "private",
"parameters": [
{
"name": "eventName",
"type": {
"text": "string"
}
}
]
},
{
"kind": "method",
"name": "logInOutButton",
"privacy": "private"
}
],
"events": [
{
"type": {
"text": "CustomEvent"
}
},
{
"type": {
"text": "CustomEvent"
},
"description": "Event send when user clicks on create account button",
"name": "sb-header:createAccount"
},
{
"type": {
"text": "CustomEvent"
},
"description": "Event send when user clicks on login button",
"name": "sb-header:login"
},
{
"type": {
"text": "CustomEvent"
},
"description": "Event send when user clicks on logout button",
"name": "sb-header:logout"
}
],
"attributes": [
{
"type": {
"text": "Object"
},
"description": "User of the app",
"name": "user"
},
{
"name": "user",
"fieldName": "user"
}
],
"superclass": {
"name": "LitElement",
"package": "lit"
},
"tagName": "sb-header",
"summary": "This is a simple Storybook Header",
"customElement": true
}
],
"exports": [
{
"kind": "js",
"name": "SbHeader",
"declaration": {
"name": "SbHeader",
"module": "src/components/sb-header.ts"
}
},
{
"kind": "custom-element-definition",
"name": "sb-header",
"declaration": {
"name": "SbHeader",
"module": "src/components/sb-header.ts"
}
}
]
},
{
"kind": "javascript-module",
"path": "src/components/sb-page.ts",
"declarations": [
{
"kind": "class",
"description": "",
"name": "SbPage",
"members": [
{
"kind": "field",
"name": "user",
"type": {
"text": "{}"
},
"privacy": "public"
}
],
"attributes": [
{
"type": {
"text": "Object"
},
"description": "User of the app",
"name": "user"
},
{
"name": "user",
"fieldName": "user"
}
],
"superclass": {
"name": "LitElement",
"package": "lit"
},
"tagName": "sb-page",
"summary": "This is a simple Storybook Page",
"customElement": true
}
],
"exports": [
{
"kind": "js",
"name": "SbPage",
"declaration": {
"name": "SbPage",
"module": "src/components/sb-page.ts"
}
},
{
"kind": "custom-element-definition",
"name": "sb-page",
"declaration": {
"name": "SbPage",
"module": "src/components/sb-page.ts"
}
}
]
},
{
"kind": "javascript-module",
"path": "src/stories/misc/to-update/DemoWcCard.js",
"declarations": [
{
"kind": "class",
"description": "This is a container looking like a card with a back and front side you can switch",
"name": "DemoWcCard",
"cssProperties": [
{
"description": "Header font size",
"name": "--demo-wc-card-header-font-size"
},
{
"description": "Font color for front",
"name": "--demo-wc-card-front-color"
},
{
"description": "Font color for back",
"name": "--demo-wc-card-back-color"
}
],
"cssParts": [
{
"description": "Front of the card",
"name": "front"
},
{
"description": "Back of the card",
"name": "back"
}
],
"slots": [
{
"description": "This is an unnamed slot (the default slot)",
"name": ""
}
],
"members": [
{
"kind": "method",
"name": "toggle"
},
{
"kind": "field",
"name": "backSide",
"privacy": "public",
"description": "Indicates that the back of the card is shown",
"default": "false"
},
{
"kind": "field",
"name": "header",
"privacy": "public",
"description": "Header message",
"default": "'Your Message'"
},
{
"kind": "field",
"name": "rows",
"privacy": "public",
"description": "Data rows",
"default": "[]"
}
],
"events": [
{
"name": "side-changed",
"type": {
"text": "CustomEvent"
},
"description": "Fires whenever it switches between front/back"
}
],
"attributes": [
{
"name": "back-side",
"fieldName": "backSide"
},
{
"name": "header",
"fieldName": "header"
},
{
"name": "rows",
"fieldName": "rows"
}
],
"superclass": {
"name": "LitElement",
"package": "lit"
},
"tagName": "demo-wc-card",
"customElement": true
}
],
"exports": [
{
"kind": "js",
"name": "DemoWcCard",
"declaration": {
"name": "DemoWcCard",
"module": "src/stories/misc/to-update/DemoWcCard.js"
}
}
]
},
{
"kind": "javascript-module",
"path": "src/stories/misc/to-update/demoWcCardStyle.css.js",
"declarations": [
{
"kind": "variable",
"name": "demoWcCardStyle"
}
],
"exports": [
{
"kind": "js",
"name": "demoWcCardStyle",
"declaration": {
"name": "demoWcCardStyle",
"module": "src/stories/misc/to-update/demoWcCardStyle.css.js"
}
}
]
}
]
}

View File

@ -2,23 +2,6 @@
exports[`web-components component properties lit-element-demo-card 1`] = `
Object {
"": Object {
"description": "This is an unnamed slot (the default slot)",
"name": "",
"required": false,
"table": Object {
"category": "slots",
"defaultValue": Object {
"summary": undefined,
},
"type": Object {
"summary": undefined,
},
},
"type": Object {
"name": "void",
},
},
"--demo-wc-card-back-color": Object {
"description": "Font color for back",
"name": "--demo-wc-card-back-color",
@ -155,6 +138,15 @@ Object {
"name": "string",
},
},
"onSideChanged": Object {
"action": Object {
"name": "side-changed",
},
"name": "onSideChanged",
"table": Object {
"disable": true,
},
},
"rows": Object {
"description": "Data rows",
"name": "rows",

View File

@ -0,0 +1,50 @@
/* eslint-disable no-underscore-dangle */
import global from 'global';
import { extractArgTypes } from './custom-elements';
import customElementsManifest from './__testfixtures__/custom-elements.json';
declare global {
interface Window {
__STORYBOOK_CUSTOM_ELEMENTS_MANIFEST__: any;
}
}
const { window } = global;
describe('extractArgTypes', () => {
beforeEach(() => {
window.__STORYBOOK_CUSTOM_ELEMENTS_MANIFEST__ = customElementsManifest;
});
afterEach(() => {
window.__STORYBOOK_CUSTOM_ELEMENTS_MANIFEST__ = undefined;
});
describe('events', () => {
it('should map to an action event handler', () => {
const { onSbHeaderCreateAccount } = extractArgTypes('sb-header');
expect(onSbHeaderCreateAccount).toEqual({
name: 'onSbHeaderCreateAccount',
action: { name: 'sb-header:createAccount' },
table: { disable: true },
});
});
it('should map to a regular item', () => {
const { 'sb-header:createAccount': item } = extractArgTypes('sb-header');
expect(item).toEqual({
name: 'sb-header:createAccount',
required: false,
description: 'Event send when user clicks on create account button',
type: { name: 'void' },
table: {
category: 'events',
type: { summary: 'CustomEvent' },
defaultValue: { summary: undefined },
},
});
});
});
});

View File

@ -1,10 +1,10 @@
import { getCustomElements, isValidComponent, isValidMetaData } from '@storybook/web-components';
import { ArgTypes } from '@storybook/api';
import { ArgType, ArgTypes } from '@storybook/api';
import { logger } from '@storybook/client-logger';
interface TagItem {
name: string;
type: { text: string };
type: { [key: string]: any };
description: string;
default?: any;
kind?: string;
@ -50,30 +50,57 @@ function mapData(data: TagItem[], category: string) {
return (
data &&
data
.filter((item) => !!item)
.filter((item) => item && item.name)
.reduce((acc, item) => {
if (item.kind === 'method') return acc;
const type =
category === 'properties' ? { name: item.type?.text || item.type } : { name: 'void' };
acc[item.name] = {
name: item.name,
required: false,
description: item.description,
type,
table: {
category,
type: { summary: item.type?.text || item.type },
defaultValue: {
summary: item.default !== undefined ? item.default : item.defaultValue,
},
},
};
switch (category) {
case 'events':
mapEvent(item).forEach((argType) => {
acc[argType.name] = argType;
});
break;
default:
acc[item.name] = mapItem(item, category);
break;
}
return acc;
}, {} as ArgTypes)
);
}
function mapItem(item: TagItem, category: string): ArgType {
const type =
category === 'properties' ? { name: item.type?.text || item.type } : { name: 'void' };
return {
name: item.name,
required: false,
description: item.description,
type,
table: {
category,
type: { summary: item.type?.text || item.type },
defaultValue: {
summary: item.default !== undefined ? item.default : item.defaultValue,
},
},
};
}
function mapEvent(item: TagItem): ArgType[] {
let name = item.name
.replace(/(-|_|:|\.|\s)+(.)?/g, (_match, _separator, chr: string) => {
return chr ? chr.toUpperCase() : '';
})
.replace(/^([A-Z])/, (match) => match.toLowerCase());
name = `on${name.charAt(0).toUpperCase() + name.substr(1)}`;
return [{ name, action: { name: item.name }, table: { disable: true } }, mapItem(item, 'events')];
}
const getMetaDataExperimental = (tagName: string, customElements: CustomElements) => {
if (!isValidComponent(tagName) || !isValidMetaData(customElements)) {
return null;

View File

@ -9,12 +9,30 @@ export default {
component: 'sb-header',
} as Meta;
const Template: Story<SbHeader> = ({ user }) => html`<sb-header .user="${user}"></sb-header>`;
interface SbHeaderProps extends SbHeader {
onSbHeaderCreateAccount: (event: Event) => void;
onSbHeaderLogin: (event: Event) => void;
onSbHeaderLogout: (event: Event) => void;
}
export const LoggedIn: Story<SbHeader> = Template.bind({});
const Template: Story<SbHeaderProps> = ({
user,
onSbHeaderCreateAccount,
onSbHeaderLogin,
onSbHeaderLogout,
}) => {
return html`<sb-header
@sb-header:createAccount=${onSbHeaderCreateAccount}
@sb-header:login=${onSbHeaderLogin}
@sb-header:logout=${onSbHeaderLogout}
.user=${user}
></sb-header>`;
};
export const LoggedIn: Story<SbHeaderProps> = Template.bind({});
LoggedIn.args = {
user: {},
};
export const LoggedOut: Story<SbHeader> = Template.bind({});
export const LoggedOut: Story<SbHeaderProps> = Template.bind({});
LoggedOut.args = {};

View File

@ -104,10 +104,16 @@ export class SbHeader extends LitElement {
private logInOutButton() {
return this.user
? html`<sb-button size="small" @sb-button:click="${() => this.dispatchCustomEvent('logout')} "
label = "Log out" </sb-button>`
: html`<sb-button size="small" @sb-button:click="${() => this.dispatchCustomEvent('login')}"
label="Log in" </sb-button>`;
? html`<sb-button
size="small"
@sb-button:click="${() => this.dispatchCustomEvent('logout')}"
label="Log out"
></sb-button>`
: html`<sb-button
size="small"
@sb-button:click="${() => this.dispatchCustomEvent('login')}"
label="Log in"
></sb-button>`;
}
}