Skip to content

Commit

Permalink
Rename Anchor Select into Dom Id Select
Browse files Browse the repository at this point in the history
It was previously a dom id - select and technically it is search for dom nodes with ids. It also adds an enabled and disabled state on the dom id - select. Also separate both dom id selects into slightly different components and add an enable and disable mechanic to alchemy-select.
  • Loading branch information
sascha-karnatz committed Mar 28, 2024
1 parent 5bc6769 commit 4f734be
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 96 deletions.
8 changes: 4 additions & 4 deletions app/components/alchemy/admin/link_dialog/anchor_tab.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def self.panel_name

def fields
[
anchor_select,
dom_id_select,
title_input
]
end
Expand All @@ -25,13 +25,13 @@ def message

private

def anchor_select
def dom_id_select
label = label_tag("anchor_link", Alchemy.t(:anchor), class: "control-label")
options = [[Alchemy.t("Please choose"), ""]]
options = [[Alchemy.t("None"), ""]]
options += [[@url, @url]] if is_selected? && @url

select = select_tag(:anchor_link, options_for_select(options, @url), is: "alchemy-select")
select_component = content_tag("alchemy-anchor-select", select, {type: "preview"})
select_component = content_tag("alchemy-dom-id-preview-select", select)

content_tag("div", label + select_component, class: "input select")
end
Expand Down
10 changes: 5 additions & 5 deletions app/components/alchemy/admin/link_dialog/internal_tab.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ def page_select
end

def dom_id_select
fragment = uri.fragment if uri
fragment = "##{uri.fragment}" if uri&.fragment
label = label_tag("element_anchor", Alchemy.t(:anchor), class: "control-label")
options = [[Alchemy.t("Please choose"), ""]]
options += [["##{fragment}", fragment]] if is_selected? && fragment
options = [[page.nil? ? Alchemy.t("Select a page first") : Alchemy.t("None"), ""]]
options += [[fragment, fragment]] if is_selected? && fragment

select = select_tag("element_anchor", options_for_select(options, fragment), is: "alchemy-select")
select_component = content_tag("alchemy-anchor-select", select, {page: page&.id})
select = select_tag("element_anchor", options_for_select(options, fragment), is: "alchemy-select", disabled: page.nil?)
select_component = content_tag("alchemy-dom-id-api-select", select, {page: page&.id})

content_tag("div", label + select_component, class: "input select")
end
Expand Down
59 changes: 0 additions & 59 deletions app/javascript/alchemy_admin/components/anchor_select.js

This file was deleted.

69 changes: 69 additions & 0 deletions app/javascript/alchemy_admin/components/dom_id_select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { get } from "alchemy_admin/utils/ajax"
import { translate } from "alchemy_admin/i18n"

class DomIdSelect extends HTMLElement {
dataItem(hash) {
return {
id: `#${hash}`,
text: `#${hash}`
}
}

get selectElement() {
return this.querySelector('select[is="alchemy-select"]')
}
}

class DomIdApiSelect extends DomIdSelect {
#pageId = undefined

connectedCallback() {
this.page = this.getAttribute("page")
}

async #fetchDomIds() {
const result = await get(Alchemy.routes.api_ingredients_path, {
page_id: this.#pageId
})
const options = result.data.ingredients
.filter((ingredient) => ingredient.data?.dom_id)
.map((ingredient) => this.dataItem(ingredient.data.dom_id))
const prompt =
options.length > 0 ? translate("None") : translate("No anchors found")

this.selectElement.setOptions(options, prompt)
this.selectElement.enable()
}

#reset() {
// wait a tick to initialize the alchemy-select
requestAnimationFrame(() => {
this.selectElement.disable()
this.selectElement.setOptions([], translate("Select a page first"))
})
}

set page(pageId) {
this.#pageId = pageId
pageId ? this.#fetchDomIds() : this.#reset()
}
}

class DomIdPreviewSelect extends DomIdSelect {
connectedCallback() {
// wait a tick to let the browser initialize the inner select component
requestAnimationFrame(() => {
const frame = document.getElementById("alchemy_preview_window")
const elements = frame.contentDocument?.querySelectorAll("[id]") || []
if (elements.length > 0) {
const options = Array.from(elements).map((element) => {
return this.dataItem(element.id)
})
this.selectElement.setOptions(options, translate("None"))
}
})
}
}

customElements.define("alchemy-dom-id-api-select", DomIdApiSelect)
customElements.define("alchemy-dom-id-preview-select", DomIdPreviewSelect)
2 changes: 1 addition & 1 deletion app/javascript/alchemy_admin/components/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import "alchemy_admin/components/anchor_select"
import "alchemy_admin/components/button"
import "alchemy_admin/components/char_counter"
import "alchemy_admin/components/clipboard_button"
import "alchemy_admin/components/datepicker"
import "alchemy_admin/components/dialog_link"
import "alchemy_admin/components/dom_id_select"
import "alchemy_admin/components/element_editor"
import "alchemy_admin/components/elements_window"
import "alchemy_admin/components/list_filter"
Expand Down
38 changes: 25 additions & 13 deletions app/javascript/alchemy_admin/components/select.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Select extends HTMLSelectElement {
#select2Element = undefined
#select2Element

connectedCallback() {
this.classList.add("alchemy_selectbox")
Expand All @@ -10,25 +10,37 @@ class Select extends HTMLSelectElement {
})
}

set data(data) {
let selected = this.value
// remove all previous entries except the default please select entry which has no value or is selected
const emptyOption =
this.options[0]?.value === ""
? this.options[0].cloneNode(true)
: undefined
enable() {
this.removeAttribute("disabled")
this.#updateSelect2()
}

disable() {
this.setAttribute("disabled", "disabled")
this.#updateSelect2()
}

setOptions(data, prompt = undefined) {
let selectedValue = this.value

// reset the old options and insert the placeholder(s) first
this.innerHTML = ""
if (emptyOption) {
this.add(emptyOption)
if (prompt) {
this.add(new Option(prompt, ""))
}

// add the new options to the select
data.forEach((item) => {
const option = new Option(item.text, item.id, false, item.id === selected)
this.add(option)
this.add(new Option(item.text, item.id, false, item.id === selectedValue))
})

// inform Select2 to update
this.#updateSelect2()
}

/**
* inform Select2 to update
*/
#updateSelect2() {
this.#select2Element.trigger("change")
}
}
Expand Down
11 changes: 6 additions & 5 deletions app/javascript/alchemy_admin/link_dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,13 @@ export class LinkDialog extends Alchemy.Dialog {
* @param page
*/
#updatePage(page = null) {
document.getElementById("internal_link").value =
page != null ? page.url_path : undefined
const internalLink = document.getElementById("internal_link")
const domIdSelect = document.querySelector(
'[data-link-form-type="internal"] alchemy-dom-id-api-select'
)

document.querySelector(
'[data-link-form-type="internal"] alchemy-anchor-select'
).page = page != null ? page.id : undefined
internalLink.value = page != null ? page.url_path : undefined
domIdSelect.page = page != null ? page.id : undefined
}

/**
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/alchemy_admin/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export const en = {
"Uploaded bytes exceed file size": "Uploaded bytes exceed file size",
"Abort upload": "Abort upload",
"Cancel all uploads": "Cancel all uploads",
None: "None",
"No anchors found": "No anchors found",
"Select a page first": "Select a page first",
Close: "Close",
formats: {
datetime: "Y-m-d H:i",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
end

it "should not have the value of the hash fragment" do
expect(page.find(:css, "select[name=element_anchor]").value).to eq(fragment)
expect(page.find(:css, "select[name=element_anchor]").value).to eq("#" + fragment)
end
end
end
Expand Down
52 changes: 44 additions & 8 deletions spec/javascript/alchemy_admin/components/select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,38 @@ describe("alchemy-select", () => {
})
})

describe("data", () => {
describe("setOptions", () => {
it("adds the new entry and replace the old ones", () => {
component.data = [
{ id: "foo", text: "bar" },
{ id: "bar", text: "last" }
]
component.setOptions(
[
{ id: "foo", text: "bar" },
{ id: "bar", text: "last" }
],
"Please Select"
)

expect(component.options.length).toEqual(3)
expect(component.options[0].text).toEqual("Please Select")
expect(component.options[1].text).toEqual("bar")
expect(component.options[2].text).toEqual("last")
})

it("does not add a prompt, if no prompt value is given", () => {
component.setOptions([
{ id: "foo", text: "bar" },
{ id: "bar", text: "last" }
])

expect(component.options.length).toEqual(2)
expect(component.options[0].text).toEqual("bar")
expect(component.options[1].text).toEqual("last")
})

it("resets without any options", () => {
const html = `<select is="alchemy-select"></select>`

component = renderComponent("alchemy-select", html)
component.data = [{ id: "foo", text: "bar" }]
component.setOptions([{ id: "foo", text: "bar" }])

expect(component.options.length).toEqual(1)
expect(component.options[0].text).toEqual("bar")
Expand All @@ -73,15 +87,37 @@ describe("alchemy-select", () => {
`

component = renderComponent("alchemy-select", html)
component.data = [
component.setOptions([
{ id: "foo", text: "bar" },
{ id: "2", text: "Second" }
]
])

expect(component.options.length).toEqual(2)
expect(component.options[0].text).toEqual("bar")
expect(component.options[1].text).toEqual("Second")
expect(component.options[1].selected).toBeTruthy()
})
})

describe("enable", () => {
it("removes the disabled attribute", () => {
const html = `<select is="alchemy-select" disabled="disabled"></select>`

component = renderComponent("alchemy-select", html)
component.enable()

expect(component.hasAttribute("disabled")).toBeFalsy()
})
})

describe("disable", () => {
it("adds the disabled attribute", () => {
const html = `<select is="alchemy-select"></select>`

component = renderComponent("alchemy-select", html)
component.disable()

expect(component.hasAttribute("disabled")).toBeTruthy()
})
})
})

0 comments on commit 4f734be

Please sign in to comment.