Skip to content

Commit 7fdb9bf

Browse files
committed
wip
1 parent f944571 commit 7fdb9bf

File tree

8 files changed

+81
-102
lines changed

8 files changed

+81
-102
lines changed

README.md

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,15 @@ To render a component preview, use the `<ComponentPreviewArea />` component in y
5252
</template>
5353
```
5454

55-
### Using the App Loader (Recommended)
55+
### Using the App Loader
5656

57-
The simplest way to enable component preview in an external HTML page is using the app-loader script. This single script automatically handles all the setup:
57+
The app-loader script automatically sets up everything needed for component preview:
5858

5959
```html
6060
<!DOCTYPE html>
6161
<html>
6262
<head>
6363
<title>Component Preview</title>
64-
<!-- Single script that handles everything -->
6564
<script src="/nuxt-component-preview/app-loader.js"></script>
6665
</head>
6766
<body>
@@ -72,10 +71,14 @@ The simplest way to enable component preview in an external HTML page is using t
7271
<div id="preview-target-2"></div>
7372

7473
<script>
75-
// Wait for the preview system to be ready
76-
window.addEventListener('nuxt-component-preview:ready', (event) => {
77-
const { nuxtApp } = event.detail;
78-
74+
// Helper function that handles both sync and async cases
75+
const onNuxtComponentPreviewReady = (callback) =>
76+
window.__nuxtComponentPreviewApp
77+
? callback(window.__nuxtComponentPreviewApp)
78+
: window.addEventListener('nuxt-component-preview:ready', event => callback(event.detail.nuxtApp), { once: true })
79+
80+
// Use the helper
81+
onNuxtComponentPreviewReady((nuxtApp) => {
7982
// Render components into targets
8083
nuxtApp.$previewComponent('MyComponent', { prop: 'value' }, '#preview-target-1');
8184
nuxtApp.$previewComponent('OtherComponent', { data: 123 }, '#preview-target-2');
@@ -85,33 +88,47 @@ The simplest way to enable component preview in an external HTML page is using t
8588
</html>
8689
```
8790

88-
The app-loader script automatically:
89-
- Creates the necessary DOM containers (`#__nuxt` and `#teleports`)
90-
- Sets up the runtime configuration with `componentPreview: true`
91-
- Loads the Nuxt entry module
92-
- Fires the `nuxt-component-preview:ready` event when ready
91+
See [playground/public/preview-test-loader.html](./playground/public/preview-test-loader.html) for a working example.
9392

94-
See [playground/public/preview-test-loader.html](./playground/public/preview-test-loader.html) for a complete working example.
93+
### API Reference
9594

96-
This setup is ideal for integrating with a Drupal backend (or any backend) that needs to render Nuxt components in isolation, such as for CMS previews or design systems.
95+
#### `nuxt-component-preview:ready` Event
9796

98-
### API Reference
97+
Fired when the Nuxt app is ready for component preview:
98+
99+
```javascript
100+
window.addEventListener('nuxt-component-preview:ready', (event) => {
101+
const { nuxtApp } = event.detail;
102+
// Use nuxtApp.$previewComponent() here
103+
});
104+
```
105+
106+
#### Helper Function Pattern
107+
108+
For convenience, you can use this helper that works whether Nuxt is already ready or still loading:
109+
110+
```javascript
111+
const onNuxtComponentPreviewReady = (callback) =>
112+
window.__nuxtComponentPreviewApp
113+
? callback(window.__nuxtComponentPreviewApp)
114+
: window.addEventListener('nuxt-component-preview:ready', event => callback(event.detail.nuxtApp), { once: true })
115+
116+
// Usage
117+
onNuxtComponentPreviewReady((nuxtApp) => {
118+
nuxtApp.$previewComponent('MyComponent', props, '#target');
119+
});
120+
```
99121

100122
#### `$previewComponent(componentName, props, targetSelector)`
101123

102-
The `$previewComponent` method is available on the Nuxt app instance after the `nuxt-component-preview:ready` event fires:
124+
Renders a Vue component to a target element:
103125

104-
- **componentName** (string): Name of the registered Vue component to render
126+
- **componentName** (string): Name of the registered Vue component
105127
- **props** (object): Props to pass to the component
106-
- **targetSelector** (string | Element): CSS selector or DOM element where the component will be rendered
128+
- **targetSelector** (string | Element): CSS selector or DOM element
107129

108130
```javascript
109-
// Example usage
110-
nuxtApp.$previewComponent(
111-
'TestCard',
112-
{ title: 'My Card', description: 'Card content' },
113-
'#preview-target'
114-
);
131+
nuxtApp.$previewComponent('TestCard', { title: 'My Card' }, '#preview-target');
115132
```
116133

117134
## Testing

playground/public/preview-test-loader.html

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ <h2>Preview Targets</h2>
5858

5959
<!-- Test script that runs when Nuxt is ready -->
6060
<script>
61-
window.addEventListener('nuxt-component-preview:ready', (event) => {
62-
const { nuxtApp } = event.detail;
61+
// Helper function to handle both sync and async cases
62+
const onNuxtComponentPreviewReady = (callback) => window.__nuxtComponentPreviewApp ? callback(window.__nuxtComponentPreviewApp) : window.addEventListener('nuxt-component-preview:ready', event => callback(event.detail.nuxtApp), { once: true })
6363

64-
console.log('Nuxt Component Preview is ready via loader!');
64+
// Use the helper
65+
onNuxtComponentPreviewReady((nuxtApp) => {
66+
console.log('Nuxt Component Preview is ready!');
6567

6668
// Preview TestMarkup component
6769
nuxtApp.$previewComponent(
@@ -97,16 +99,6 @@ <h2>HTML Content via Loader</h2>
9799
},
98100
'#preview-target-3'
99101
);
100-
101-
// Log success
102-
console.log('All components rendered successfully using the loader!');
103-
});
104-
105-
// Error handling
106-
window.addEventListener('error', (event) => {
107-
if (event.message && event.message.includes('nuxt')) {
108-
console.error('Nuxt error:', event);
109-
}
110102
});
111103
</script>
112104
</body>

playground/public/preview-test.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ <h1>Nuxt Component Preview Test (Manual Setup - Dev Only)</h1>
6262

6363
<!-- Test script that runs when Nuxt is ready -->
6464
<script>
65-
window.addEventListener('nuxt-component-preview:ready', (event) => {
66-
const { nuxtApp } = event.detail;
65+
// Helper function to handle both sync and async cases
66+
const onNuxtComponentPreviewReady = (callback) => window.__nuxtComponentPreviewApp ? callback(window.__nuxtComponentPreviewApp) : window.addEventListener('nuxt-component-preview:ready', event => callback(event.detail.nuxtApp), { once: true })
6767

68+
// Use the helper
69+
onNuxtComponentPreviewReady((nuxtApp) => {
6870
console.log('Nuxt Component Preview is ready!');
6971

7072
// Preview TestMarkup component

src/runtime/plugin.client.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,15 @@ export default defineNuxtPlugin((nuxtApp) => {
4646
// Provide the preview function
4747
nuxtApp.provide('previewComponent', previewComponent)
4848

49-
// Dispatch ready event when Nuxt is ready
49+
// Store nuxtApp globally and dispatch event when ready
5050
onNuxtReady(() => {
51+
// Store nuxtApp for direct access
52+
window.__nuxtComponentPreviewApp = nuxtApp
53+
54+
// Dispatch event
5155
const event = new CustomEvent('nuxt-component-preview:ready', {
5256
detail: { nuxtApp },
5357
})
54-
5558
window.dispatchEvent(event)
5659
})
5760
})

src/runtime/server/api/app-loader.js.get.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,6 @@ export default defineEventHandler((event) => {
7373
entry.type = 'module';
7474
entry.src = '${entryPathValue}';
7575
document.head.appendChild(entry);
76-
77-
// Add event listener for component preview
78-
window.addEventListener('nuxt-component-preview:ready', function(event) {
79-
console.log('Nuxt Component Preview initialized via loader');
80-
});
8176
}
8277
8378
// Wait for DOM to be ready

src/types/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare global {
2+
interface Window {
3+
__nuxtComponentPreviewApp?: any
4+
}
5+
}
6+
7+
export {}

test/preview-dev.test.ts

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,12 @@ describe('preview E2E (dev mode)', async () => {
3535
it('fires ready event when component preview is enabled', async () => {
3636
const page = await createPage('/preview-test-loader.html')
3737

38-
// Inject listener before page loads
39-
await page.addInitScript(() => {
40-
window.__PREVIEW_READY__ = false
41-
window.addEventListener('nuxt-component-preview:ready', () => {
42-
window.__PREVIEW_READY__ = true
43-
})
44-
})
45-
46-
await page.reload()
47-
48-
// Wait for event
49-
const ready = await page.waitForFunction(() => {
50-
return window.__PREVIEW_READY__ === true
38+
// Wait for event to fire or check if app is already ready
39+
const eventFired = await page.waitForFunction(() => {
40+
return window.__nuxtComponentPreviewApp !== undefined
5141
}, { timeout: 10000 })
5242

53-
expect(ready).toBeTruthy()
43+
expect(eventFired).toBeTruthy()
5444
await page.close()
5545
})
5646

@@ -95,45 +85,28 @@ describe('preview E2E (dev mode)', async () => {
9585
it('works with manually configured HTML', async () => {
9686
const page = await createPage('/preview-test.html')
9787

98-
// Inject listener before page loads
99-
await page.addInitScript(() => {
100-
window.__MANUAL_READY__ = false
101-
window.addEventListener('nuxt-component-preview:ready', () => {
102-
window.__MANUAL_READY__ = true
103-
})
104-
})
105-
106-
await page.reload()
107-
108-
// Wait for ready event
109-
const ready = await page.waitForFunction(() => {
110-
return window.__MANUAL_READY__ === true
88+
// Check if global nuxtApp is set after ready
89+
const isReady = await page.waitForFunction(() => {
90+
return window.__nuxtComponentPreviewApp !== undefined
11191
}, { timeout: 10000 })
11292

113-
expect(ready).toBeTruthy()
93+
expect(isReady).toBeTruthy()
11494
await page.close()
11595
})
11696

11797
it('provides $previewComponent method', async () => {
11898
const page = await createPage('/preview-test.html')
11999

120-
// Inject listener before page loads
121-
await page.addInitScript(() => {
122-
window.__HAS_METHOD__ = false
123-
window.addEventListener('nuxt-component-preview:ready', (event) => {
124-
const { nuxtApp } = (event as CustomEvent).detail
125-
window.__HAS_METHOD__ = typeof nuxtApp?.$previewComponent === 'function'
126-
})
127-
})
128-
129-
await page.reload()
100+
// Wait for app to be ready and check if method exists
101+
await page.waitForFunction(() => {
102+
return window.__nuxtComponentPreviewApp !== undefined
103+
}, { timeout: 10000 })
130104

131-
// Wait and check
132-
await page.waitForFunction(() => window.__HAS_METHOD__ !== false, { timeout: 10000 })
105+
const hasMethod = await page.evaluate(() => {
106+
return typeof window.__nuxtComponentPreviewApp?.$previewComponent === 'function'
107+
})
133108

134-
const hasMethod = await page.evaluate(() => window.__HAS_METHOD__)
135109
expect(hasMethod).toBe(true)
136-
137110
await page.close()
138111
})
139112

test/preview-prod.test.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,12 @@ describe('preview E2E (production mode)', async () => {
3535
it('fires ready event when component preview is enabled', async () => {
3636
const page = await createPage('/preview-test-loader.html')
3737

38-
// Inject listener before page loads
39-
await page.addInitScript(() => {
40-
window.__PREVIEW_READY__ = false
41-
window.addEventListener('nuxt-component-preview:ready', () => {
42-
window.__PREVIEW_READY__ = true
43-
})
44-
})
45-
46-
await page.reload()
47-
48-
// Wait for event
49-
const ready = await page.waitForFunction(() => {
50-
return window.__PREVIEW_READY__ === true
38+
// Wait for event to fire or check if app is already ready
39+
const eventFired = await page.waitForFunction(() => {
40+
return window.__nuxtComponentPreviewApp !== undefined
5141
}, { timeout: 10000 })
5242

53-
expect(ready).toBeTruthy()
43+
expect(eventFired).toBeTruthy()
5444
await page.close()
5545
})
5646

0 commit comments

Comments
 (0)