UX: Show parent category name for category hashtags (#31188)

This commit changes the display of category hashtag
autocomplete to show the parent category name in this
format:

* Parent Name > Child Name

This helps further distinguish categories in the autocomplete
where there may be multiple different parent categories with
the same name child category, e.g. if every category has an
Announcements subcategory.
This commit is contained in:
Martin Brennan 2025-02-05 12:31:50 +10:00 committed by GitHub
parent f01c0c9740
commit a2dbcedbd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 9 deletions

View File

@ -18,7 +18,14 @@ class CategoryHashtagDataSource
def self.category_to_hashtag_item(category) def self.category_to_hashtag_item(category)
HashtagAutocompleteService::HashtagItem.new.tap do |item| HashtagAutocompleteService::HashtagItem.new.tap do |item|
item.text = category.name item.text =
(
if category.parent_category
"#{category.parent_category.name} > #{category.name}"
else
category.name
end
)
item.slug = category.slug item.slug = category.slug
item.description = category.description_text item.description = category.description_text
item.icon = icon item.icon = icon

View File

@ -1,15 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe CategoryHashtagDataSource do RSpec.describe CategoryHashtagDataSource do
fab!(:parent_category) { Fabricate(:category, slug: "fun", topic_count: 2) } fab!(:parent_category) { Fabricate(:category, name: "Silly Land", slug: "fun", topic_count: 2) }
fab!(:category1) do fab!(:category1) do
Fabricate(:category, slug: "random", topic_count: 12, parent_category: parent_category) Fabricate(
:category,
name: "Random",
slug: "random",
topic_count: 12,
parent_category: parent_category,
)
end end
fab!(:category2) { Fabricate(:category, name: "Book Section", slug: "books", topic_count: 566) } fab!(:category2) { Fabricate(:category, name: "Book Section", slug: "books", topic_count: 566) }
fab!(:category3) { Fabricate(:category, slug: "movies", topic_count: 245) } fab!(:category3) { Fabricate(:category, name: "Kino Korner", slug: "movies", topic_count: 245) }
fab!(:group) fab!(:group)
fab!(:category4) { Fabricate(:private_category, slug: "secret", group: group, topic_count: 40) } fab!(:category4) do
fab!(:category5) { Fabricate(:category, slug: "casual", topic_count: 99) } Fabricate(:private_category, name: "Nunya", slug: "secret", group: group, topic_count: 40)
end
fab!(:category5) { Fabricate(:category, name: "Chillzone", slug: "casual", topic_count: 99) }
fab!(:user) fab!(:user)
let(:guardian) { Guardian.new(user) } let(:guardian) { Guardian.new(user) }
let(:uncategorized_category) { Category.find(SiteSetting.uncategorized_category_id) } let(:uncategorized_category) { Category.find(SiteSetting.uncategorized_category_id) }
@ -55,6 +63,16 @@ RSpec.describe CategoryHashtagDataSource do
expect(described_class.lookup(Guardian.new(user), ["secret"]).first).not_to eq(nil) expect(described_class.lookup(Guardian.new(user), ["secret"]).first).not_to eq(nil)
end end
it "formats the category text for a category without a parent" do
result = described_class.lookup(guardian, ["movies"]).first
expect(result.text).to eq("Kino Korner")
end
it "formats the category text for a category with a parent" do
result = described_class.lookup(guardian, ["fun:random"]).first
expect(result.text).to eq("Silly Land > Random")
end
context "with sub-sub-categories" do context "with sub-sub-categories" do
before { SiteSetting.max_category_nesting = 3 } before { SiteSetting.max_category_nesting = 3 }
@ -94,6 +112,15 @@ RSpec.describe CategoryHashtagDataSource do
result = described_class.lookup(guardian, ["child:grandchild"]) result = described_class.lookup(guardian, ["child:grandchild"])
expect(result.map(&:relative_url)).to eq([parent2_child_grandchild.url]) expect(result.map(&:relative_url)).to eq([parent2_child_grandchild.url])
end end
it "formats the category text for sub-sub-categories" do
parent = Fabricate(:category, slug: "parent", name: "Parent")
child = Fabricate(:category, slug: "child", parent_category_id: parent.id, name: "Child")
grandchild =
Fabricate(:category, slug: "grandchild", parent_category_id: child.id, name: "Grandchild")
result = described_class.lookup(guardian, ["child:grandchild"])
expect(result.last.text).to eq("Child > Grandchild")
end
end end
end end
@ -102,6 +129,7 @@ RSpec.describe CategoryHashtagDataSource do
result = described_class.search(guardian, "mov", 5).first result = described_class.search(guardian, "mov", 5).first
expect(result.ref).to eq("movies") expect(result.ref).to eq("movies")
expect(result.slug).to eq("movies") expect(result.slug).to eq("movies")
expect(result.text).to eq("Kino Korner")
end end
it "finds categories by partial slug" do it "finds categories by partial slug" do
@ -121,6 +149,11 @@ RSpec.describe CategoryHashtagDataSource do
expect(result.ref).to eq("fun:random") expect(result.ref).to eq("fun:random")
expect(result.slug).to eq("random") expect(result.slug).to eq("random")
end end
it "formats the text correctly for a parent:child category that is found" do
result = described_class.search(guardian, "random", 5).first
expect(result.text).to eq("Silly Land > Random")
end
end end
describe "#search_without_term" do describe "#search_without_term" do

View File

@ -212,8 +212,8 @@ RSpec.describe HashtagAutocompleteService do
expect(service.search("book", %w[category tag]).map(&:ref)).to eq( expect(service.search("book", %w[category tag]).map(&:ref)).to eq(
[ [
"book-reviews:book", # category exact match on slug, name sorted
"book-library:book", "book-library:book",
"book-reviews:book", # category exact match on slug, name sorted
"book-library", # category starts with match on slug, name sorted "book-library", # category starts with match on slug, name sorted
"book-reviews", "book-reviews",
"bookmania", # tag starts with match on slug, name sorted "bookmania", # tag starts with match on slug, name sorted
@ -226,8 +226,8 @@ RSpec.describe HashtagAutocompleteService do
) )
expect(service.search("book", %w[category tag]).map(&:text)).to eq( expect(service.search("book", %w[category tag]).map(&:text)).to eq(
[ [
"Good Books", "Book Library > Horror",
"Horror", "Book Reviews > Good Books",
"Book Library", "Book Library",
"Book Reviews", "Book Reviews",
"bookmania", "bookmania",