FIX: Add aria-label attribute to cooked hashtags (#22182)
This commit adds an aria-label attribute to cooked hashtags using the post/chat message decorateCooked functionality. I have just used the inner content of the hashtag (the tag/category/channel name) for the label -- we can reexamine at some point if we want something different like "Link to dev category" or something, but from what I can tell things like Twitter don't even have aria-labels for hashtags so the text would be read out directly. This commit also refactors any ruby specs checking the HTML of hashtags to use rspec-html-matchers which is far clearer than having to maintain the HTML structure in a HEREDOC for comparison, and gives better spec failures. c.f. https://meta.discourse.org/t/hashtags-are-getting-a-makeover/248866/23?u=martin
This commit is contained in:
parent
6781e31195
commit
fc199d42fa
|
@ -1,5 +1,5 @@
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
import { replaceHashtagIconPlaceholder } from "discourse/lib/hashtag-autocomplete";
|
import { decorateHashtags } from "discourse/lib/hashtag-autocomplete";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
after: "hashtag-css-generator",
|
after: "hashtag-css-generator",
|
||||||
|
@ -10,13 +10,10 @@ export default {
|
||||||
|
|
||||||
withPluginApi("0.8.7", (api) => {
|
withPluginApi("0.8.7", (api) => {
|
||||||
if (siteSettings.enable_experimental_hashtag_autocomplete) {
|
if (siteSettings.enable_experimental_hashtag_autocomplete) {
|
||||||
api.decorateCookedElement(
|
api.decorateCookedElement((post) => decorateHashtags(post, site), {
|
||||||
(post) => replaceHashtagIconPlaceholder(post, site),
|
onlyStream: true,
|
||||||
{
|
id: "hashtag-icons",
|
||||||
onlyStream: true,
|
});
|
||||||
id: "hashtag-icons",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,8 +25,9 @@ export function cleanUpHashtagTypeClasses() {
|
||||||
export function getHashtagTypeClasses() {
|
export function getHashtagTypeClasses() {
|
||||||
return hashtagTypeClasses;
|
return hashtagTypeClasses;
|
||||||
}
|
}
|
||||||
export function replaceHashtagIconPlaceholder(element, site) {
|
export function decorateHashtags(element, site) {
|
||||||
element.querySelectorAll(".hashtag-cooked").forEach((hashtagEl) => {
|
element.querySelectorAll(".hashtag-cooked").forEach((hashtagEl) => {
|
||||||
|
// Replace the empty icon placeholder span with actual icon HTML.
|
||||||
const iconPlaceholderEl = hashtagEl.querySelector(
|
const iconPlaceholderEl = hashtagEl.querySelector(
|
||||||
".hashtag-icon-placeholder"
|
".hashtag-icon-placeholder"
|
||||||
);
|
);
|
||||||
|
@ -41,6 +42,10 @@ export function replaceHashtagIconPlaceholder(element, site) {
|
||||||
.trim();
|
.trim();
|
||||||
iconPlaceholderEl.replaceWith(domFromString(hashtagIconHTML)[0]);
|
iconPlaceholderEl.replaceWith(domFromString(hashtagIconHTML)[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add an aria-label to the hashtag element so that screen readers
|
||||||
|
// can read the hashtag text.
|
||||||
|
hashtagEl.setAttribute("aria-label", `${hashtagEl.innerText.trim()}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { decorateGithubOneboxBody } from "discourse/instance-initializers/onebox-decorators";
|
import { decorateGithubOneboxBody } from "discourse/instance-initializers/onebox-decorators";
|
||||||
import { replaceHashtagIconPlaceholder } from "discourse/lib/hashtag-autocomplete";
|
import { decorateHashtags } from "discourse/lib/hashtag-autocomplete";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
import highlightSyntax from "discourse/lib/highlight-syntax";
|
import highlightSyntax from "discourse/lib/highlight-syntax";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
@ -73,10 +73,9 @@ export default {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
api.decorateChatMessage(
|
api.decorateChatMessage((element) => decorateHashtags(element, site), {
|
||||||
(element) => replaceHashtagIconPlaceholder(element, site),
|
id: "hashtagIcons",
|
||||||
{ id: "hashtagIcons" }
|
});
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getScrollParent(node, maxParentSelector) {
|
_getScrollParent(node, maxParentSelector) {
|
||||||
|
|
|
@ -194,9 +194,20 @@ describe Chat::ChannelArchiveService do
|
||||||
subject.new(@channel_archive).execute
|
subject.new(@channel_archive).execute
|
||||||
expect(@channel_archive.reload.complete?).to eq(true)
|
expect(@channel_archive.reload.complete?).to eq(true)
|
||||||
pm_topic = Topic.private_messages.last
|
pm_topic = Topic.private_messages.last
|
||||||
expect(pm_topic.first_post.cooked).to include(
|
expect(pm_topic.first_post.cooked).to have_tag(
|
||||||
"<a class=\"hashtag-cooked\" href=\"#{channel.relative_url}\" data-type=\"channel\" data-slug=\"#{channel.slug}\" data-id=\"#{channel.id}\" data-ref=\"#{channel.slug}::channel\"><span class=\"hashtag-icon-placeholder\"></span><span>#{channel.title(user)}</span></a>",
|
"a",
|
||||||
)
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: channel.relative_url,
|
||||||
|
"data-type": "channel",
|
||||||
|
"data-slug": channel.slug,
|
||||||
|
"data-id": channel.id,
|
||||||
|
"data-ref": "#{channel.slug}::channel",
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
with_tag("span", text: channel.title(user))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -241,7 +241,7 @@ describe Chat::Message do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "supports hashtag-autocomplete plugin" do
|
it "supports hashtag autocomplete" do
|
||||||
SiteSetting.chat_enabled = true
|
SiteSetting.chat_enabled = true
|
||||||
SiteSetting.enable_experimental_hashtag_autocomplete = true
|
SiteSetting.enable_experimental_hashtag_autocomplete = true
|
||||||
|
|
||||||
|
@ -250,9 +250,18 @@ describe Chat::Message do
|
||||||
|
|
||||||
cooked = described_class.cook("##{category.slug}", user_id: user.id)
|
cooked = described_class.cook("##{category.slug}", user_id: user.id)
|
||||||
|
|
||||||
expect(cooked).to eq(
|
expect(cooked).to have_tag(
|
||||||
"<p><a class=\"hashtag-cooked\" href=\"#{category.url}\" data-type=\"category\" data-slug=\"#{category.slug}\" data-id=\"#{category.id}\"><span class=\"hashtag-icon-placeholder\"></span><span>#{category.name}</span></a></p>",
|
"a",
|
||||||
)
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: category.url,
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": category.slug,
|
||||||
|
"data-id": category.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "supports censored plugin" do
|
it "supports censored plugin" do
|
||||||
|
|
|
@ -68,15 +68,63 @@ describe "Using #hashtag autocompletion to search for and lookup channels", type
|
||||||
|
|
||||||
cooked_hashtags = page.all(".hashtag-cooked", count: 3)
|
cooked_hashtags = page.all(".hashtag-cooked", count: 3)
|
||||||
|
|
||||||
expect(cooked_hashtags[0]["outerHTML"]).to eq(<<~HTML.chomp)
|
expect(cooked_hashtags[0]["outerHTML"]).to have_tag(
|
||||||
<a class=\"hashtag-cooked\" href=\"#{channel2.relative_url}\" data-type=\"channel\" data-slug=\"random\" data-id=\"#{channel2.id}\"><svg class=\"fa d-icon d-icon-comment svg-icon hashtag-color--channel-#{channel2.id} svg-string\" xmlns=\"http://www.w3.org/2000/svg\"><use href=\"#comment\"></use></svg><span>Random</span></a>
|
"a",
|
||||||
HTML
|
with: {
|
||||||
expect(cooked_hashtags[1]["outerHTML"]).to eq(<<~HTML.chomp)
|
class: "hashtag-cooked",
|
||||||
<a class=\"hashtag-cooked\" href=\"#{category.url}\" data-type=\"category\" data-slug=\"raspberry-beret\" data-id="#{category.id}"><span class=\"hashtag-category-badge hashtag-color--category-#{category.id}\"></span><span>Raspberry</span></a>
|
href: channel2.relative_url,
|
||||||
HTML
|
"data-type": "channel",
|
||||||
expect(cooked_hashtags[2]["outerHTML"]).to eq(<<~HTML.chomp)
|
"data-slug": "random",
|
||||||
<a class=\"hashtag-cooked\" href=\"#{tag.url}\" data-type=\"tag\" data-slug=\"razed\" data-id="#{tag.id}"><svg class=\"fa d-icon d-icon-tag svg-icon hashtag-color--tag-#{tag.id} svg-string\" xmlns=\"http://www.w3.org/2000/svg\"><use href=\"#tag\"></use></svg><span>razed</span></a>
|
"data-id": channel2.id,
|
||||||
HTML
|
"aria-label": "Random",
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"svg",
|
||||||
|
with: {
|
||||||
|
class:
|
||||||
|
"fa d-icon d-icon-comment svg-icon hashtag-color--channel-#{channel2.id} svg-string",
|
||||||
|
},
|
||||||
|
) { with_tag("use", with: { href: "#comment" }) }
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(cooked_hashtags[1]["outerHTML"]).to have_tag(
|
||||||
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: category.url,
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": "raspberry-beret",
|
||||||
|
"data-id": category.id,
|
||||||
|
"aria-label": "Raspberry",
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"span",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-category-badge hashtag-color--category-#{category.id}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(cooked_hashtags[2]["outerHTML"]).to have_tag(
|
||||||
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: tag.url,
|
||||||
|
"data-type": "tag",
|
||||||
|
"data-slug": "razed",
|
||||||
|
"data-id": tag.id,
|
||||||
|
"aria-label": "razed",
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"svg",
|
||||||
|
with: {
|
||||||
|
class: "fa d-icon d-icon-tag svg-icon hashtag-color--tag-#{tag.id} svg-string",
|
||||||
|
},
|
||||||
|
) { with_tag("use", with: { href: "#tag" }) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when a user cannot access the category for a cooked channel hashtag" do
|
context "when a user cannot access the category for a cooked channel hashtag" do
|
||||||
|
|
|
@ -109,12 +109,10 @@ RSpec.describe Email::Styles do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "replaces hashtag-cooked text with raw #hashtag" do
|
it "replaces hashtag-cooked text with raw #hashtag" do
|
||||||
hashtag_html =
|
category = Fabricate(:category, name: "dev", slug: "dev")
|
||||||
"<a class=\"hashtag-cooked\" href=\"#{Discourse.base_url}/c/123/dev\" data-type=\"category\" data-slug=\"dev\"><svg class=\"fa d-icon d-icon-folder svg-icon svg-node\"><use href=\"#folder\"></use></svg><span>Dev Zone</span></a>"
|
post = Fabricate(:post, raw: "this is #dev")
|
||||||
frag = html_fragment(hashtag_html)
|
post.rebake!
|
||||||
expect(frag.at("a").text.chomp).to eq("#dev")
|
hashtag_html = post.cooked
|
||||||
hashtag_html =
|
|
||||||
"<a class=\"hashtag-cooked\" href=\"#{Discourse.base_url}/c/123/dev\" data-type=\"category\" data-slug=\"dev\"><svg class=\"fa d-icon d-icon-folder svg-icon svg-node\">Dev Zone</a>"
|
|
||||||
frag = html_fragment(hashtag_html)
|
frag = html_fragment(hashtag_html)
|
||||||
expect(frag.at("a").text.chomp).to eq("#dev")
|
expect(frag.at("a").text.chomp).to eq("#dev")
|
||||||
end
|
end
|
||||||
|
|
|
@ -190,9 +190,31 @@ RSpec.describe Oneboxer do
|
||||||
.inner_html
|
.inner_html
|
||||||
.chomp
|
.chomp
|
||||||
.strip
|
.strip
|
||||||
expect(preview).to eq(<<~HTML.chomp.strip)
|
expect(preview).to include("This post has some hashtags")
|
||||||
This post has some hashtags, <a class="hashtag-cooked" href="#{category.url}" data-type="category" data-slug="random" data-id="#{category.id}"><span class="hashtag-icon-placeholder"></span>#{category.name}</a> and <a class="hashtag-cooked" href="#{tag.url}" data-type="tag" data-slug="bug" data-id="#{tag.id}"><span class="hashtag-icon-placeholder"></span>#{tag.name}</a>
|
expect(preview).to have_tag(
|
||||||
HTML
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: category.url,
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": category.slug,
|
||||||
|
"data-id": category.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
expect(preview).to have_tag(
|
||||||
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: tag.url,
|
||||||
|
"data-type": "tag",
|
||||||
|
"data-slug": tag.name,
|
||||||
|
"data-id": tag.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1776,24 +1776,60 @@ RSpec.describe PrettyText do
|
||||||
|
|
||||||
cooked = PrettyText.cook(" #unknown::tag #known #known::tag #testing #secret", user_id: user.id)
|
cooked = PrettyText.cook(" #unknown::tag #known #known::tag #testing #secret", user_id: user.id)
|
||||||
|
|
||||||
expect(cooked).to include("<span class=\"hashtag-raw\">#unknown::tag</span>")
|
expect(cooked).to have_tag("span", text: "#unknown::tag", with: { class: "hashtag-raw" })
|
||||||
expect(cooked).to include(
|
expect(cooked).to have_tag(
|
||||||
"<a class=\"hashtag-cooked\" href=\"#{category2.url}\" data-type=\"category\" data-slug=\"known\" data-id=\"#{category2.id}\"><span class=\"hashtag-icon-placeholder\"></span><span>known</span></a>",
|
"a",
|
||||||
)
|
with: {
|
||||||
expect(cooked).to include(
|
class: "hashtag-cooked",
|
||||||
"<a class=\"hashtag-cooked\" href=\"/tag/known\" data-type=\"tag\" data-slug=\"known\" data-id=\"#{tag.id}\" data-ref=\"known::tag\"><span class=\"hashtag-icon-placeholder\"></span><span>known</span></a>",
|
href: category2.url,
|
||||||
)
|
"data-type": "category",
|
||||||
expect(cooked).to include(
|
"data-slug": category2.slug,
|
||||||
"<a class=\"hashtag-cooked\" href=\"#{category.url}\" data-type=\"category\" data-slug=\"testing\" data-id=\"#{category.id}\"><span class=\"hashtag-icon-placeholder\"></span><span>testing</span></a>",
|
"data-id": category2.id,
|
||||||
)
|
},
|
||||||
expect(cooked).to include("<span class=\"hashtag-raw\">#secret</span>")
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
expect(cooked).to have_tag(
|
||||||
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: category.url,
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": category.slug,
|
||||||
|
"data-id": category.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
expect(cooked).to have_tag(
|
||||||
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: tag.url,
|
||||||
|
"data-type": "tag",
|
||||||
|
"data-slug": tag.name,
|
||||||
|
"data-id": tag.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
expect(cooked).to have_tag("span", text: "#secret", with: { class: "hashtag-raw" })
|
||||||
|
|
||||||
# If the user hash access to the private category it should be cooked with the details + icon
|
# If the user hash access to the private category it should be cooked with the details + icon
|
||||||
group.add(user)
|
group.add(user)
|
||||||
cooked = PrettyText.cook(" #unknown::tag #known #known::tag #testing #secret", user_id: user.id)
|
cooked = PrettyText.cook(" #unknown::tag #known #known::tag #testing #secret", user_id: user.id)
|
||||||
expect(cooked).to include(
|
expect(cooked).to have_tag(
|
||||||
"<a class=\"hashtag-cooked\" href=\"#{private_category.url}\" data-type=\"category\" data-slug=\"secret\" data-id=\"#{private_category.id}\"><span class=\"hashtag-icon-placeholder\"></span><span>secret</span></a>",
|
"a",
|
||||||
)
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: private_category.url,
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": private_category.slug,
|
||||||
|
"data-id": private_category.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
|
||||||
cooked = PrettyText.cook("[`a` #known::tag here](http://example.com)", user_id: user.id)
|
cooked = PrettyText.cook("[`a` #known::tag here](http://example.com)", user_id: user.id)
|
||||||
|
|
||||||
|
@ -1809,10 +1845,18 @@ RSpec.describe PrettyText do
|
||||||
expect(cooked).to eq(html.strip)
|
expect(cooked).to eq(html.strip)
|
||||||
|
|
||||||
cooked = PrettyText.cook("<A href='/a'>test</A> #known::tag", user_id: user.id)
|
cooked = PrettyText.cook("<A href='/a'>test</A> #known::tag", user_id: user.id)
|
||||||
html = <<~HTML
|
expect(cooked).to have_tag(
|
||||||
<p><a href="/a">test</a> <a class="hashtag-cooked" href="/tag/known" data-type="tag" data-slug="known" data-id=\"#{tag.id}\" data-ref="known::tag"><span class=\"hashtag-icon-placeholder\"></span><span>known</span></a></p>
|
"a",
|
||||||
HTML
|
with: {
|
||||||
expect(cooked).to eq(html.strip)
|
class: "hashtag-cooked",
|
||||||
|
href: tag.url,
|
||||||
|
"data-type": "tag",
|
||||||
|
"data-slug": tag.name,
|
||||||
|
"data-id": tag.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
|
||||||
# ensure it does not fight with the autolinker
|
# ensure it does not fight with the autolinker
|
||||||
expect(PrettyText.cook(" http://somewhere.com/#known")).not_to include("hashtag")
|
expect(PrettyText.cook(" http://somewhere.com/#known")).not_to include("hashtag")
|
||||||
|
@ -2051,22 +2095,35 @@ HTML
|
||||||
replacement: "discourse",
|
replacement: "discourse",
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(PrettyText.cook("@test #test test")).to match_html(<<~HTML)
|
cooked = PrettyText.cook("@test #test test")
|
||||||
<p>
|
expect(cooked).to have_tag("a", text: "@test", with: { class: "mention", href: "/u/test" })
|
||||||
<a class="mention" href="/u/test">@test</a>
|
expect(cooked).to have_tag(
|
||||||
<a class="hashtag" href="/c/test/#{category.id}">#<span>test</span></a>
|
"a",
|
||||||
tdiscourset
|
text: "#test",
|
||||||
</p>
|
with: {
|
||||||
HTML
|
class: "hashtag",
|
||||||
|
href: "/c/test/#{category.id}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(cooked).to include("tdiscourset")
|
||||||
|
|
||||||
SiteSetting.enable_experimental_hashtag_autocomplete = true
|
SiteSetting.enable_experimental_hashtag_autocomplete = true
|
||||||
expect(PrettyText.cook("@test #test test")).to match_html(<<~HTML)
|
cooked = PrettyText.cook("@test #test test")
|
||||||
<p>
|
expect(cooked).to have_tag("a", text: "@test", with: { class: "mention", href: "/u/test" })
|
||||||
<a class="mention" href="/u/test">@test</a>
|
expect(cooked).to have_tag(
|
||||||
<a class="hashtag-cooked" href="#{category.url}" data-type="category" data-slug="test" data-id="#{category.id}"><span class="hashtag-icon-placeholder"></span><span>test</span></a>
|
"a",
|
||||||
tdiscourset
|
text: "test",
|
||||||
</p>
|
with: {
|
||||||
HTML
|
class: "hashtag-cooked",
|
||||||
|
href: "/c/test/#{category.id}",
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": category.slug,
|
||||||
|
"data-id": category.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag("span", with: { class: "hashtag-icon-placeholder" })
|
||||||
|
end
|
||||||
|
expect(cooked).to include("tdiscourset")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "supports overlapping words" do
|
it "supports overlapping words" do
|
||||||
|
|
|
@ -53,25 +53,53 @@ describe "Using #hashtag autocompletion to search for and lookup categories and
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "cooks the selected hashtag clientside with the correct url and icon" do
|
it "cooks the selected hashtag clientside in the composer preview with the correct url and icon" do
|
||||||
visit_topic_and_initiate_autocomplete
|
visit_topic_and_initiate_autocomplete
|
||||||
hashtag_results = page.all(".hashtag-autocomplete__link", count: 2)
|
hashtag_results = page.all(".hashtag-autocomplete__link", count: 2)
|
||||||
hashtag_results[0].click
|
hashtag_results[0].click
|
||||||
expect(page).to have_css(".hashtag-cooked")
|
expect(page).to have_css(".hashtag-cooked")
|
||||||
cooked_hashtag = page.find(".hashtag-cooked")
|
cooked_hashtag = page.find(".hashtag-cooked")
|
||||||
expected = <<~HTML.chomp
|
|
||||||
<a class=\"hashtag-cooked\" href=\"#{category.url}\" data-type=\"category\" data-id=\"#{category.id}\" data-slug=\"cool-cat\" tabindex=\"-1\"><span class="hashtag-category-badge hashtag-color--category-#{category.id}"></span><span>Cool Category</span></a>
|
expect(cooked_hashtag["outerHTML"]).to have_tag(
|
||||||
HTML
|
"a",
|
||||||
expect(cooked_hashtag["outerHTML"].squish).to eq(expected)
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: category.url,
|
||||||
|
"data-type": "category",
|
||||||
|
"data-slug": category.slug,
|
||||||
|
"data-id": category.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"span",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-category-badge hashtag-color--category-#{category.id}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
visit_topic_and_initiate_autocomplete
|
visit_topic_and_initiate_autocomplete
|
||||||
hashtag_results = page.all(".hashtag-autocomplete__link", count: 2)
|
hashtag_results = page.all(".hashtag-autocomplete__link", count: 2)
|
||||||
hashtag_results[1].click
|
hashtag_results[1].click
|
||||||
expect(page).to have_css(".hashtag-cooked")
|
expect(page).to have_css(".hashtag-cooked")
|
||||||
cooked_hashtag = page.find(".hashtag-cooked")
|
cooked_hashtag = page.find(".hashtag-cooked")
|
||||||
expect(cooked_hashtag["outerHTML"].squish).to eq(<<~HTML.chomp)
|
expect(cooked_hashtag["outerHTML"]).to have_tag(
|
||||||
<a class=\"hashtag-cooked\" href=\"#{tag.url}\" data-type=\"tag\" data-id=\"#{tag.id}\" data-slug=\"cooltag\" tabindex=\"-1\"><svg class=\"fa d-icon d-icon-tag svg-icon hashtag-color--tag-#{tag.id} svg-string\" xmlns=\"http://www.w3.org/2000/svg\"><use href=\"#tag\"></use></svg><span>cooltag</span></a>
|
"a",
|
||||||
HTML
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: tag.url,
|
||||||
|
"data-type": "tag",
|
||||||
|
"data-slug": tag.name,
|
||||||
|
"data-id": tag.id,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"svg",
|
||||||
|
with: {
|
||||||
|
class: "fa d-icon d-icon-tag svg-icon hashtag-color--tag-#{tag.id} svg-string",
|
||||||
|
},
|
||||||
|
) { with_tag("use", with: { href: "#tag" }) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "cooks the hashtags for tag and category correctly serverside when the post is saved to the database" do
|
it "cooks the hashtags for tag and category correctly serverside when the post is saved to the database" do
|
||||||
|
@ -85,12 +113,43 @@ describe "Using #hashtag autocompletion to search for and lookup categories and
|
||||||
|
|
||||||
cooked_hashtags = page.all(".hashtag-cooked", count: 2)
|
cooked_hashtags = page.all(".hashtag-cooked", count: 2)
|
||||||
|
|
||||||
expect(cooked_hashtags[0]["outerHTML"]).to eq(<<~HTML.chomp)
|
expect(cooked_hashtags[0]["outerHTML"]).to have_tag(
|
||||||
<a class=\"hashtag-cooked\" href=\"#{category.url}\" data-type=\"category\" data-slug=\"cool-cat\" data-id=\"#{category.id}\"><span class=\"hashtag-category-badge hashtag-color--category-#{category.id}\"></span><span>Cool Category</span></a>
|
"a",
|
||||||
HTML
|
with: {
|
||||||
expect(cooked_hashtags[1]["outerHTML"]).to eq(<<~HTML.chomp)
|
class: "hashtag-cooked",
|
||||||
<a class=\"hashtag-cooked\" href=\"#{tag.url}\" data-type=\"tag\" data-slug=\"cooltag\" data-id=\"#{tag.id}\"><svg class=\"fa d-icon d-icon-tag svg-icon hashtag-color--tag-#{tag.id} svg-string\" xmlns=\"http://www.w3.org/2000/svg\"><use href=\"#tag\"></use></svg><span>cooltag</span></a>
|
href: category.url,
|
||||||
HTML
|
"data-type": "category",
|
||||||
|
"data-slug": category.slug,
|
||||||
|
"data-id": category.id,
|
||||||
|
"aria-label": category.name,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"span",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-category-badge hashtag-color--category-#{category.id}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(cooked_hashtags[1]["outerHTML"]).to have_tag(
|
||||||
|
"a",
|
||||||
|
with: {
|
||||||
|
class: "hashtag-cooked",
|
||||||
|
href: tag.url,
|
||||||
|
"data-type": "tag",
|
||||||
|
"data-slug": tag.name,
|
||||||
|
"data-id": tag.id,
|
||||||
|
"aria-label": tag.name,
|
||||||
|
},
|
||||||
|
) do
|
||||||
|
with_tag(
|
||||||
|
"svg",
|
||||||
|
with: {
|
||||||
|
class: "fa d-icon d-icon-tag svg-icon hashtag-color--tag-#{tag.id} svg-string",
|
||||||
|
},
|
||||||
|
) { with_tag("use", with: { href: "#tag" }) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when a user cannot access the category for a hashtag cooked in another post" do
|
context "when a user cannot access the category for a hashtag cooked in another post" do
|
||||||
|
|
Loading…
Reference in New Issue