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:
parent
c7767686cc
commit
92bb728fe5
|
@ -136,7 +136,21 @@ export default DiscourseRoute.extend(FilterModeMixin, {
|
||||||
noSubcategories,
|
noSubcategories,
|
||||||
loading: false,
|
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() {
|
titleToken() {
|
||||||
|
|
|
@ -415,13 +415,42 @@ createWidget("search-menu-assistant", {
|
||||||
|
|
||||||
const content = [];
|
const content = [];
|
||||||
const { suggestionKeyword, term } = attrs;
|
const { suggestionKeyword, term } = attrs;
|
||||||
let prefix = term?.split(suggestionKeyword)[0].trim() || "";
|
|
||||||
|
|
||||||
if (prefix.length) {
|
let prefix;
|
||||||
prefix = `${prefix} `;
|
if (suggestionKeyword !== "+") {
|
||||||
|
prefix = term?.split(suggestionKeyword)[0].trim() || "";
|
||||||
|
|
||||||
|
if (prefix.length) {
|
||||||
|
prefix = `${prefix} `;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (suggestionKeyword) {
|
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 "#":
|
case "#":
|
||||||
attrs.results.forEach((item) => {
|
attrs.results.forEach((item) => {
|
||||||
if (item.model) {
|
if (item.model) {
|
||||||
|
@ -572,6 +601,36 @@ createWidget("search-menu-initial-options", {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
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":
|
case "user":
|
||||||
content.push(
|
content.push(
|
||||||
this.attach("search-menu-assistant-item", {
|
this.attach("search-menu-assistant-item", {
|
||||||
|
@ -677,11 +736,22 @@ createWidget("search-menu-assistant-item", {
|
||||||
link: false,
|
link: false,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else if (attrs.tag) {
|
|
||||||
attributes.href = getURL(`/tag/${attrs.tag}`);
|
|
||||||
|
|
||||||
content.push(iconNode("tag"));
|
// category and tag combination
|
||||||
content.push(h("span.search-item-tag", attrs.tag));
|
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) {
|
} else if (attrs.user) {
|
||||||
const userResult = [
|
const userResult = [
|
||||||
avatarImg("small", {
|
avatarImg("small", {
|
||||||
|
|
|
@ -757,6 +757,47 @@ acceptance("Search - assistant", function (needs) {
|
||||||
return helper.response(searchFixtures["search/query"]);
|
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", () => {
|
server.get("/u/search/users", () => {
|
||||||
return helper.response({
|
return helper.response({
|
||||||
users: [
|
users: [
|
||||||
|
@ -810,13 +851,92 @@ acceptance("Search - assistant", function (needs) {
|
||||||
query(
|
query(
|
||||||
".search-menu .results ul.search-menu-assistant .search-item-prefix"
|
".search-menu .results ul.search-menu-assistant .search-item-prefix"
|
||||||
).innerText,
|
).innerText,
|
||||||
"sam "
|
"sam"
|
||||||
);
|
);
|
||||||
|
|
||||||
await click(firstCategory);
|
await click(firstCategory);
|
||||||
assert.strictEqual(query("#search-term").value, `sam #${firstResultSlug}`);
|
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) {
|
test("shows in: shortcuts", async function (assert) {
|
||||||
await visit("/");
|
await visit("/");
|
||||||
await click("#search-button");
|
await click("#search-button");
|
||||||
|
|
|
@ -208,9 +208,13 @@ $search-pad-horizontal: 0.5em;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-item-slug .badge-wrapper {
|
.search-item-slug {
|
||||||
font-size: var(--font-0);
|
margin-right: 5px;
|
||||||
margin-left: 2px;
|
|
||||||
|
.badge-wrapper {
|
||||||
|
font-size: var(--font-0);
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-menu-initial-options {
|
.search-menu-initial-options {
|
||||||
|
@ -225,7 +229,13 @@ $search-pad-horizontal: 0.5em;
|
||||||
.search-menu-initial-options,
|
.search-menu-initial-options,
|
||||||
.search-result-tag,
|
.search-result-tag,
|
||||||
.search-menu-assistant {
|
.search-menu-assistant {
|
||||||
|
.search-item-prefix {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
.search-link {
|
.search-link {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
@include ellipsis;
|
@include ellipsis;
|
||||||
.d-icon {
|
.d-icon {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
|
|
Loading…
Reference in New Issue