mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-05 06:41:17 +08:00
Merge pull request #19377 from storybookjs/norbert/sb-565-javascript
add template for html-webpack5
This commit is contained in:
commit
a81118ca14
@ -305,7 +305,7 @@ jobs:
|
||||
executor:
|
||||
class: medium+
|
||||
name: sb_node_14_browsers
|
||||
parallelism: 15
|
||||
parallelism: 16
|
||||
steps:
|
||||
- git-shallow-clone/checkout_advanced:
|
||||
clone_options: '--depth 1 --verbose'
|
||||
@ -324,7 +324,7 @@ jobs:
|
||||
executor:
|
||||
class: medium+
|
||||
name: sb_node_14_browsers
|
||||
parallelism: 14
|
||||
parallelism: 15
|
||||
steps:
|
||||
- git-shallow-clone/checkout_advanced:
|
||||
clone_options: '--depth 1 --verbose'
|
||||
@ -339,7 +339,7 @@ jobs:
|
||||
executor:
|
||||
class: medium+
|
||||
name: sb_node_14_browsers
|
||||
parallelism: 15
|
||||
parallelism: 16
|
||||
steps:
|
||||
- git-shallow-clone/checkout_advanced:
|
||||
clone_options: '--depth 1 --verbose'
|
||||
@ -358,7 +358,7 @@ jobs:
|
||||
executor:
|
||||
class: medium+
|
||||
name: sb_node_14_browsers
|
||||
parallelism: 15
|
||||
parallelism: 16
|
||||
steps:
|
||||
- git-shallow-clone/checkout_advanced:
|
||||
clone_options: '--depth 1 --verbose'
|
||||
@ -373,7 +373,7 @@ jobs:
|
||||
executor:
|
||||
class: medium+
|
||||
name: sb_node_14_browsers
|
||||
parallelism: 15
|
||||
parallelism: 16
|
||||
steps:
|
||||
- git-shallow-clone/checkout_advanced:
|
||||
clone_options: '--depth 1 --verbose'
|
||||
@ -388,7 +388,7 @@ jobs:
|
||||
executor:
|
||||
class: medium+
|
||||
name: sb_playwright
|
||||
parallelism: 15
|
||||
parallelism: 16
|
||||
steps:
|
||||
- git-shallow-clone/checkout_advanced:
|
||||
clone_options: '--depth 1 --verbose'
|
||||
|
1
.github/workflows/generate-repros-next.yml
vendored
1
.github/workflows/generate-repros-next.yml
vendored
@ -8,6 +8,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- vite-frameworks-xyz
|
||||
- norbert/sb-565-javascript
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
|
@ -109,6 +109,19 @@ const vue2ViteTemplates = {
|
||||
},
|
||||
};
|
||||
|
||||
const htmlWebpackTemplates = {
|
||||
'html-webpack/default': {
|
||||
name: 'HTML Webpack5',
|
||||
script: 'yarn create webpack5-html .',
|
||||
cadence: ['ci', 'daily', 'weekly'],
|
||||
expected: {
|
||||
framework: '@storybook/html-webpack5',
|
||||
renderer: '@storybook/html',
|
||||
builder: '@storybook/builder-webpack5',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const svelteViteTemplates = {
|
||||
'svelte-vite/default-js': {
|
||||
name: 'Svelte Vite (JS)',
|
||||
@ -188,6 +201,7 @@ export default {
|
||||
...svelteViteTemplates,
|
||||
...litViteTemplates,
|
||||
...vueCliTemplates,
|
||||
...htmlWebpackTemplates,
|
||||
// FIXME: missing documentation.json
|
||||
// 'angular/latest': {
|
||||
// name: 'Angular (latest)',
|
||||
|
@ -33,7 +33,7 @@ export const ChangeArgs = {
|
||||
// Vue3: https://github.com/storybookjs/storybook/issues/13913
|
||||
// Svelte: https://github.com/storybookjs/storybook/issues/19205
|
||||
// Web-components: https://github.com/storybookjs/storybook/issues/19415
|
||||
if (['vue3', 'svelte', 'web-components'].includes(globalThis.storybookRenderer)) return;
|
||||
if (['vue3', 'svelte', 'web-components', 'html'].includes(globalThis.storybookRenderer)) return;
|
||||
|
||||
// When we change the args to the button, it should not rerender
|
||||
await channel.emit('updateStoryArgs', { storyId: id, updatedArgs: { children: 'New Text' } });
|
||||
|
@ -2,4 +2,4 @@ import { parameters as docsParams } from './docs/config';
|
||||
|
||||
export const parameters = { framework: 'html' as const, ...docsParams };
|
||||
export { decorators, argTypesEnhancers } from './docs/config';
|
||||
export { renderToDOM } from './render';
|
||||
export { renderToDOM, render } from './render';
|
||||
|
@ -3,7 +3,7 @@ import { start } from '@storybook/core-client';
|
||||
import type { ClientStoryApi, Loadable } from '@storybook/addons';
|
||||
import type { HtmlFramework, IStorybookSection } from './types';
|
||||
|
||||
import { renderToDOM } from './render';
|
||||
import { renderToDOM, render } from './render';
|
||||
|
||||
const FRAMEWORK = 'html';
|
||||
|
||||
@ -16,7 +16,7 @@ interface ClientApi extends ClientStoryApi<HtmlFramework['storyResult']> {
|
||||
raw: () => any; // todo add type
|
||||
}
|
||||
|
||||
const api = start(renderToDOM);
|
||||
const api = start(renderToDOM, { render });
|
||||
|
||||
export const storiesOf: ClientApi['storiesOf'] = (kind, m) => {
|
||||
return (api.clientApi.storiesOf(kind, m) as ReturnType<ClientApi['storiesOf']>).addParameters({
|
||||
|
@ -5,10 +5,43 @@ import global from 'global';
|
||||
import { dedent } from 'ts-dedent';
|
||||
import { simulatePageLoad, simulateDOMContentLoaded } from '@storybook/preview-web';
|
||||
import type { RenderContext } from '@storybook/store';
|
||||
import { ArgsStoryFn } from '@storybook/csf';
|
||||
import type { HtmlFramework } from './types';
|
||||
|
||||
const { Node } = global;
|
||||
|
||||
export const render: ArgsStoryFn<HtmlFramework> = (args, context) => {
|
||||
const { id, component: Component } = context;
|
||||
|
||||
if (typeof Component === 'string') {
|
||||
let output = Component;
|
||||
Object.keys(args).forEach((key) => {
|
||||
output = output.replace(`{{${key}}}`, args[key]);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
if (Component instanceof HTMLElement) {
|
||||
const output = Component.cloneNode(true) as HTMLElement;
|
||||
Object.keys(args).forEach((key) => {
|
||||
output.setAttribute(
|
||||
key,
|
||||
typeof args[key] === 'string' ? args[key] : JSON.stringify(args[key])
|
||||
);
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
if (typeof Component === 'function') {
|
||||
return Component(args, context);
|
||||
}
|
||||
|
||||
console.warn(dedent`
|
||||
Storybook's HTML renderer only supports rendering DOM elements and strings.
|
||||
Received: ${Component}
|
||||
`);
|
||||
throw new Error(`Unable to render story ${id}`);
|
||||
};
|
||||
|
||||
export function renderToDOM(
|
||||
{ storyFn, kind, name, showMain, showError, forceRemount }: RenderContext<HtmlFramework>,
|
||||
domElement: Element
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { StoryContext as DefaultStoryContext } from '@storybook/csf';
|
||||
import type { ArgsStoryFn, StoryContext as DefaultStoryContext } from '@storybook/csf';
|
||||
import { parameters } from './config';
|
||||
|
||||
export type { RenderContext } from '@storybook/core-client';
|
||||
@ -21,7 +21,7 @@ export interface ShowErrorArgs {
|
||||
}
|
||||
|
||||
export type HtmlFramework = {
|
||||
component: HTMLElement;
|
||||
component: string | HTMLElement | ArgsStoryFn<HtmlFramework>;
|
||||
storyResult: StoryFnHtmlReturnType;
|
||||
};
|
||||
|
||||
|
9
code/renderers/html/template/components/Button.js
Normal file
9
code/renderers/html/template/components/Button.js
Normal file
@ -0,0 +1,9 @@
|
||||
/* eslint-disable no-undef */
|
||||
export const Button = (args) => {
|
||||
const button = document.createElement('button');
|
||||
|
||||
button.innerHTML = args.children;
|
||||
button.addEventListener('click', args.onClick);
|
||||
|
||||
return button;
|
||||
};
|
32
code/renderers/html/template/components/Form.js
Normal file
32
code/renderers/html/template/components/Form.js
Normal file
@ -0,0 +1,32 @@
|
||||
/* eslint-disable no-undef */
|
||||
export const Form = ({ onSuccess }) => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const getInnerHTML = ({ complete }) => `
|
||||
<form id="interaction-test-form">
|
||||
<label>
|
||||
Enter Value
|
||||
<input type="text" data-testid="value" required />
|
||||
</label>
|
||||
<button type="submit">Submit</button>
|
||||
${complete ? '<p>Completed!!</p>' : ''}
|
||||
</form>
|
||||
`;
|
||||
|
||||
container.innerHTML = getInnerHTML({ complete: false });
|
||||
|
||||
const form = container.querySelector('form');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
setTimeout(() => {
|
||||
container.innerHTML = getInnerHTML({ complete: true });
|
||||
}, 500);
|
||||
setTimeout(() => {
|
||||
container.innerHTML = getInnerHTML({ complete: false });
|
||||
}, 1500);
|
||||
onSuccess(e);
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
1
code/renderers/html/template/components/Html.js
Normal file
1
code/renderers/html/template/components/Html.js
Normal file
@ -0,0 +1 @@
|
||||
export const Html = `<div>{{content}}</div>`;
|
15
code/renderers/html/template/components/Pre.js
Normal file
15
code/renderers/html/template/components/Pre.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-disable no-undef */
|
||||
export const Pre = (args) => {
|
||||
const pre = document.createElement('pre');
|
||||
|
||||
pre.setAttribute('data-testid', 'pre');
|
||||
pre.style = args.style;
|
||||
|
||||
if (args.object) {
|
||||
pre.textContent = JSON.stringify(args.object, null, 2);
|
||||
} else {
|
||||
pre.textContent = args.text;
|
||||
}
|
||||
|
||||
return pre;
|
||||
};
|
9
code/renderers/html/template/components/index.js
Normal file
9
code/renderers/html/template/components/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import globalThis from 'global';
|
||||
|
||||
import { Button } from './Button';
|
||||
import { Pre } from './Pre';
|
||||
import { Form } from './Form';
|
||||
import { Html } from './Html';
|
||||
|
||||
globalThis.Components = { Button, Pre, Form, Html };
|
||||
globalThis.storybookRenderer = 'html';
|
1
code/renderers/html/template/stories/README.md
Normal file
1
code/renderers/html/template/stories/README.md
Normal file
@ -0,0 +1 @@
|
||||
Placeholder until we write some render-specific stories
|
@ -24,11 +24,12 @@ const OUTPUT_DIRECTORY = join(__dirname, '..', '..', 'repros');
|
||||
const BEFORE_DIR_NAME = 'before-storybook';
|
||||
const AFTER_DIR_NAME = 'after-storybook';
|
||||
|
||||
const sbInit = async (cwd: string) => {
|
||||
const sbInit = async (cwd: string, flags?: string[]) => {
|
||||
const sbCliBinaryPath = join(__dirname, `../../code/lib/cli/bin/index.js`);
|
||||
console.log(`🎁 Installing storybook`);
|
||||
const env = { STORYBOOK_DISABLE_TELEMETRY: 'true' };
|
||||
await runCommand(`${sbCliBinaryPath} init --yes`, { cwd, env });
|
||||
const fullFlags = ['--yes', ...(flags || [])];
|
||||
await runCommand(`${sbCliBinaryPath} init ${fullFlags.join(' ')}`, { cwd, env });
|
||||
};
|
||||
|
||||
const LOCAL_REGISTRY_URL = 'http://localhost:6001';
|
||||
@ -44,7 +45,7 @@ const withLocalRegistry = async (packageManager: JsPackageManager, action: () =>
|
||||
}
|
||||
};
|
||||
|
||||
const addStorybook = async (baseDir: string, localRegistry: boolean) => {
|
||||
const addStorybook = async (baseDir: string, localRegistry: boolean, flags?: string[]) => {
|
||||
const beforeDir = join(baseDir, BEFORE_DIR_NAME);
|
||||
const afterDir = join(baseDir, AFTER_DIR_NAME);
|
||||
const tmpDir = join(baseDir, 'tmp');
|
||||
@ -59,10 +60,10 @@ const addStorybook = async (baseDir: string, localRegistry: boolean) => {
|
||||
await withLocalRegistry(packageManager, async () => {
|
||||
packageManager.addPackageResolutions(storybookVersions);
|
||||
|
||||
await sbInit(tmpDir);
|
||||
await sbInit(tmpDir, flags);
|
||||
});
|
||||
} else {
|
||||
await sbInit(tmpDir);
|
||||
await sbInit(tmpDir, flags);
|
||||
}
|
||||
await rename(tmpDir, afterDir);
|
||||
};
|
||||
@ -105,15 +106,15 @@ const runGenerators = async (
|
||||
|
||||
let controller: AbortController;
|
||||
if (localRegistry) {
|
||||
// @ts-expect-error (Converted from ts-ignore)
|
||||
await publish.run();
|
||||
console.log(`⚙️ Starting local registry: ${LOCAL_REGISTRY_URL}`);
|
||||
controller = await runRegistry({ debug: true });
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
generators.map(({ dirName, name, script }) =>
|
||||
generators.map(({ dirName, name, script, expected }) =>
|
||||
limit(async () => {
|
||||
const flags = expected.renderer === '@storybook/html' ? ['--type html'] : [];
|
||||
|
||||
const time = process.hrtime();
|
||||
console.log(`🧬 generating ${name}`);
|
||||
|
||||
@ -129,7 +130,7 @@ const runGenerators = async (
|
||||
|
||||
await localizeYarnConfigFiles(baseDir, beforeDir);
|
||||
|
||||
await addStorybook(baseDir, localRegistry);
|
||||
await addStorybook(baseDir, localRegistry, flags);
|
||||
|
||||
await addDocumentation(baseDir, { name, dirName });
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
export type GeneratorConfig = {
|
||||
name: string;
|
||||
script: string;
|
||||
cadence: ('ci' | 'daily' | 'weekly')[];
|
||||
expected: {
|
||||
framework: string;
|
||||
renderer: string;
|
||||
builder: string;
|
||||
};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user