Skip to content

Commit

Permalink
Convert Preview Window into custom web component
Browse files Browse the repository at this point in the history
Much nicer to init and handle state this way.
  • Loading branch information
tvdeyen committed Mar 25, 2024
1 parent 5f05218 commit 51bb02e
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 101 deletions.
1 change: 0 additions & 1 deletion app/assets/javascripts/alchemy/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@
//= require alchemy/alchemy.fixed_elements
//= require alchemy/alchemy.image_overlay
//= require alchemy/alchemy.link_dialog
//= require alchemy/alchemy.preview_window
82 changes: 0 additions & 82 deletions app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee

This file was deleted.

1 change: 1 addition & 0 deletions app/controllers/alchemy/admin/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def edit
klass.new(routes: Alchemy::Engine.routes).url_for(@page)
]
end
@preview_url = @preview_urls.first.last
@layoutpage = @page.layoutpage?
end

Expand Down
4 changes: 3 additions & 1 deletion app/javascript/alchemy_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Sitemap from "alchemy_admin/sitemap"
import SortableElements from "alchemy_admin/sortable_elements"
import Spinner from "alchemy_admin/spinner"
import PagePublicationFields from "alchemy_admin/page_publication_fields"
import { reloadPreview } from "alchemy_admin/components/preview_window"

// Web Components
import "alchemy_admin/components"
Expand Down Expand Up @@ -66,7 +67,8 @@ Object.assign(Alchemy, {
Sitemap,
SortableElements,
Spinner,
PagePublicationFields
PagePublicationFields,
reloadPreview
})

Rails.start()
Expand Down
1 change: 1 addition & 0 deletions app/javascript/alchemy_admin/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import "alchemy_admin/components/node_select"
import "alchemy_admin/components/uploader"
import "alchemy_admin/components/overlay"
import "alchemy_admin/components/page_select"
import "alchemy_admin/components/preview_window"
import "alchemy_admin/components/select"
import "alchemy_admin/components/spinner"
import "alchemy_admin/components/tags_autocomplete"
Expand Down
10 changes: 8 additions & 2 deletions app/javascript/alchemy_admin/components/element_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class ElementEditor extends HTMLElement {
}

focusElementPreview() {
Alchemy.PreviewWindow.postMessage({
this.previewWindow?.postMessage({
message: "Alchemy.focusElement",
element_id: this.elementId
})
Expand Down Expand Up @@ -141,7 +141,9 @@ export class ElementEditor extends HTMLElement {
this.elementErrors.classList.remove("hidden")
} else {
Alchemy.growl(data.notice)
Alchemy.PreviewWindow.refresh(() => this.focusElementPreview())
this.previewWindow?.refresh().then(() => {
this.focusElementPreview()
})
this.updateTitle(data.previewText)
data.ingredientAnchors.forEach((anchor) => {
IngredientAnchorLink.updateIcon(anchor.ingredientId, anchor.active)
Expand Down Expand Up @@ -547,6 +549,10 @@ export class ElementEditor extends HTMLElement {
get parentElementEditor() {
return this.parentElement?.closest("alchemy-element-editor")
}

get previewWindow() {
return document.getElementById("alchemy_preview_window")
}
}

customElements.define("alchemy-element-editor", ElementEditor)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class PublishElementButton extends HTMLElement {
.then((response) => {
this.elementEditor.published = response.data.public
this.tooltip.setAttribute("content", response.data.label)
Alchemy.reloadPreview()
this.elementEditor.previewWindow.refresh()
})
.catch((error) => Alchemy.growl(error.message, "error"))
}
Expand Down
6 changes: 5 additions & 1 deletion app/javascript/alchemy_admin/components/elements_window.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class ElementsWindow extends HTMLElement {
return document.querySelector("#element_window_button")
}

get previewWindow() {
return document.getElementById("alchemy_preview_window")
}

#attachEvents() {
this.collapseButton?.addEventListener("click", () => {
this.collapseAllElements()
Expand All @@ -72,7 +76,7 @@ class ElementsWindow extends HTMLElement {
this.querySelectorAll("alchemy-element-editor").forEach((editor) => {
editor.classList.remove("selected")
})
Alchemy.PreviewWindow.postMessage({ message: "Alchemy.blurElements" })
this.previewWindow?.postMessage({ message: "Alchemy.blurElements" })
}
})
}
Expand Down
119 changes: 119 additions & 0 deletions app/javascript/alchemy_admin/components/preview_window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
const MIN_WIDTH = 240

class PreviewWindow extends HTMLIFrameElement {
#afterLoad
#reloadIcon

constructor() {
super()
this.addEventListener("load", this)
}

handleEvent(evt) {
if (evt.type === "load") {
this.#stopSpinner()
this.#afterLoad?.call(this, evt)
}
}

connectedCallback() {
let url = this.url

this.#attachEvents()

if (window.localStorage.getItem("alchemy-preview-url")) {
url = window.localStorage.getItem("alchemy-preview-url")
this.previewUrlSelect.value = url
}

this.refresh(url)
}

disconnectedCallback() {
key.unbind("alt+r")
}

postMessage(data) {
this.contentWindow.postMessage(data, "*")
}

resize(width) {
if (width < MIN_WIDTH) width = MIN_WIDTH
this.style.width = `${width}px`
}

refresh(url) {
this.#startSpinner()

if (url) {
this.src = url
} else {
this.src = this.url
}

return new Promise((resolve) => {
this.#afterLoad = resolve
})
}

#attachEvents() {
this.reloadButton?.addEventListener("click", (evt) => {
evt.preventDefault()
this.refresh()
})

key("alt+r", () => this.refresh())

// Need to listen with jQuery here because select2 does not emit native events.
$(this.sizeSelect).on("change", (evt) => {
const select = evt.target
const width = select.value

if (width === "auto") {
this.style.width = null
} else {
this.resize(width)
}
})

this.previewUrlSelect?.addEventListener("change", (evt) => {
const url = evt.target.value
window.localStorage.setItem("alchemy-preview-url", url)
this.refresh(url)
})
}

#startSpinner() {
this.#reloadIcon = this.reloadButton.innerHTML
this.reloadButton.innerHTML = `<alchemy-spinner size="small"></alchemy-spinner>`
}

#stopSpinner() {
this.reloadButton.innerHTML = this.#reloadIcon
}

get url() {
return this.getAttribute("url")
}

get sizeSelect() {
return document.querySelector("select#preview_size")
}

get previewUrlSelect() {
return document.querySelector("select#preview_url")
}

get reloadButton() {
return document.querySelector("#reload_preview_button")
}
}

customElements.define("alchemy-preview-window", PreviewWindow, {
extends: "iframe"
})

export function reloadPreview() {
const previewWindow = document.querySelector("alchemy-preview-window")
previewWindow.refresh()
}
2 changes: 1 addition & 1 deletion app/javascript/alchemy_admin/sortable_elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function onSort(event) {
post(Alchemy.routes.order_admin_elements_path, params).then((response) => {
const data = response.data
Alchemy.growl(data.message)
Alchemy.PreviewWindow.refresh()
Alchemy.reloadPreview()
item.updateTitle(data.preview_text)
})
}
Expand Down
17 changes: 7 additions & 10 deletions app/views/alchemy/admin/pages/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@
<meta name="turbo-cache-control" content="no-cache">
<% end %>

<iframe
url="<%= @preview_url %>"
id="alchemy_preview_window"
is="alchemy-preview-window"
frameborder="0">
</iframe>

<%= turbo_frame_tag "alchemy_elements_window", src: alchemy.admin_elements_path(page_version_id: @page_version.id) do %>
<alchemy-spinner></alchemy-spinner>
<% end %>
Expand All @@ -147,16 +154,6 @@
});
Alchemy.PagePublicationFields();
Alchemy.PageLeaveObserver();
Alchemy.PreviewWindow.init(<%== @preview_urls.first %>);

$('select#preview_size').on('change', function() {
var width = this.value;
if (width === 'auto') {
Alchemy.PreviewWindow.currentWindow.css('width', '');
} else {
Alchemy.PreviewWindow.resize(width);
}
});
});

</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,14 @@ describe("alchemy-element-editor", () => {

it("focuses element in preview", () => {
const click = new Event("click", { bubbles: true })
const postMessage = jest.fn()
jest.spyOn(editor, "previewWindow", "get").mockImplementation(() => {
return {
postMessage
}
})
editor.dispatchEvent(click)
expect(Alchemy.PreviewWindow.postMessage).toHaveBeenCalledWith({
expect(postMessage).toHaveBeenCalledWith({
message: "Alchemy.focusElement",
element_id: "123"
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,19 @@ describe("alchemy-publish-element-button", () => {
describe("on change", () => {
it("Publishes element editor", () => {
const change = new CustomEvent("sl-change", { bubbles: true })
const refresh = jest.fn()
jest.spyOn(button, "elementId", "get").mockReturnValue("123")
button.dispatchEvent(change)
jest.spyOn(button, "elementEditor", "get").mockImplementation(() => {
return {
previewWindow: { refresh }
}
})

return new Promise((resolve) => {
setTimeout(() => {
expect(button.tooltip.getAttribute("content")).toEqual("Hide element")
expect(Alchemy.reloadPreview).toHaveBeenCalled()
expect(refresh).toHaveBeenCalled()
resolve()
}, 1)
})
Expand Down

0 comments on commit 51bb02e

Please sign in to comment.