DEV: Add search suggestions for tag-intersections (#19777)

Added `tagIntersection` search context for handling search suggestions on tag intersection and tag+category routes.

# Tag & Category Route Search Suggestions
eg. /tags/c/general/5/updates
### Before
<img width="422" alt="Screenshot 2023-01-06 at 2 58 50 PM" src="https://user-images.githubusercontent.com/50783505/211098933-ade438c6-5008-49ce-9a90-c8200ec5fe00.png">

### After
<img width="359" alt="Screenshot 2023-01-06 at 3 00 35 PM" src="https://user-images.githubusercontent.com/50783505/211099183-c3feaeac-8661-47ed-843c-da9d9fb78e9e.png">

# Tag Intersection Route Search Suggestions
eg. /tags/intersection/updates/foo
### Before
<img width="421" alt="Screenshot 2023-01-06 at 3 02 23 PM" src="https://user-images.githubusercontent.com/50783505/211099435-e8fc6d87-2772-45b5-b455-1831f80eab3a.png">

### After
<img width="362" alt="Screenshot 2023-01-09 at 2 02 09 PM" src="https://user-images.githubusercontent.com/50783505/211397349-acb350f7-8e6a-4d9f-a749-8292e49400d9.png">

I defaulted to using `+` as a separator for tag intersections. The reasoning behind this is that we don't make the tag intersection routes easily accessible, so if you are going out of your way to view multiple tags, you are most likely going to be searching by **both** of those tags as well.

# General Search
Introducing flex wrap removes whitespace causing a [test](https://github.com/discourse/discourse/pull/19777/files#diff-5d3d13fabc1a511635eb7471ffe74f4d455d77f6984543c2be6ad136dfaa6d3aR813) to fail, but to remedy this I added spacing to the `.search-item-prefix` and `.search-item-slug` which achieves the same thing.

### After
<img width="359" alt="Screenshot 2023-01-09 at 2 04 54 PM" src="https://user-images.githubusercontent.com/50783505/211397900-60220394-5596-4e13-afd0-b6130afa0de2.png">
This commit is contained in:
Isaac Janzen 2023-01-11 13:02:22 -06:00 committed by GitHub
parent c7767686cc
commit 92bb728fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 226 additions and 12 deletions

View File

@ -136,7 +136,21 @@ export default DiscourseRoute.extend(FilterModeMixin, {
noSubcategories,
loading: false,
});
this.searchService.set("searchContext", model.tag.searchContext);
if (model.category || model.additionalTags) {
const tagIntersectionSearchContext = {
type: "tagIntersection",
tagId: model.tag.id,
tag: model.tag,
additionalTags: model.additionalTags || null,
categoryId: model.category?.id || null,
category: model.category || null,
};
this.searchService.set("searchContext", tagIntersectionSearchContext);
} else {
this.searchService.set("searchContext", model.tag.searchContext);
}
},
titleToken() {

View File

@ -415,13 +415,42 @@ createWidget("search-menu-assistant", {
const content = [];
const { suggestionKeyword, term } = attrs;
let prefix = term?.split(suggestionKeyword)[0].trim() || "";
if (prefix.length) {
prefix = `${prefix} `;
let prefix;
if (suggestionKeyword !== "+") {
prefix = term?.split(suggestionKeyword)[0].trim() || "";
if (prefix.length) {
prefix = `${prefix} `;
}
}
switch (suggestionKeyword) {
case "+":
attrs.results.forEach((item) => {
if (item.additionalTags) {
prefix = term?.split(" ").slice(0, -1).join(" ").trim() || "";
} else {
prefix = term?.split("#")[0].trim() || "";
}
if (prefix.length) {
prefix = `${prefix} `;
}
content.push(
this.attach("search-menu-assistant-item", {
prefix,
tag: item.tagName,
additionalTags: item.additionalTags,
category: item.category,
slug: term,
withInLabel: attrs.withInLabel,
isIntersection: true,
})
);
});
break;
case "#":
attrs.results.forEach((item) => {
if (item.model) {
@ -572,6 +601,36 @@ createWidget("search-menu-initial-options", {
})
);
break;
case "tagIntersection":
let tagTerm;
if (ctx.additionalTags) {
const tags = [ctx.tagId, ...ctx.additionalTags];
tagTerm = `${term} tags:${tags.join("+")}`;
} else {
tagTerm = `${term} #${ctx.tagId}`;
}
let suggestionOptions = {
tagName: ctx.tagId,
additionalTags: ctx.additionalTags,
};
if (ctx.category) {
const categorySlug = ctx.category.parentCategory
? `#${ctx.category.parentCategory.slug}:${ctx.category.slug}`
: `#${ctx.category.slug}`;
suggestionOptions.categoryName = categorySlug;
suggestionOptions.category = ctx.category;
tagTerm = tagTerm + ` ${categorySlug}`;
}
content.push(
this.attach("search-menu-assistant", {
term: tagTerm,
suggestionKeyword: "+",
results: [suggestionOptions],
withInLabel: true,
})
);
break;
case "user":
content.push(
this.attach("search-menu-assistant-item", {
@ -677,11 +736,22 @@ createWidget("search-menu-assistant-item", {
link: false,
})
);
} else if (attrs.tag) {
attributes.href = getURL(`/tag/${attrs.tag}`);
content.push(iconNode("tag"));
content.push(h("span.search-item-tag", attrs.tag));
// category and tag combination
if (attrs.tag && attrs.isIntersection) {
attributes.href = getURL(`/tag/${attrs.tag}`);
content.push(iconNode("tag"));
content.push(h("span.search-item-tag", attrs.tag));
}
} else if (attrs.tag) {
if (attrs.isIntersection && attrs.additionalTags?.length) {
const tags = [attrs.tag, ...attrs.additionalTags];
content.push(h("span.search-item-tag", `tags:${tags.join("+")}`));
} else {
attributes.href = getURL(`/tag/${attrs.tag}`);
content.push(iconNode("tag"));
content.push(h("span.search-item-tag", attrs.tag));
}
} else if (attrs.user) {
const userResult = [
avatarImg("small", {

View File

@ -757,6 +757,47 @@ acceptance("Search - assistant", function (needs) {
return helper.response(searchFixtures["search/query"]);
});
server.get("/tag/dev/notifications", () => {
return helper.response({
tag_notification: { id: "dev", notification_level: 2 },
});
});
server.get("/tags/c/bug/1/dev/l/latest.json", () => {
return helper.response({
users: [],
primary_groups: [],
topic_list: {
can_create_topic: true,
draft: null,
draft_key: "new_topic",
draft_sequence: 1,
per_page: 30,
tags: [
{
id: 1,
name: "dev",
topic_count: 1,
},
],
topics: [],
},
});
});
server.get("/tags/intersection/dev/foo.json", () => {
return helper.response({
topic_list: {
can_create_topic: true,
draft: null,
draft_key: "new_topic",
draft_sequence: 1,
per_page: 30,
topics: [],
},
});
});
server.get("/u/search/users", () => {
return helper.response({
users: [
@ -810,13 +851,92 @@ acceptance("Search - assistant", function (needs) {
query(
".search-menu .results ul.search-menu-assistant .search-item-prefix"
).innerText,
"sam "
"sam"
);
await click(firstCategory);
assert.strictEqual(query("#search-term").value, `sam #${firstResultSlug}`);
});
test("Shows category / tag combination shortcut when both are present", async function (assert) {
await visit("/tags/c/bug/dev");
await click("#search-button");
assert.strictEqual(
query(".search-menu .results ul.search-menu-assistant .category-name")
.innerText,
"bug",
"Category is displayed"
);
assert.strictEqual(
query(".search-menu .results ul.search-menu-assistant .search-item-tag")
.innerText,
"dev",
"Tag is displayed"
);
});
test("Updates tag / category combination search suggestion when typing", async function (assert) {
await visit("/tags/c/bug/dev");
await click("#search-button");
await fillIn("#search-term", "foo bar");
assert.strictEqual(
query(
".search-menu .results ul.search-menu-assistant .search-item-prefix"
).innerText,
"foo bar",
"Input is applied to search query"
);
assert.strictEqual(
query(".search-menu .results ul.search-menu-assistant .category-name")
.innerText,
"bug"
);
assert.strictEqual(
query(".search-menu .results ul.search-menu-assistant .search-item-tag")
.innerText,
"dev",
"Tag is displayed"
);
});
test("Shows tag combination shortcut when visiting tag intersection", async function (assert) {
await visit("/tags/intersection/dev/foo");
await click("#search-button");
assert.strictEqual(
query(".search-menu .results ul.search-menu-assistant .search-item-tag")
.innerText,
"tags:dev+foo",
"Tags are displayed"
);
});
test("Updates tag intersection search suggestion when typing", async function (assert) {
await visit("/tags/intersection/dev/foo");
await click("#search-button");
await fillIn("#search-term", "foo bar");
assert.strictEqual(
query(
".search-menu .results ul.search-menu-assistant .search-item-prefix"
).innerText,
"foo bar",
"Input is applied to search query"
);
assert.strictEqual(
query(".search-menu .results ul.search-menu-assistant .search-item-tag")
.innerText,
"tags:dev+foo",
"Tags are displayed"
);
});
test("shows in: shortcuts", async function (assert) {
await visit("/");
await click("#search-button");

View File

@ -208,9 +208,13 @@ $search-pad-horizontal: 0.5em;
margin-top: 2px;
}
.search-item-slug .badge-wrapper {
font-size: var(--font-0);
margin-left: 2px;
.search-item-slug {
margin-right: 5px;
.badge-wrapper {
font-size: var(--font-0);
margin-left: 2px;
}
}
.search-menu-initial-options {
@ -225,7 +229,13 @@ $search-pad-horizontal: 0.5em;
.search-menu-initial-options,
.search-result-tag,
.search-menu-assistant {
.search-item-prefix {
padding-right: 5px;
}
.search-link {
display: flex;
flex-wrap: wrap;
align-items: center;
@include ellipsis;
.d-icon {
margin-right: 5px;