diff --git a/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js b/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js index 1d209a637f7..cd25b9e9c7e 100644 --- a/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js +++ b/plugins/footnote/assets/javascripts/initializers/inline-footnotes.js @@ -29,47 +29,49 @@ function applyInlineFootnotes(elem) { } function buildTooltip() { - let html = ` + const template = document.createElement("template"); + template.innerHTML = ` - `; + `.trim(); - let template = document.createElement("template"); - html = html.trim(); - template.innerHTML = html; return template.content.firstChild; } -function footNoteEventHandler(event) { - inlineFootnotePopper?.destroy(); - +function footnoteEventHandler(event) { const tooltip = document.getElementById("footnote-tooltip"); + const displayedFootnoteId = tooltip?.dataset.footnoteId; + const expandableFootnote = event.target; + const footnoteId = expandableFootnote.dataset.footnoteId; - // reset state by hidding tooltip, it handles "click outside" - // allowing to hide the tooltip when you click anywhere else + inlineFootnotePopper?.destroy(); tooltip?.removeAttribute("data-show"); + tooltip?.removeAttribute("data-footnote-id"); - // if we didn't actually click a footnote button, exit early if (!event.target.classList.contains("expand-footnote")) { + // dismissing the tooltip by clicking outside return; } event.preventDefault(); event.stopPropagation(); - // append footnote to tooltip body - const expandableFootnote = event.target; - const cooked = expandableFootnote.closest(".cooked"); - const footnoteId = expandableFootnote.dataset.footnoteId; - const footnoteContent = tooltip.querySelector(".footnote-tooltip-content"); - let newContent = cooked.querySelector(footnoteId); + if (displayedFootnoteId === footnoteId) { + // dismissing the tooltip by clicking the footnote button + return; + } + // append footnote to tooltip body + const footnoteContent = tooltip.querySelector(".footnote-tooltip-content"); + const cooked = expandableFootnote.closest(".cooked"); + const newContent = cooked.querySelector(footnoteId); footnoteContent.innerHTML = newContent.innerHTML; // display tooltip tooltip.dataset.show = ""; + tooltip.dataset.footnoteId = footnoteId; // setup popper inlineFootnotePopper?.destroy(); @@ -104,9 +106,8 @@ export default { return; } - document.documentElement.append(buildTooltip()); - - window.addEventListener("click", footNoteEventHandler); + document.body.append(buildTooltip()); + window.addEventListener("click", footnoteEventHandler); withPluginApi("0.8.9", (api) => { api.decorateCookedElement((elem) => applyInlineFootnotes(elem), { @@ -115,16 +116,18 @@ export default { }); api.onPageChange(() => { - document - .getElementById("footnote-tooltip") - ?.removeAttribute("data-show"); + inlineFootnotePopper?.destroy(); + + const tooltip = document.getElementById("footnote-tooltip"); + tooltip?.removeAttribute("data-show"); + tooltip?.removeAttribute("data-footnote-id"); }); }); }, teardown() { inlineFootnotePopper?.destroy(); - window.removeEventListener("click", footNoteEventHandler); + window.removeEventListener("click", footnoteEventHandler); document.getElementById("footnote-tooltip")?.remove(); }, }; diff --git a/plugins/footnote/test/javascripts/acceptance/footnote-test.js b/plugins/footnote/test/javascripts/acceptance/footnote-test.js index 4ff4d8f6603..c7af4c92397 100644 --- a/plugins/footnote/test/javascripts/acceptance/footnote-test.js +++ b/plugins/footnote/test/javascripts/acceptance/footnote-test.js @@ -1,12 +1,10 @@ import { click, visit } from "@ember/test-helpers"; import { test } from "qunit"; import topicFixtures from "discourse/tests/fixtures/topic"; -import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers"; +import { acceptance } from "discourse/tests/helpers/qunit-helpers"; import { cloneJSON } from "discourse-common/lib/object"; -acceptance("Discourse Foonote Plugin", function (needs) { - needs.user(); - +acceptance("Discourse Footnote Plugin", function (needs) { needs.settings({ display_footnotes_inline: true, }); @@ -28,30 +26,47 @@ acceptance("Discourse Foonote Plugin", function (needs) { }); }); - test("displays the foonote on click", async function (assert) { - await visit("/t/45"); + test("displays the footnote on click", async function (assert) { + await visit("/t/-/45"); - const tooltip = document.getElementById("footnote-tooltip"); - assert.ok(exists(tooltip)); + assert.dom("#footnote-tooltip", document.body).exists(); + // open await click(".expand-footnote"); - assert.equal( - tooltip.querySelector(".footnote-tooltip-content").textContent.trim(), - "consectetur adipiscing elit ↩︎" - ); + assert + .dom(".footnote-tooltip-content", document.body) + .hasText("consectetur adipiscing elit ↩︎"); + assert.dom("#footnote-tooltip", document.body).hasAttribute("data-show"); + + // close by clicking outside + await click(document.body); + assert + .dom("#footnote-tooltip", document.body) + .doesNotHaveAttribute("data-show"); + + // open again + await click(".expand-footnote"); + assert + .dom(".footnote-tooltip-content", document.body) + .hasText("consectetur adipiscing elit ↩︎"); + assert.dom("#footnote-tooltip", document.body).hasAttribute("data-show"); + + // close by clicking the button + await click(".expand-footnote"); + assert + .dom("#footnote-tooltip", document.body) + .doesNotHaveAttribute("data-show"); }); test("clicking a second footnote with same name works", async function (assert) { - await visit("/t/45"); + await visit("/t/-/45"); - const tooltip = document.getElementById("footnote-tooltip"); - assert.ok(exists(tooltip)); + assert.dom("#footnote-tooltip", document.body).exists(); await click(".second .expand-footnote"); - - assert.equal( - tooltip.querySelector(".footnote-tooltip-content").textContent.trim(), - "consectetur adipiscing elit ↩︎" - ); + assert + .dom(".footnote-tooltip-content", document.body) + .hasText("consectetur adipiscing elit ↩︎"); + assert.dom("#footnote-tooltip", document.body).hasAttribute("data-show"); }); });