diff --git a/packages/builder/assets/error.svg b/packages/builder/assets/error.svg
new file mode 100644
index 0000000000..4cc1d753c9
--- /dev/null
+++ b/packages/builder/assets/error.svg
@@ -0,0 +1,34 @@
+
+
diff --git a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte
index 5ae4ac0ddf..12f5280b83 100644
--- a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte
+++ b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte
@@ -5,12 +5,16 @@
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
import { FrontendTypes } from "constants"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
+ import { ProgressCircle, Layout, Heading, Body } from "@budibase/bbui"
+ import ErrorSVG from "assets/error.svg?raw"
let iframe
let layout
let screen
let confirmDeleteDialog
let idToDelete
+ let loading = true
+ let error
// Create screen slot placeholder for use when a page is selected rather
// than a screen
@@ -68,11 +72,21 @@
onMount(() => {
// Initialise the app when mounted
iframe.contentWindow.addEventListener(
- "bb-ready",
+ "ready",
() => refreshContent(strippedJson),
{ once: true }
)
+ // Catch any app errors
+ iframe.contentWindow.addEventListener(
+ "error",
+ event => {
+ loading = false
+ error = event.detail || "An unknown error occurred"
+ },
+ { once: true }
+ )
+
// Add listener for events sent by cliebt library in preview
iframe.contentWindow.addEventListener("bb-event", event => {
const { type, data } = event.detail
@@ -83,8 +97,10 @@
} else if (type === "delete-component" && data.id) {
idToDelete = data.id
confirmDeleteDialog.show()
+ } else if (type === "preview-loaded") {
+ loading = false
} else {
- console.log(data)
+ console.warning(`Client sent unknown event type: ${type}`)
}
})
})
@@ -99,11 +115,25 @@
+ {#if loading}
+
+ {:else if error}
+
+
+ {@html ErrorSVG}
+ App preview failed to load
+ {error}
+
+
+ {/if}
diff --git a/packages/builder/src/components/design/AppPreview/iframeTemplate.js b/packages/builder/src/components/design/AppPreview/iframeTemplate.js
index 7a7c04b3a1..81b9a9c86f 100644
--- a/packages/builder/src/components/design/AppPreview/iframeTemplate.js
+++ b/packages/builder/src/components/design/AppPreview/iframeTemplate.js
@@ -25,7 +25,9 @@ export default `
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
- box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
+ }
+ html.loaded {
+ box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.1);
}
body {
flex: 1 1 auto;
@@ -58,13 +60,20 @@ export default `
window["##BUDIBASE_PREVIEW_TYPE##"] = previewType
// Initialise app
- if (window.loadBudibase) {
- loadBudibase()
+ try {
+ if (window.loadBudibase) {
+ window.loadBudibase()
+ document.documentElement.classList.add("loaded")
+ } else {
+ throw "The client library couldn't be loaded"
+ }
+ } catch (error) {
+ window.dispatchEvent(new CustomEvent("error", { detail: error }))
}
}
window.addEventListener("message", receiveMessage)
- window.dispatchEvent(new Event("bb-ready"))
+ window.dispatchEvent(new Event("ready"))
diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte
index 8e4d8864ed..8db4648cd9 100644
--- a/packages/client/src/components/ClientApp.svelte
+++ b/packages/client/src/components/ClientApp.svelte
@@ -31,6 +31,9 @@
await initialise()
await authStore.actions.fetchUser()
dataLoaded = true
+ if ($builderStore.inBuilder) {
+ builderStore.actions.notifyLoaded()
+ }
})
// Register this as a refreshable datasource so that user changes cause
diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js
index f229f66b83..401ccfcc79 100644
--- a/packages/client/src/store/builder.js
+++ b/packages/client/src/store/builder.js
@@ -1,7 +1,7 @@
import { writable, derived } from "svelte/store"
import Manifest from "@budibase/standard-components/manifest.json"
-const dispatchEvent = (type, data) => {
+const dispatchEvent = (type, data = {}) => {
window.dispatchEvent(
new CustomEvent("bb-event", {
detail: { type, data },
@@ -64,6 +64,9 @@ const createBuilderStore = () => {
deleteComponent: id => {
dispatchEvent("delete-component", { id })
},
+ notifyLoaded: () => {
+ dispatchEvent("preview-loaded")
+ },
}
return {
...writableStore,