@aurelia/storybook 1.0.0 → 1.0.2
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.
- package/README.md +29 -33
- package/__tests__/render.test.ts +1 -1
- package/apps/hello-world/.storybook/main.ts +32 -12
- package/apps/hello-world/package-lock.json +7448 -0
- package/apps/hello-world/package.json +10 -10
- package/apps/hello-world/src/stories/hello-world.stories.ts +11 -10
- package/apps/hello-world-webpack/.storybook/main.ts +1 -4
- package/apps/hello-world-webpack/package-lock.json +380 -3515
- package/apps/hello-world-webpack/package.json +9 -10
- package/apps/hello-world-webpack/src/hello-world.html +6 -0
- package/apps/hello-world-webpack/src/hello-world.ts +17 -0
- package/apps/hello-world-webpack/src/my-app.stories.ts +10 -7
- package/apps/hello-world-webpack/src/stories/hello-world.stories.ts +54 -0
- package/dist/index.js +65 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -39
- package/dist/index.mjs.map +1 -1
- package/dist/preset.js +15 -1
- package/dist/preset.js.map +1 -1
- package/dist/preset.mjs +15 -1
- package/dist/preset.mjs.map +1 -1
- package/dist/preview/render.js +41 -40
- package/dist/preview/render.js.map +1 -1
- package/dist/preview/render.mjs +41 -40
- package/dist/preview/render.mjs.map +1 -1
- package/dist/preview.js +81 -39
- package/dist/preview.js.map +1 -1
- package/dist/preview.mjs +81 -40
- package/dist/preview.mjs.map +1 -1
- package/package.json +6 -10
- package/rollup.config.mjs +6 -4
- package/src/index.ts +28 -0
- package/src/preset.ts +19 -1
- package/src/preview/render.ts +51 -49
- package/src/preview/types.ts +1 -1
- package/src/preview.ts +1 -50
package/dist/preview.mjs
CHANGED
|
@@ -1,7 +1,84 @@
|
|
|
1
|
-
import '@storybook/core-events';
|
|
2
1
|
import Aurelia, { CustomElement } from 'aurelia';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
// Track Aurelia apps for cleanup
|
|
4
|
+
const appMap = new Map();
|
|
5
|
+
async function teardown(element) {
|
|
6
|
+
if (appMap.has(element)) {
|
|
7
|
+
const app = appMap.get(element);
|
|
8
|
+
if (app) {
|
|
9
|
+
await app.stop();
|
|
10
|
+
appMap.delete(element);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const render = (args, context) => {
|
|
15
|
+
const { id, component: Component } = context;
|
|
16
|
+
if (!Component) {
|
|
17
|
+
throw new Error(`Unable to render story ${id} as the component annotation is missing from the default export`);
|
|
18
|
+
}
|
|
19
|
+
return { Component, props: args, template: '' };
|
|
20
|
+
};
|
|
21
|
+
async function renderToCanvas({ storyFn, title, name, showMain, showError, storyContext, forceRemount, }, canvasElement, bootstrapAppFn) {
|
|
22
|
+
// Store reference to the original storybook root element
|
|
23
|
+
const rootElement = canvasElement;
|
|
24
|
+
// Ensure we have (or create) a single container inside the root where the Aurelia app actually renders
|
|
25
|
+
let hostElement;
|
|
26
|
+
if (rootElement.id === 'storybook-root') {
|
|
27
|
+
hostElement = rootElement.querySelector('.aurelia-story-container');
|
|
28
|
+
if (!hostElement) {
|
|
29
|
+
hostElement = document.createElement('div');
|
|
30
|
+
hostElement.className = 'aurelia-story-container';
|
|
31
|
+
hostElement.style.height = '100%';
|
|
32
|
+
rootElement.appendChild(hostElement);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
hostElement = rootElement;
|
|
37
|
+
}
|
|
38
|
+
// All app instances are now tracked by the *root* element, ensuring we only ever have one per story iframe
|
|
39
|
+
const appBootstrapFn = bootstrapAppFn ?? createAureliaApp;
|
|
40
|
+
const { parameters, component, args } = storyContext;
|
|
41
|
+
let app = appMap.get(rootElement);
|
|
42
|
+
const story = storyFn();
|
|
43
|
+
// Temporary debug logging
|
|
44
|
+
console.log(`[DEBUG] Story: ${name}, forceRemount: ${forceRemount}, hasExistingApp: ${!!app}, canvasId: ${canvasElement.className}`);
|
|
45
|
+
if (!story) {
|
|
46
|
+
showError({
|
|
47
|
+
title: `Expecting an Aurelia component from the story: "${name}" of "${title}".`,
|
|
48
|
+
description: `
|
|
49
|
+
Did you forget to return the Aurelia component from the story?
|
|
50
|
+
Use "() => ({ template: '<custom-component></custom-component>' })" when defining the story.
|
|
51
|
+
`,
|
|
52
|
+
});
|
|
53
|
+
return () => { };
|
|
54
|
+
}
|
|
55
|
+
showMain();
|
|
56
|
+
if (!app || forceRemount) {
|
|
57
|
+
if (forceRemount && app) {
|
|
58
|
+
await teardown(rootElement);
|
|
59
|
+
app = undefined;
|
|
60
|
+
}
|
|
61
|
+
// Clear container before mounting new app
|
|
62
|
+
hostElement.innerHTML = '';
|
|
63
|
+
const mergedProps = { ...parameters?.args, ...args, ...story.props };
|
|
64
|
+
const aureliaApp = appBootstrapFn(story, mergedProps, hostElement, component);
|
|
65
|
+
await aureliaApp.start();
|
|
66
|
+
appMap.set(rootElement, aureliaApp);
|
|
67
|
+
app = aureliaApp;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// update existing app props
|
|
71
|
+
const mergedProps = { ...parameters?.args, ...args, ...story.props };
|
|
72
|
+
if (app?.root?.controller?.viewModel) {
|
|
73
|
+
Object.assign(app.root.controller.viewModel, mergedProps);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Return cleanup fn
|
|
77
|
+
return async () => {
|
|
78
|
+
await teardown(rootElement);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createAureliaApp(story, args, domElement, component) {
|
|
5
82
|
const aurelia = new Aurelia(story.container);
|
|
6
83
|
if (story.items?.length) {
|
|
7
84
|
aurelia.register(...story.items);
|
|
@@ -15,7 +92,7 @@ function bootstrapAureliaApp(story, args, domElement, component) {
|
|
|
15
92
|
aurelia.register(component);
|
|
16
93
|
}
|
|
17
94
|
const App = CustomElement.define({
|
|
18
|
-
name: '
|
|
95
|
+
name: 'sb-app',
|
|
19
96
|
template,
|
|
20
97
|
containerless: true,
|
|
21
98
|
}, class {
|
|
@@ -33,41 +110,5 @@ function createComponentTemplate(component, innerHtml) {
|
|
|
33
110
|
.join(' ')}>${innerHtml ?? ''}</${def.name}>`;
|
|
34
111
|
}
|
|
35
112
|
|
|
36
|
-
|
|
37
|
-
let currentCleanup = null;
|
|
38
|
-
const render = (args, context) => {
|
|
39
|
-
// Clean up previous story if exists
|
|
40
|
-
if (currentCleanup) {
|
|
41
|
-
currentCleanup();
|
|
42
|
-
currentCleanup = null;
|
|
43
|
-
}
|
|
44
|
-
// Create a container element
|
|
45
|
-
const container = document.createElement('div');
|
|
46
|
-
// Get the story function result
|
|
47
|
-
const story = context.storyFn();
|
|
48
|
-
// Bootstrap Aurelia app immediately
|
|
49
|
-
if (story && (story.Component || story.template)) {
|
|
50
|
-
const app = bootstrapAureliaApp(story, args, container, story.Component || context.component);
|
|
51
|
-
// Start the app asynchronously
|
|
52
|
-
const startPromise = app.start();
|
|
53
|
-
if (startPromise && typeof startPromise.catch === 'function') {
|
|
54
|
-
startPromise.catch((error) => {
|
|
55
|
-
console.error('Failed to start Aurelia app:', error);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
// Set cleanup function
|
|
59
|
-
currentCleanup = () => {
|
|
60
|
-
const stopPromise = app.stop();
|
|
61
|
-
if (stopPromise && typeof stopPromise.catch === 'function') {
|
|
62
|
-
stopPromise.catch((error) => {
|
|
63
|
-
console.error('Failed to stop Aurelia app:', error);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
// Return the container element immediately
|
|
69
|
-
return container;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export { render };
|
|
113
|
+
export { render, renderToCanvas };
|
|
73
114
|
//# sourceMappingURL=preview.mjs.map
|
package/dist/preview.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.mjs","sources":["../src/preview/render.ts"
|
|
1
|
+
{"version":3,"file":"preview.mjs","sources":["../src/preview/render.ts"],"sourcesContent":["import { STORY_CHANGED } from 'storybook/internal/core-events';\nimport type { RenderContext, ArgsStoryFn } from 'storybook/internal/types';\nimport type { AureliaRenderer } from './types';\nimport Aurelia, { Constructable, CustomElement } from 'aurelia';\n\ninterface AureliaStoryResult {\n template: string;\n components?: unknown[];\n Component?: unknown;\n container?: any;\n items?: unknown[];\n innerHtml?: string;\n props?: Record<string, any>;\n}\n\n// Track Aurelia apps for cleanup\nconst appMap = new Map<HTMLElement, any>();\n\nasync function teardown(element: HTMLElement) {\n if (appMap.has(element)) {\n const app = appMap.get(element);\n if (app) {\n await app.stop();\n appMap.delete(element);\n }\n }\n}\n\nexport const render: ArgsStoryFn<AureliaRenderer> = (args, context) => {\n const { id, component: Component } = context;\n \n if (!Component) {\n throw new Error(\n `Unable to render story ${id} as the component annotation is missing from the default export`\n );\n }\n return { Component, props: args, template: '' };\n};\n\nexport async function renderToCanvas(\n {\n storyFn,\n title,\n name,\n showMain,\n showError,\n storyContext,\n forceRemount,\n }: RenderContext<AureliaRenderer>,\n canvasElement: HTMLElement,\n bootstrapAppFn?: typeof createAureliaApp\n) {\n // Store reference to the original storybook root element\n const rootElement = canvasElement;\n\n // Ensure we have (or create) a single container inside the root where the Aurelia app actually renders\n let hostElement: HTMLElement;\n if (rootElement.id === 'storybook-root') {\n hostElement = rootElement.querySelector('.aurelia-story-container') as HTMLElement;\n if (!hostElement) {\n hostElement = document.createElement('div');\n hostElement.className = 'aurelia-story-container';\n hostElement.style.height = '100%';\n rootElement.appendChild(hostElement);\n }\n } else {\n hostElement = rootElement;\n }\n\n // All app instances are now tracked by the *root* element, ensuring we only ever have one per story iframe\n const appBootstrapFn = bootstrapAppFn ?? createAureliaApp;\n const { parameters, component, args } = storyContext;\n \n let app = appMap.get(rootElement);\n const story = storyFn() as AureliaStoryResult;\n \n // Temporary debug logging\n console.log(`[DEBUG] Story: ${name}, forceRemount: ${forceRemount}, hasExistingApp: ${!!app}, canvasId: ${canvasElement.className}`);\n\n if (!story) {\n showError({\n title: `Expecting an Aurelia component from the story: \"${name}\" of \"${title}\".`,\n description: `\n Did you forget to return the Aurelia component from the story?\n Use \"() => ({ template: '<custom-component></custom-component>' })\" when defining the story.\n `,\n });\n return () => {};\n }\n\n showMain();\n\n if (!app || forceRemount) {\n if (forceRemount && app) {\n await teardown(rootElement);\n app = undefined;\n }\n // Clear container before mounting new app\n hostElement.innerHTML = '';\n\n const mergedProps = { ...parameters?.args, ...args, ...story.props };\n\n const aureliaApp = appBootstrapFn(\n story,\n mergedProps,\n hostElement,\n component as Constructable\n );\n await aureliaApp.start();\n appMap.set(rootElement, aureliaApp);\n app = aureliaApp;\n } else {\n // update existing app props\n const mergedProps = { ...parameters?.args, ...args, ...story.props };\n if (app?.root?.controller?.viewModel) {\n Object.assign(app.root.controller.viewModel, mergedProps);\n }\n }\n\n // Return cleanup fn\n return async () => {\n await teardown(rootElement);\n };\n}\n\nexport function createAureliaApp(\n story: AureliaStoryResult,\n args: Record<string, any>,\n domElement: HTMLElement,\n component?: Constructable\n) {\n const aurelia = new Aurelia(story.container);\n\n if (story.items?.length) {\n aurelia.register(...story.items);\n }\n\n if (story.components?.length) {\n aurelia.register(...story.components);\n }\n\n let { template } = story;\n\n if (component) {\n template = template ?? createComponentTemplate(component, story.innerHtml);\n aurelia.register(component);\n }\n\n const App = CustomElement.define(\n {\n name: 'sb-app',\n template,\n containerless: true,\n },\n class {}\n );\n\n const app = Object.assign(new App(), args);\n\n return aurelia.app({\n host: domElement,\n component: app,\n });\n}\n\nexport function createComponentTemplate(\n component: Constructable,\n innerHtml?: string\n): string {\n const def = CustomElement.getDefinition(component);\n\n return `<${def.name} ${Object.values(def.bindables)\n .map((bindable) => `${bindable.attribute}.bind=\"${bindable.name}\"`)\n .join(' ')}>${innerHtml ?? ''}</${def.name}>`;\n}"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurelia/storybook",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A Storybook plugin to render Aurelia 2 components using Vite or Webpack",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,12 +17,10 @@
|
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"@aurelia/runtime-html": "^2.0.0-beta.24",
|
|
19
19
|
"@aurelia/vite-plugin": "^2.0.0-beta.24",
|
|
20
|
-
"@storybook/
|
|
21
|
-
"@storybook/builder-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"@storybook/core-events": "^8.6.14",
|
|
25
|
-
"aurelia": "^2.0.0-beta.24"
|
|
20
|
+
"@storybook/builder-vite": "^9.0.0",
|
|
21
|
+
"@storybook/builder-webpack5": "^9.0.0",
|
|
22
|
+
"aurelia": "^2.0.0-beta.24",
|
|
23
|
+
"storybook": "^9.0.0"
|
|
26
24
|
},
|
|
27
25
|
"devDependencies": {
|
|
28
26
|
"@aurelia/webpack-loader": "^2.0.0-beta.24",
|
|
@@ -30,9 +28,7 @@
|
|
|
30
28
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
31
29
|
"@rollup/plugin-typescript": "^12.1.3",
|
|
32
30
|
"@storybook/builder-vite": "^9.0.12",
|
|
33
|
-
"
|
|
34
|
-
"@storybook/preview-api": "^8.6.14",
|
|
35
|
-
"@storybook/types": "^8.6.14",
|
|
31
|
+
"storybook": "^9.0.12",
|
|
36
32
|
"@types/jest": "^30.0.0",
|
|
37
33
|
"glob": "^11.0.3",
|
|
38
34
|
"jest": "^30.0.2",
|
package/rollup.config.mjs
CHANGED
|
@@ -6,11 +6,13 @@ import { glob } from 'glob';
|
|
|
6
6
|
const external = [
|
|
7
7
|
'@aurelia/runtime-html',
|
|
8
8
|
'@aurelia/vite-plugin',
|
|
9
|
-
'@storybook/addons',
|
|
10
9
|
'@storybook/builder-vite',
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
10
|
+
'aurelia',
|
|
11
|
+
'react',
|
|
12
|
+
'react-dom',
|
|
13
|
+
'storybook/internal/core-events',
|
|
14
|
+
'storybook/internal/types',
|
|
15
|
+
'storybook/theming'
|
|
14
16
|
];
|
|
15
17
|
|
|
16
18
|
// Get all TypeScript files from src directory
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
|
+
import type { StorybookConfig } from 'storybook/internal/types';
|
|
1
2
|
import { renderToCanvas } from './preview/render';
|
|
2
3
|
|
|
3
4
|
export { renderToCanvas };
|
|
4
5
|
export const render = renderToCanvas;
|
|
6
|
+
|
|
7
|
+
// Define the framework
|
|
8
|
+
export const framework = {
|
|
9
|
+
name: '@aurelia/storybook',
|
|
10
|
+
options: {}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Framework configuration for Storybook
|
|
14
|
+
export const frameworkOptions = {
|
|
15
|
+
builder: {
|
|
16
|
+
name: '@storybook/builder-vite',
|
|
17
|
+
options: {}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Export a complete framework configuration
|
|
22
|
+
export const aureliaFramework = {
|
|
23
|
+
name: '@aurelia/storybook',
|
|
24
|
+
options: {},
|
|
25
|
+
builder: '@storybook/builder-vite'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Provide external dependencies configuration
|
|
29
|
+
export const externals = {
|
|
30
|
+
'react': 'React',
|
|
31
|
+
'react-dom': 'ReactDOM'
|
|
32
|
+
};
|
package/src/preset.ts
CHANGED
|
@@ -7,7 +7,25 @@ import { getRules } from './webpack';
|
|
|
7
7
|
* Optionally adjust the Vite configuration.
|
|
8
8
|
*/
|
|
9
9
|
export async function viteFinal(config: any): Promise<any> {
|
|
10
|
-
//
|
|
10
|
+
// Configure Vite to properly handle dependencies
|
|
11
|
+
config.define = config.define || {};
|
|
12
|
+
config.define['process.env.NODE_ENV'] = JSON.stringify(process.env.NODE_ENV || 'development');
|
|
13
|
+
|
|
14
|
+
// Configure optimization deps
|
|
15
|
+
config.optimizeDeps = config.optimizeDeps || {};
|
|
16
|
+
config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
|
|
17
|
+
|
|
18
|
+
// Only exclude Aurelia-specific dependencies that cause issues
|
|
19
|
+
const excludeList = [
|
|
20
|
+
'@aurelia/runtime-html'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
excludeList.forEach(dep => {
|
|
24
|
+
if (!config.optimizeDeps.exclude.includes(dep)) {
|
|
25
|
+
config.optimizeDeps.exclude.push(dep);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
11
29
|
return config;
|
|
12
30
|
}
|
|
13
31
|
|
package/src/preview/render.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { STORY_CHANGED } from '
|
|
2
|
-
import type { RenderContext, ArgsStoryFn } from '
|
|
1
|
+
import { STORY_CHANGED } from 'storybook/internal/core-events';
|
|
2
|
+
import type { RenderContext, ArgsStoryFn } from 'storybook/internal/types';
|
|
3
3
|
import type { AureliaRenderer } from './types';
|
|
4
4
|
import Aurelia, { Constructable, CustomElement } from 'aurelia';
|
|
5
5
|
|
|
@@ -13,18 +13,8 @@ interface AureliaStoryResult {
|
|
|
13
13
|
props?: Record<string, any>;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* Merges multiple sources into a single object.
|
|
18
|
-
* Sources can be story parameters, args, or story.props.
|
|
19
|
-
*/
|
|
20
|
-
function mergeStoryProps(
|
|
21
|
-
...sources: Array<Record<string, any> | undefined>
|
|
22
|
-
): Record<string, any> {
|
|
23
|
-
return Object.assign({}, ...sources);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
16
|
// Track Aurelia apps for cleanup
|
|
27
|
-
const appMap = new Map<HTMLElement,
|
|
17
|
+
const appMap = new Map<HTMLElement, any>();
|
|
28
18
|
|
|
29
19
|
async function teardown(element: HTMLElement) {
|
|
30
20
|
if (appMap.has(element)) {
|
|
@@ -36,7 +26,9 @@ async function teardown(element: HTMLElement) {
|
|
|
36
26
|
}
|
|
37
27
|
}
|
|
38
28
|
|
|
39
|
-
export const render: ArgsStoryFn<AureliaRenderer> = (args,
|
|
29
|
+
export const render: ArgsStoryFn<AureliaRenderer> = (args, context) => {
|
|
30
|
+
const { id, component: Component } = context;
|
|
31
|
+
|
|
40
32
|
if (!Component) {
|
|
41
33
|
throw new Error(
|
|
42
34
|
`Unable to render story ${id} as the component annotation is missing from the default export`
|
|
@@ -56,14 +48,34 @@ export async function renderToCanvas(
|
|
|
56
48
|
forceRemount,
|
|
57
49
|
}: RenderContext<AureliaRenderer>,
|
|
58
50
|
canvasElement: HTMLElement,
|
|
59
|
-
bootstrapAppFn?: typeof
|
|
51
|
+
bootstrapAppFn?: typeof createAureliaApp
|
|
60
52
|
) {
|
|
61
|
-
|
|
53
|
+
// Store reference to the original storybook root element
|
|
54
|
+
const rootElement = canvasElement;
|
|
55
|
+
|
|
56
|
+
// Ensure we have (or create) a single container inside the root where the Aurelia app actually renders
|
|
57
|
+
let hostElement: HTMLElement;
|
|
58
|
+
if (rootElement.id === 'storybook-root') {
|
|
59
|
+
hostElement = rootElement.querySelector('.aurelia-story-container') as HTMLElement;
|
|
60
|
+
if (!hostElement) {
|
|
61
|
+
hostElement = document.createElement('div');
|
|
62
|
+
hostElement.className = 'aurelia-story-container';
|
|
63
|
+
hostElement.style.height = '100%';
|
|
64
|
+
rootElement.appendChild(hostElement);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
hostElement = rootElement;
|
|
68
|
+
}
|
|
62
69
|
|
|
70
|
+
// All app instances are now tracked by the *root* element, ensuring we only ever have one per story iframe
|
|
71
|
+
const appBootstrapFn = bootstrapAppFn ?? createAureliaApp;
|
|
63
72
|
const { parameters, component, args } = storyContext;
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
|
|
74
|
+
let app = appMap.get(rootElement);
|
|
66
75
|
const story = storyFn() as AureliaStoryResult;
|
|
76
|
+
|
|
77
|
+
// Temporary debug logging
|
|
78
|
+
console.log(`[DEBUG] Story: ${name}, forceRemount: ${forceRemount}, hasExistingApp: ${!!app}, canvasId: ${canvasElement.className}`);
|
|
67
79
|
|
|
68
80
|
if (!story) {
|
|
69
81
|
showError({
|
|
@@ -78,50 +90,40 @@ export async function renderToCanvas(
|
|
|
78
90
|
|
|
79
91
|
showMain();
|
|
80
92
|
|
|
81
|
-
let mergedProps;
|
|
82
|
-
// Use full merge (including story.props) when bootstrapping a new app or force remounting.
|
|
83
93
|
if (!app || forceRemount) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
if (forceRemount && app) {
|
|
95
|
+
await teardown(rootElement);
|
|
96
|
+
app = undefined;
|
|
87
97
|
}
|
|
88
|
-
|
|
98
|
+
// Clear container before mounting new app
|
|
99
|
+
hostElement.innerHTML = '';
|
|
100
|
+
|
|
101
|
+
const mergedProps = { ...parameters?.args, ...args, ...story.props };
|
|
102
|
+
|
|
103
|
+
const aureliaApp = appBootstrapFn(
|
|
89
104
|
story,
|
|
90
105
|
mergedProps,
|
|
91
|
-
|
|
106
|
+
hostElement,
|
|
92
107
|
component as Constructable
|
|
93
|
-
)
|
|
94
|
-
await
|
|
95
|
-
appMap.set(
|
|
108
|
+
);
|
|
109
|
+
await aureliaApp.start();
|
|
110
|
+
appMap.set(rootElement, aureliaApp);
|
|
111
|
+
app = aureliaApp;
|
|
96
112
|
} else {
|
|
97
|
-
//
|
|
98
|
-
mergedProps =
|
|
99
|
-
if (app
|
|
113
|
+
// update existing app props
|
|
114
|
+
const mergedProps = { ...parameters?.args, ...args, ...story.props };
|
|
115
|
+
if (app?.root?.controller?.viewModel) {
|
|
100
116
|
Object.assign(app.root.controller.viewModel, mergedProps);
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
104
|
-
//
|
|
105
|
-
const channel = storyContext.viewMode === 'story' ? storyContext.channel : null;
|
|
106
|
-
let onStoryChange: () => void;
|
|
107
|
-
if (channel) {
|
|
108
|
-
onStoryChange = () => {
|
|
109
|
-
// When the story changes, clean up the Aurelia app
|
|
110
|
-
teardown(canvasElement);
|
|
111
|
-
};
|
|
112
|
-
channel.on(STORY_CHANGED, onStoryChange);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Return teardown function that also unsubscribes from STORY_CHANGED
|
|
120
|
+
// Return cleanup fn
|
|
116
121
|
return async () => {
|
|
117
|
-
|
|
118
|
-
channel.off(STORY_CHANGED, onStoryChange);
|
|
119
|
-
}
|
|
120
|
-
await teardown(canvasElement);
|
|
122
|
+
await teardown(rootElement);
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
export function
|
|
126
|
+
export function createAureliaApp(
|
|
125
127
|
story: AureliaStoryResult,
|
|
126
128
|
args: Record<string, any>,
|
|
127
129
|
domElement: HTMLElement,
|
|
@@ -146,7 +148,7 @@ export function bootstrapAureliaApp(
|
|
|
146
148
|
|
|
147
149
|
const App = CustomElement.define(
|
|
148
150
|
{
|
|
149
|
-
name: '
|
|
151
|
+
name: 'sb-app',
|
|
150
152
|
template,
|
|
151
153
|
containerless: true,
|
|
152
154
|
},
|
package/src/preview/types.ts
CHANGED
package/src/preview.ts
CHANGED
|
@@ -1,50 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import Aurelia from 'aurelia';
|
|
3
|
-
|
|
4
|
-
// Track the current story's cleanup function
|
|
5
|
-
let currentCleanup: (() => void) | null = null;
|
|
6
|
-
|
|
7
|
-
export const render = (args: any, context: any) => {
|
|
8
|
-
// Clean up previous story if exists
|
|
9
|
-
if (currentCleanup) {
|
|
10
|
-
currentCleanup();
|
|
11
|
-
currentCleanup = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Create a container element
|
|
15
|
-
const container = document.createElement('div');
|
|
16
|
-
|
|
17
|
-
// Get the story function result
|
|
18
|
-
const story = context.storyFn();
|
|
19
|
-
|
|
20
|
-
// Bootstrap Aurelia app immediately
|
|
21
|
-
if (story && (story.Component || story.template)) {
|
|
22
|
-
const app = bootstrapAureliaApp(
|
|
23
|
-
story,
|
|
24
|
-
args,
|
|
25
|
-
container,
|
|
26
|
-
story.Component || context.component
|
|
27
|
-
) as Aurelia;
|
|
28
|
-
|
|
29
|
-
// Start the app asynchronously
|
|
30
|
-
const startPromise = app.start();
|
|
31
|
-
if (startPromise && typeof startPromise.catch === 'function') {
|
|
32
|
-
startPromise.catch((error: any) => {
|
|
33
|
-
console.error('Failed to start Aurelia app:', error);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Set cleanup function
|
|
38
|
-
currentCleanup = () => {
|
|
39
|
-
const stopPromise = app.stop();
|
|
40
|
-
if (stopPromise && typeof stopPromise.catch === 'function') {
|
|
41
|
-
stopPromise.catch((error: any) => {
|
|
42
|
-
console.error('Failed to stop Aurelia app:', error);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Return the container element immediately
|
|
49
|
-
return container;
|
|
50
|
-
};
|
|
1
|
+
export { renderToCanvas, render } from './preview/render';
|