Hi.
I’ve been able to create a code with the help of GROK and ChatGPT, that I inserted into components JS tab, after I created some custom user fields in the signup menu.
I have managed to get the functionality of the dependent dropdown menu to work, but, but submiting the form is not possible.
The code is currently available for review at foorum.maarahvas.ee/signup
Here’s the JS code that is currently in use there:
// dependent-dropdown.js
import { apiInitializer } from "discourse/lib/api";
export default apiInitializer("0.8", api => {
if (window.__DEPENDENT_DROPDOWN_INITIALIZED) {
console.debug("Dependent dropdown script already initialized, skipping.");
return;
}
window.__DEPENDENT_DROPDOWN_INITIALIZED = true;
const landParishMap = {
"Virumaa": ["Ebavere (Väike-Maarja)", "Haljala", "Jõvi (Jõhvi)", "Katkuküla (Simuna)", "Lüganuse", "Mahu (Viru-Nigula)", "Rakvere", "Rõhu (Viru-Jaagupi)", "Torvastvere (Kadrina)", "Tärevere (Iisaku)", "Vaivara"],
"Järvamaa": ["Ambla", "Keika (Järva-Jaani)", "Koeru", "Kullamäe (Järva-Madise)", "Nõstvere (Anna)", "Paide", "Türi", "Ämbra (Peetri)"],
"Harjumaa": ["Hageri", "Juuru", "Jõelähtme", "Keila", "Kose", "Kuusalu", "Külaselja (Risti)", "Nissi", "Padise (Harju-Madise)", "Rapla", "Sahataguse (Harju-Jaani)", "Vaskjala (Jüri)"],
"Läänemaa": ["Emaste (Emmaste)", "Hanila", "Karuse", "Kirbla", "Kullamaa", "Käina", "Lihula", "Märjamaa", "Noarootsi", "Pöhelepe (Pühalepa)", "Pööna (Lääne-Nigula)", "Reigi", "Rõdali (Ridala)", "Soontaga (Mihkli)", "Umra (Martna)", "Varbla", "Vigala", "Vormsi"],
"Saaremaa": ["Anseküla", "Jämaja", "Kaarma", "Karja", "Kihelkonna", "Kärla", "Muhu", "Mustjala", "Para (Jaani)", "Pöide", "Püha", "Ruhnu", "Valjala"],
"Pärnumaa": ["Aliste (Halliste)", "Audru", "Häädemeeste", "Karksi", "Kõrve (Pärnu-Jaagupi)", "Pärnu", "Ruhja", "Saarde", "Salatsi", "Soontaga (Mihkli)", "Tori", "Tõstamaa", "Vändra"],
"Wiljandimaa": ["Helme", "Kolga (Kolga-Jaani)", "Kõpu", "Paistu", "Pilistvere", "Põltsamaa", "Tarvastu", "Valula (Suure-Jaani)", "Viljandi"],
"Tartumaa": ["Kambja", "Kodavere", "Kursi", "Kõrenduse (Maarja-Magdaleena)", "Laiuse", "Nõo", "Otepää", "Palamuse", "Puhja", "Rannu", "Rõngu", "Sangaste", "Tartu (Tartu-Maarja)", "Tõrma (Torma)", "Võnnu", "Äksi"],
"Võrumaa": ["Hargla", "Kanepi", "Karula", "Luke", "Põlva", "Rõugu (Rõuge)", "Räpinä (Räpina)", "Urvaste", "Vahtseliina (Vastseliina)", "Valga"],
"Setomaa": ["Setomaa"]
};
const LAND_FIELD_NAME = "user_fields[6]";
const PARISH_FIELD_NAME = "user_fields[8]";
function findSignupForm() {
return (
document.querySelector(".user-signup-form") ||
document.querySelector("form[action*='signup']") ||
document.querySelector("form[id*='new-account']") ||
document.querySelector("form:has(.user-field)")
);
}
function resetSelectKitState(header, body, fieldName) {
header.classList.remove("is-expanded", "is-invalid", "is-disabled");
header.classList.add("is-valid");
header.setAttribute("tabindex", "0");
header.setAttribute("aria-expanded", "false");
body.style.display = "";
body.classList.remove("is-expanded");
console.debug(`Resetting SelectKit state for ${fieldName}:`, {
headerClasses: header.className,
bodyDisplay: body.style.display,
ariaExpanded: header.getAttribute("aria-expanded")
});
setTimeout(() => {
body.style.display = "";
header.classList.remove("is-expanded");
header.focus();
}, 100);
}
function initializeDropdowns() {
try {
if (!window.location.pathname.includes("/signup")) {
console.debug("Not on sign-up page, skipping initialization");
return;
}
const signupForm = findSignupForm();
if (!signupForm) {
console.error("Sign-up form not found");
return;
}
const landField = Array.from(signupForm.querySelectorAll(".user-field")).find(field =>
field.querySelector("label")?.textContent.trim() === "Maa"
);
const parishField = Array.from(signupForm.querySelectorAll(".user-field")).find(field =>
field.querySelector("label")?.textContent.trim() === "Kodukihelkond"
);
const landHeader = landField?.querySelector(".select-kit-header");
const parishHeader = parishField?.querySelector(".select-kit-header");
const landBody = landField?.querySelector(".select-kit-body");
const parishBody = parishField?.querySelector(".select-kit-body");
console.debug("Form and field details:", {
signupForm: !!signupForm,
landField: !!landField,
parishField: !!parishField,
landHeader: !!landHeader,
parishHeader: !!parishHeader,
landBody: !!landBody,
parishBody: !!parishBody
});
if (!landField || !parishField || !landHeader || !parishHeader || !landBody || !parishBody) {
console.error("Required elements not found");
return;
}
const landLabel = landField.querySelector("label");
const parishLabel = parishField.querySelector("label");
if (landLabel) landLabel.setAttribute("for", "user_fields_6");
if (parishLabel) parishLabel.setAttribute("for", "user_fields_8");
function getOrCreateInputField(fieldContainer, fieldName, inputId) {
let inputField = signupForm.querySelector(`input[name='${fieldName}']`);
if (!inputField) {
console.warn(`No input field found for ${fieldName}, creating one`);
inputField = document.createElement("input");
inputField.type = "hidden";
inputField.name = fieldName;
inputField.id = inputId;
inputField.value = "";
inputField.setAttribute("required", "required");
inputField.setAttribute("data-select-kit", "true");
const controls = fieldContainer.querySelector(".controls");
if (controls) {
controls.appendChild(inputField);
} else {
fieldContainer.appendChild(inputField);
}
}
console.debug(`Input field details for ${fieldName}:`, {
name: inputField.name,
value: inputField.value,
id: inputField.id,
parent: inputField.parentElement?.className || inputField.parentElement?.tagName
});
return inputField;
}
const landInput = getOrCreateInputField(landField, LAND_FIELD_NAME, "user_fields_6");
const parishInput = getOrCreateInputField(parishField, PARISH_FIELD_NAME, "user_fields_8");
const usernameInput = signupForm.querySelector("input[name='username']");
const emailInput = signupForm.querySelector("input[name='email']");
if (usernameInput) usernameInput.setAttribute("autocomplete", "username");
if (emailInput) emailInput.setAttribute("autocomplete", "email");
function updateParishOptions(selectedLand) {
if (!parishBody) {
console.error("Parish SelectKit body not found");
return;
}
parishBody.innerHTML = "";
const defaultOption = document.createElement("div");
defaultOption.className = "select-kit-row is-none";
defaultOption.setAttribute("data-value", "");
defaultOption.setAttribute("data-name", "Vali kihelkond");
defaultOption.innerHTML = '<span class="name">Vali kihelkond</span>';
parishBody.appendChild(defaultOption);
if (selectedLand && landParishMap[selectedLand]) {
landParishMap[selectedLand].forEach(parish => {
const option = document.createElement("div");
option.className = "select-kit-row";
option.setAttribute("data-value", parish);
option.setAttribute("data-name", parish);
option.innerHTML = `<span class="name">${parish}</span>`;
parishBody.appendChild(option);
});
parishHeader.setAttribute("data-value", "");
parishHeader.setAttribute("data-name", "Vali kihelkond");
const selectedName = parishHeader.querySelector(".select-kit-selected-name");
if (selectedName) {
selectedName.innerHTML = '<span class="name">Vali kihelkond</span>';
}
parishHeader.setAttribute("aria-label", "Vali kihelkond");
parishHeader.classList.remove("is-disabled", "is-invalid");
parishHeader.classList.add("is-valid");
parishHeader.setAttribute("tabindex", "0");
parishInput.value = "";
parishInput.classList.remove("invalid");
parishInput.dispatchEvent(new Event("change", { bubbles: true }));
parishInput.dispatchEvent(new Event("input", { bubbles: true }));
parishInput.dispatchEvent(new Event("blur", { bubbles: true }));
} else {
parishHeader.classList.add("is-disabled");
parishHeader.setAttribute("tabindex", "-1");
parishInput.value = "";
parishInput.dispatchEvent(new Event("change", { bubbles: true }));
}
resetSelectKitState(parishHeader, parishBody, "Kodukihelkond");
}
parishBody.addEventListener("click", (event) => {
const row = event.target.closest(".select-kit-row:not(.is-none)");
if (!row) return;
const value = row.getAttribute("data-value");
const name = row.getAttribute("data-name");
parishHeader.setAttribute("data-value", value);
parishHeader.setAttribute("data-name", name);
const selectedName = parishHeader.querySelector(".select-kit-selected-name");
if (selectedName) {
selectedName.innerHTML = `<span class="name">${name}</span>`;
}
parishHeader.setAttribute("aria-label", `Valitud: ${name}`);
parishHeader.classList.remove("is-invalid");
parishHeader.classList.add("is-valid");
parishHeader.closest(".select-kit")?.classList.remove("is-invalid");
parishInput.value = value;
parishInput.classList.remove("invalid");
parishInput.dispatchEvent(new Event("change", { bubbles: true }));
parishInput.dispatchEvent(new Event("input", { bubbles: true }));
parishInput.dispatchEvent(new Event("blur", { bubbles: true }));
const customEvent = new CustomEvent("select-kit:change", { bubbles: true, detail: { value } });
parishInput.dispatchEvent(customEvent);
signupForm.dispatchEvent(new Event("change", { bubbles: true }));
console.debug("Parish input updated:", {
value,
valid: parishInput.checkValidity(),
formValid: signupForm.checkValidity(),
parishHeaderClasses: parishHeader.className,
parishBodyDisplay: parishBody.style.display
});
resetSelectKitState(parishHeader, parishBody, "Kodukihelkond");
});
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "data-value") {
const selectedLand = landHeader.getAttribute("data-value");
landInput.value = selectedLand || "";
landInput.classList.remove("invalid");
landInput.dispatchEvent(new Event("change", { bubbles: true }));
landInput.dispatchEvent(new Event("input", { bubbles: true }));
landInput.dispatchEvent(new Event("blur", { bubbles: true }));
console.debug("Land input updated:", {
value: selectedLand,
valid: landInput.checkValidity(),
formValid: signupForm.checkValidity()
});
updateParishOptions(selectedLand);
}
});
});
observer.observe(landHeader, {
attributes: true,
attributeFilter: ["data-value"]
});
const initialLand = landHeader.getAttribute("data-value");
landInput.value = initialLand || "";
updateParishOptions(initialLand);
const formObserver = new MutationObserver(() => {
if (!signupForm.querySelector(`input[name='${LAND_FIELD_NAME}']`)) {
console.warn("Land input missing, recreating");
getOrCreateInputField(landField, LAND_FIELD_NAME, "user_fields_6");
}
if (!signupForm.querySelector(`input[name='${PARISH_FIELD_NAME}']`)) {
console.warn("Parish input missing, recreating");
getOrCreateInputField(parishField, PARISH_FIELD_NAME, "user_fields_8");
}
});
formObserver.observe(signupForm, { childList: true, subtree: true });
signupForm.addEventListener("submit", () => {
console.debug("Form submission attempted:", {
landValue: landInput.value,
landValid: landInput.checkValidity(),
parishValue: parishInput.value,
parishValid: parishInput.checkValidity(),
formValid: signupForm.checkValidity(),
formErrors: Array.from(signupForm.querySelectorAll(".invalid")).map(el => el.name || el.id)
});
});
signupForm.addEventListener("ajax:error", (event) => {
console.debug("Form submission error:", event.detail);
});
console.log("Dependent dropdowns initialized successfully.");
} catch (error) {
console.error("Error initializing dependent dropdowns:", error);
}
}
const formObserver = new MutationObserver((mutations, obs) => {
const signupForm = findSignupForm();
if (signupForm && signupForm.querySelector(".user-field")) {
initializeDropdowns();
obs.disconnect();
}
});
formObserver.observe(document.body, {
childList: true,
subtree: true
});
setTimeout(initializeDropdowns, 5000);
});
Does anyone have any clue what is wrong there and how can this be fixed?
Thanks