diff --git a/javascripts/discourse/initializers/setup.js b/javascripts/discourse/initializers/setup.js index 3587f3c..0b75628 100644 --- a/javascripts/discourse/initializers/setup.js +++ b/javascripts/discourse/initializers/setup.js @@ -64,6 +64,20 @@ function buildSelect(key, placeholder) { return select; } +function replaceInText(text, placeholders) { + for (const [key, { delimiter, value }] of Object.entries(placeholders)) { + const placeholderWithDelimiter = `${delimiter}${key}${delimiter}`; + + let substitution = value; + if (!substitution?.length || substitution === "none") { + substitution = placeholderWithDelimiter; + } + + text = text.replaceAll(placeholderWithDelimiter, substitution); + } + return text; +} + function performReplacements(cooked, placeholders) { cooked.querySelectorAll(VALID_TAGS).forEach((elem) => { const textNodeWalker = document.createTreeWalker( @@ -71,6 +85,7 @@ function performReplacements(cooked, placeholders) { NodeFilter.SHOW_TEXT ); + // Handle text nodes while (textNodeWalker.nextNode()) { const node = textNodeWalker.currentNode; @@ -80,23 +95,28 @@ function performReplacements(cooked, placeholders) { } const originalText = originalContentMap.get(node); - let text = originalText; - - for (const [key, { delimiter, value }] of Object.entries(placeholders)) { - const placeholderWithDelimiter = `${delimiter}${key}${delimiter}`; - - let substitution = value; - if (!substitution?.length || substitution === "none") { - substitution = placeholderWithDelimiter; - } - - text = text.replaceAll(placeholderWithDelimiter, substitution); - } + const text = replaceInText(originalText, placeholders); if (node.data !== text) { node.data = text; } } + + // Handle a[href] attributes + document.querySelectorAll("a[href]").forEach((link) => { + const hrefAttr = link.attributes.getNamedItem("href"); + + if (!originalContentMap.has(hrefAttr)) { + // Haven't seen this attr before. Get the text, and store it for future transformations + originalContentMap.set(hrefAttr, hrefAttr.value); + } + const originalUrl = originalContentMap.get(hrefAttr); + const newUrl = replaceInText(originalUrl, placeholders); + + if (hrefAttr.value !== newUrl) { + hrefAttr.value = newUrl; + } + }); }); } diff --git a/spec/system/placeholder_spec.rb b/spec/system/placeholder_spec.rb index ff56cfa..67c57e9 100644 --- a/spec/system/placeholder_spec.rb +++ b/spec/system/placeholder_spec.rb @@ -47,4 +47,30 @@ RSpec.describe "Placeholder", system: true do expect(page).to have_content("BEFORE foo bar AFTER") end end + + context "when placeholder is used in a[href]" do + fab!(:post) do + Fabricate( + :post, + raw: <<~MD + [wrap=placeholder key=\"TEST1\"][/wrap] + [Some link](https://example.com/=TEST1=) + MD + ) + end + + it "replaces string in href" do + topic_page.visit_topic(post.topic) + + expect(page).to have_link(href: "https://example.com/=TEST1=") + + page.find('.discourse-placeholder-value[data-key="TEST1"]').fill_in(with: "foo") + + expect(page).to have_link(href: "https://example.com/foo") + + page.find('.discourse-placeholder-value[data-key="TEST1"]').fill_in(with: "bar") + + expect(page).to have_link(href: "https://example.com/bar") + end + end end