From 5b657072e42614d34e2342f5eed8edb890527865 Mon Sep 17 00:00:00 2001 From: Liam Bigelow Date: Tue, 5 Apr 2022 12:40:09 +1200 Subject: [PATCH] feat: set a CMS loading state while live editing is initializing --- .../generate/lib/live-connector.js | 17 +++++- .../live/bookshop_live_loading.feature | 54 +++++++++++++++++++ .../integration-tests/support/steps.js | 3 +- javascript-modules/live/lib/app/live.js | 11 +++- 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 javascript-modules/integration-tests/features/live/bookshop_live_loading.feature diff --git a/javascript-modules/generate/lib/live-connector.js b/javascript-modules/generate/lib/live-connector.js index eb086be4..d3032a19 100644 --- a/javascript-modules/generate/lib/live-connector.js +++ b/javascript-modules/generate/lib/live-connector.js @@ -9,13 +9,28 @@ const getLiveEditingConnector = () => { (function(){ const bookshopLiveSetup = (CloudCannon) => { CloudCannon.enableEvents(); + CloudCannon?.setLoading?.("Loading Bookshop Live Editing"); + let triggeredLoad = false; + const whenLoaded = () => { + triggeredLoad = true; + CloudCannon?.setLoading?.(false); + } + setTimeout(() => { + if (!triggeredLoad) { + CloudCannon?.setLoading?.("Error Loading Bookshop Live Editing"); + setTimeout(() => { + if (!triggeredLoad) { whenLoaded() } + }, 2000); + } + }, 12000); const head = document.querySelector('head'); const script = document.createElement('script'); script.src = \`/_cloudcannon/bookshop-live.js\`; script.onload = function() { window.bookshopLive = new window.BookshopLive({ - remoteGlobals: [] + remoteGlobals: [], + loadedFn: whenLoaded, }); const updateBookshopLive = async () => { const frontMatter = await CloudCannon.value({ diff --git a/javascript-modules/integration-tests/features/live/bookshop_live_loading.feature b/javascript-modules/integration-tests/features/live/bookshop_live_loading.feature new file mode 100644 index 00000000..5d3953c8 --- /dev/null +++ b/javascript-modules/integration-tests/features/live/bookshop_live_loading.feature @@ -0,0 +1,54 @@ +Feature: Bookshop CloudCannon Live Editing Loading + + Background: + Given the file tree: + """ + package.json from starters/generate/package.json # <-- this .json line hurts my syntax highlighting + component-lib/ + go.mod from starters/hugo/components.go.mod + config.toml from starters/hugo/components.config.toml + bookshop/ + bookshop.config.js from starters/hugo/bookshop.config.js + site/ + go.mod from starters/hugo/site.go.mod + config.toml from starters/hugo/site.config.toml + """ + * a component-lib/components/single/single.hugo.html file containing: + """ +

{{ .title }}

+ """ + * a site/layouts/index.html file containing: + """ + + + {{ partial "bookshop_bindings" `(dict "title" .Params.block.title)` }} + {{ partial "bookshop" (slice "single" (dict "title" .Params.block.title)) }} + + + """ + * [front_matter]: + """ + block: + title: "Hello There" + """ + * a site/content/_index.md file containing: + """ + --- + [front_matter] + --- + """ + * [ssg]: "hugo" + + @web + Scenario: Bookshop live sets the CloudCannon loading state + Given 🌐 I have loaded my site in CloudCannon + When 🌐 CloudCannon pushes new yaml: + """ + block: + title: "Rendered!" + """ + Then 🌐 There should be no errors + * 🌐 There should be no logs + * 🌐 "window.CloudCannon?.loadingMessages[0] === 'Loading Bookshop Live Editing'" should evaluate + * 🌐 "window.CloudCannon?.loadingMessages[1] === false" should evaluate + * 🌐 The selector h1 should contain "Rendered!" diff --git a/javascript-modules/integration-tests/support/steps.js b/javascript-modules/integration-tests/support/steps.js index 47d0e0a2..4ca3c99f 100644 --- a/javascript-modules/integration-tests/support/steps.js +++ b/javascript-modules/integration-tests/support/steps.js @@ -262,11 +262,12 @@ const loadPage = async (url, world) => { const readyCloudCannon = async (data, world) => { if (!world.page) throw Error("No page open"); const script = `window.CC = class CloudCannon { - constructor(options) { this.isMocked = true; this.data = options.data; document.dispatchEvent(this.event('cloudcannon:load')); } + constructor(options) { this.isMocked = true; this.loadingMessages = []; this.data = options.data; document.dispatchEvent(this.event('cloudcannon:load')); } newData(data) { this.data = data; document.dispatchEvent(this.event('cloudcannon:update')); } event(name) { return new CustomEvent(name, { detail: { CloudCannon: this } });} enableEvents() {} refreshInterface() {} + setLoading(str) { this.loadingMessages.push(str); } async value() { return this.data; } }; window.CloudCannon = new window.CC({ data: ${data} })`; diff --git a/javascript-modules/live/lib/app/live.js b/javascript-modules/live/lib/app/live.js index a2d8391d..ef25e87e 100644 --- a/javascript-modules/live/lib/app/live.js +++ b/javascript-modules/live/lib/app/live.js @@ -20,6 +20,7 @@ export const getLive = (engines) => class BookshopLive { this.verbose = false; this.logFn = this.logger(); + this.loadedFn = options?.loadedFn; const remoteGlobals = options?.remoteGlobals?.length || 0; this.awaitingDataFetches = remoteGlobals + 1; @@ -27,6 +28,14 @@ export const getLive = (engines) => class BookshopLive { this.fetchInfo(); } + completeRender() { + if (typeof this.loadedFn === 'function') { + this.loadedFn(); + this.loadedFn = null; + } + this.renderCount += 1; + } + logger(depth = 0) { return { log: (str) => { @@ -188,7 +197,7 @@ export const getLive = (engines) => class BookshopLive { core.graftTrees(startNode, endNode, output, this.logFn.nested()); this.logFn.log(`Completed grafting ${name}'s update to the DOM tree`); } - this.renderCount += 1; + this.completeRender(); this.logFn.log(`Finished rendering`); } }