mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Add assistant to quick search widget (#13650)
Replaces the autocomplete overlay for categories and usernames on the search input and adds suggestions as items in the search results instead. Also adds the same behaviour for @mentions as well as special `in: status: order:` keywords. See PR for more details.
This commit is contained in:
parent
361c8be547
commit
438a762956
@ -218,7 +218,6 @@ const SiteHeaderComponent = MountWidget.extend(
|
|||||||
|
|
||||||
this.dispatch("notifications:changed", "user-notifications");
|
this.dispatch("notifications:changed", "user-notifications");
|
||||||
this.dispatch("header:keyboard-trigger", "header");
|
this.dispatch("header:keyboard-trigger", "header");
|
||||||
this.dispatch("search-autocomplete:after-complete", "search-term");
|
|
||||||
this.dispatch("user-menu:navigation", "user-menu");
|
this.dispatch("user-menu:navigation", "user-menu");
|
||||||
|
|
||||||
this.appEvents.on("dom:clean", this, "_cleanDom");
|
this.appEvents.on("dom:clean", this, "_cleanDom");
|
||||||
|
@ -73,9 +73,10 @@ import { replaceFormatter } from "discourse/lib/utilities";
|
|||||||
import { replaceTagRenderer } from "discourse/lib/render-tag";
|
import { replaceTagRenderer } from "discourse/lib/render-tag";
|
||||||
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
|
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
|
||||||
import { addSearchResultsCallback } from "discourse/lib/search";
|
import { addSearchResultsCallback } from "discourse/lib/search";
|
||||||
|
import { addInSearchShortcut } from "discourse/widgets/search-menu-results";
|
||||||
|
|
||||||
// If you add any methods to the API ensure you bump up this number
|
// If you add any methods to the API ensure you bump up this number
|
||||||
const PLUGIN_API_VERSION = "0.11.5";
|
const PLUGIN_API_VERSION = "0.11.6";
|
||||||
|
|
||||||
class PluginApi {
|
class PluginApi {
|
||||||
constructor(version, container) {
|
constructor(version, container) {
|
||||||
@ -1295,6 +1296,18 @@ class PluginApi {
|
|||||||
addSearchResultsCallback(callback) {
|
addSearchResultsCallback(callback) {
|
||||||
addSearchResultsCallback(callback);
|
addSearchResultsCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a in: shortcut to search menu panel.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* addInSearchShortcut("in:assigned");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
addInSearchShortcut(value) {
|
||||||
|
addInSearchShortcut(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// from http://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number
|
// from http://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number
|
||||||
|
@ -206,53 +206,30 @@ export function isValidSearchTerm(searchTerm, siteSettings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applySearchAutocomplete(
|
export function applySearchAutocomplete($input, siteSettings) {
|
||||||
$input,
|
|
||||||
siteSettings,
|
|
||||||
appEvents,
|
|
||||||
options
|
|
||||||
) {
|
|
||||||
const afterComplete = function () {
|
|
||||||
if (appEvents) {
|
|
||||||
appEvents.trigger("search-autocomplete:after-complete");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$input.autocomplete(
|
$input.autocomplete(
|
||||||
deepMerge(
|
deepMerge({
|
||||||
{
|
template: findRawTemplate("category-tag-autocomplete"),
|
||||||
template: findRawTemplate("category-tag-autocomplete"),
|
key: "#",
|
||||||
key: "#",
|
width: "100%",
|
||||||
width: "100%",
|
treatAsTextarea: true,
|
||||||
treatAsTextarea: true,
|
autoSelectFirstSuggestion: false,
|
||||||
autoSelectFirstSuggestion: false,
|
transformComplete: (obj) => obj.text,
|
||||||
transformComplete(obj) {
|
dataSource: (term) => searchCategoryTag(term, siteSettings),
|
||||||
return obj.text;
|
})
|
||||||
},
|
|
||||||
dataSource(term) {
|
|
||||||
return searchCategoryTag(term, siteSettings);
|
|
||||||
},
|
|
||||||
afterComplete,
|
|
||||||
},
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (siteSettings.enable_mentions) {
|
if (siteSettings.enable_mentions) {
|
||||||
$input.autocomplete(
|
$input.autocomplete(
|
||||||
deepMerge(
|
deepMerge({
|
||||||
{
|
template: findRawTemplate("user-selector-autocomplete"),
|
||||||
template: findRawTemplate("user-selector-autocomplete"),
|
key: "@",
|
||||||
key: "@",
|
width: "100%",
|
||||||
width: "100%",
|
treatAsTextarea: true,
|
||||||
treatAsTextarea: true,
|
autoSelectFirstSuggestion: false,
|
||||||
autoSelectFirstSuggestion: false,
|
transformComplete: (v) => v.username || v.name,
|
||||||
transformComplete: (v) => v.username || v.name,
|
dataSource: (term) => userSearch({ term, includeGroups: true }),
|
||||||
dataSource: (term) => userSearch({ term, includeGroups: true }),
|
})
|
||||||
afterComplete,
|
|
||||||
},
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import DiscourseURL, { userPath } from "discourse/lib/url";
|
|||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { addExtraUserClasses } from "discourse/helpers/user-avatar";
|
import { addExtraUserClasses } from "discourse/helpers/user-avatar";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { applySearchAutocomplete } from "discourse/lib/search";
|
|
||||||
import { avatarImg } from "discourse/widgets/post";
|
import { avatarImg } from "discourse/widgets/post";
|
||||||
import { createWidget } from "discourse/widgets/widget";
|
import { createWidget } from "discourse/widgets/widget";
|
||||||
import { get } from "@ember/object";
|
import { get } from "@ember/object";
|
||||||
@ -484,17 +483,9 @@ export default createWidget("header", {
|
|||||||
|
|
||||||
if (this.state.searchVisible) {
|
if (this.state.searchVisible) {
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
const $searchInput = $("#search-term");
|
const searchInput = document.querySelector("#search-term");
|
||||||
$searchInput.focus().select();
|
searchInput.focus();
|
||||||
|
searchInput.select();
|
||||||
applySearchAutocomplete(
|
|
||||||
$searchInput,
|
|
||||||
this.siteSettings,
|
|
||||||
this.appEvents,
|
|
||||||
{
|
|
||||||
appendSelector: ".menu-panel",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -13,10 +13,6 @@ createWidget("search-term", {
|
|||||||
return { afterAutocomplete: false };
|
return { afterAutocomplete: false };
|
||||||
},
|
},
|
||||||
|
|
||||||
searchAutocompleteAfterComplete() {
|
|
||||||
this.state.afterAutocomplete = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
buildAttributes(attrs) {
|
buildAttributes(attrs) {
|
||||||
return {
|
return {
|
||||||
type: "text",
|
type: "text",
|
||||||
@ -28,12 +24,8 @@ createWidget("search-term", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
keyUp(e) {
|
keyUp(e) {
|
||||||
if (e.which === 13) {
|
if (e.which === 13 && !this.state.afterAutocomplete) {
|
||||||
if (this.state.afterAutocomplete) {
|
return this.sendWidgetAction("fullSearch");
|
||||||
this.state.afterAutocomplete = false;
|
|
||||||
} else {
|
|
||||||
return this.sendWidgetAction("fullSearch");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const val = this.attrs.value;
|
const val = this.attrs.value;
|
||||||
|
@ -10,6 +10,33 @@ import highlightSearch from "discourse/lib/highlight-search";
|
|||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
import { iconNode } from "discourse-common/lib/icon-library";
|
||||||
import renderTag from "discourse/lib/render-tag";
|
import renderTag from "discourse/lib/render-tag";
|
||||||
|
|
||||||
|
const inSearchShortcuts = [
|
||||||
|
"in:title",
|
||||||
|
"in:personal",
|
||||||
|
"in:seen",
|
||||||
|
"in:likes",
|
||||||
|
"in:bookmarks",
|
||||||
|
"in:created",
|
||||||
|
];
|
||||||
|
const statusSearchShortcuts = [
|
||||||
|
"status:open",
|
||||||
|
"status:closed",
|
||||||
|
"status:public",
|
||||||
|
"status:noreplies",
|
||||||
|
];
|
||||||
|
const orderSearchShortcuts = [
|
||||||
|
"order:latest",
|
||||||
|
"order:views",
|
||||||
|
"order:likes",
|
||||||
|
"order:latest_topic",
|
||||||
|
];
|
||||||
|
|
||||||
|
export function addInSearchShortcut(value) {
|
||||||
|
if (inSearchShortcuts.indexOf(value) === -1) {
|
||||||
|
inSearchShortcuts.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Highlighted extends RawHtml {
|
class Highlighted extends RawHtml {
|
||||||
constructor(html, term) {
|
constructor(html, term) {
|
||||||
super({ html: `<span>${html}</span>` });
|
super({ html: `<span>${html}</span>` });
|
||||||
@ -207,6 +234,14 @@ createWidget("search-menu-results", {
|
|||||||
tagName: "div.results",
|
tagName: "div.results",
|
||||||
|
|
||||||
html(attrs) {
|
html(attrs) {
|
||||||
|
if (attrs.suggestionKeyword) {
|
||||||
|
return this.attach("search-menu-assistant", {
|
||||||
|
fullTerm: attrs.term,
|
||||||
|
suggestionKeyword: attrs.suggestionKeyword,
|
||||||
|
results: attrs.suggestionResults || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (attrs.invalidTerm) {
|
if (attrs.invalidTerm) {
|
||||||
return h("div.no-results", I18n.t("search.too_short"));
|
return h("div.no-results", I18n.t("search.too_short"));
|
||||||
}
|
}
|
||||||
@ -320,3 +355,134 @@ createWidget("search-menu-results", {
|
|||||||
return content;
|
return content;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createWidget("search-menu-assistant", {
|
||||||
|
tagName: "ul.search-menu-assistant",
|
||||||
|
|
||||||
|
html(attrs) {
|
||||||
|
if (this.siteSettings.tagging_enabled) {
|
||||||
|
addInSearchShortcut("in:tagged");
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = [];
|
||||||
|
const { fullTerm, suggestionKeyword } = attrs;
|
||||||
|
const prefix = fullTerm.split(suggestionKeyword)[0].trim() || null;
|
||||||
|
|
||||||
|
switch (suggestionKeyword) {
|
||||||
|
case "#":
|
||||||
|
attrs.results.map((category) => {
|
||||||
|
const slug = prefix
|
||||||
|
? `${prefix} #${category.slug} `
|
||||||
|
: `#${category.slug} `;
|
||||||
|
|
||||||
|
content.push(
|
||||||
|
this.attach("search-menu-assistant-item", {
|
||||||
|
prefix: prefix,
|
||||||
|
category,
|
||||||
|
slug,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "@":
|
||||||
|
attrs.results.map((user) => {
|
||||||
|
const slug = prefix
|
||||||
|
? `${prefix} @${user.username} `
|
||||||
|
: `@${user.username} `;
|
||||||
|
|
||||||
|
content.push(
|
||||||
|
this.attach("search-menu-assistant-item", {
|
||||||
|
prefix: prefix,
|
||||||
|
user,
|
||||||
|
slug,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "in:":
|
||||||
|
inSearchShortcuts.map((item) => {
|
||||||
|
const slug = prefix ? `${prefix} ${item} ` : item;
|
||||||
|
content.push(this.attach("search-menu-assistant-item", { slug }));
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "status:":
|
||||||
|
statusSearchShortcuts.map((item) => {
|
||||||
|
const slug = prefix ? `${prefix} ${item} ` : item;
|
||||||
|
content.push(this.attach("search-menu-assistant-item", { slug }));
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "order:":
|
||||||
|
orderSearchShortcuts.map((item) => {
|
||||||
|
const slug = prefix ? `${prefix} ${item} ` : item;
|
||||||
|
content.push(this.attach("search-menu-assistant-item", { slug }));
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createWidget("search-menu-assistant-item", {
|
||||||
|
tagName: "li.search-menu-assistant-item",
|
||||||
|
|
||||||
|
html(attrs) {
|
||||||
|
if (attrs.category) {
|
||||||
|
return h(
|
||||||
|
"a.widget-link.search-link",
|
||||||
|
{
|
||||||
|
attributes: {
|
||||||
|
href: attrs.category.url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h("span.search-item-prefix", attrs.prefix),
|
||||||
|
this.attach("category-link", {
|
||||||
|
category: attrs.category,
|
||||||
|
allowUncategorized: true,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if (attrs.user) {
|
||||||
|
const userResult = [
|
||||||
|
avatarImg("small", {
|
||||||
|
template: attrs.user.avatar_template,
|
||||||
|
username: attrs.user.username,
|
||||||
|
}),
|
||||||
|
h("span.username", formatUsername(attrs.user.username)),
|
||||||
|
];
|
||||||
|
|
||||||
|
return h(
|
||||||
|
"a.widget-link.search-link",
|
||||||
|
{
|
||||||
|
attributes: {
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h("span.search-item-prefix", attrs.prefix),
|
||||||
|
h("span.search-item-user", userResult),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return h(
|
||||||
|
"a.widget-link.search-link",
|
||||||
|
{
|
||||||
|
attributes: {
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
h("span.search-item-slug", attrs.slug)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
click(e) {
|
||||||
|
const searchInput = document.querySelector("#search-term");
|
||||||
|
searchInput.value = this.attrs.slug;
|
||||||
|
searchInput.focus();
|
||||||
|
this.sendWidgetAction("triggerAutocomplete", this.attrs.slug);
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { isValidSearchTerm, searchForTerm } from "discourse/lib/search";
|
import { isValidSearchTerm, searchForTerm } from "discourse/lib/search";
|
||||||
|
import Category from "discourse/models/category";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
import { createWidget } from "discourse/widgets/widget";
|
import { createWidget } from "discourse/widgets/widget";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
@ -6,8 +7,15 @@ import { get } from "@ember/object";
|
|||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { h } from "virtual-dom";
|
import { h } from "virtual-dom";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import userSearch from "discourse/lib/user-search";
|
||||||
|
|
||||||
|
const CATEGORY_SLUG_REGEXP = /(\#[a-zA-Z0-9\-:]*)$/gi;
|
||||||
|
// The backend user search query returns zero results for a term-free search
|
||||||
|
// so the regexp below only matches @ followed by a valid character
|
||||||
|
const USERNAME_REGEXP = /(\@[a-zA-Z0-9\-\_]+)$/gi;
|
||||||
|
|
||||||
const searchData = {};
|
const searchData = {};
|
||||||
|
const suggestionTriggers = ["in:", "status:", "order:"];
|
||||||
|
|
||||||
export function initSearchData() {
|
export function initSearchData() {
|
||||||
searchData.loading = false;
|
searchData.loading = false;
|
||||||
@ -17,6 +25,7 @@ export function initSearchData() {
|
|||||||
searchData.typeFilter = null;
|
searchData.typeFilter = null;
|
||||||
searchData.invalidTerm = false;
|
searchData.invalidTerm = false;
|
||||||
searchData.topicId = null;
|
searchData.topicId = null;
|
||||||
|
searchData.afterAutocomplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
initSearchData();
|
initSearchData();
|
||||||
@ -39,6 +48,49 @@ const SearchHelper = {
|
|||||||
const { term, typeFilter, contextEnabled } = searchData;
|
const { term, typeFilter, contextEnabled } = searchData;
|
||||||
const searchContext = contextEnabled ? widget.searchContext() : null;
|
const searchContext = contextEnabled ? widget.searchContext() : null;
|
||||||
const fullSearchUrl = widget.fullSearchUrl();
|
const fullSearchUrl = widget.fullSearchUrl();
|
||||||
|
const matchSuggestions = this.matchesSuggestions();
|
||||||
|
|
||||||
|
if (matchSuggestions) {
|
||||||
|
searchData.noResults = true;
|
||||||
|
searchData.results = [];
|
||||||
|
searchData.loading = false;
|
||||||
|
|
||||||
|
if (typeof matchSuggestions === "string") {
|
||||||
|
searchData.suggestionKeyword = matchSuggestions;
|
||||||
|
widget.scheduleRerender();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (matchSuggestions.type === "category") {
|
||||||
|
const categorySearchTerm = matchSuggestions.categoriesMatch[0].replace(
|
||||||
|
"#",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
searchData.suggestionResults = Category.search(categorySearchTerm);
|
||||||
|
searchData.suggestionKeyword = "#";
|
||||||
|
widget.scheduleRerender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (matchSuggestions.type === "username") {
|
||||||
|
userSearch({
|
||||||
|
term: matchSuggestions.usernamesMatch[0],
|
||||||
|
includeGroups: true,
|
||||||
|
}).then((result) => {
|
||||||
|
if (result?.users.length > 0) {
|
||||||
|
searchData.suggestionResults = result.users;
|
||||||
|
searchData.suggestionKeyword = "@";
|
||||||
|
} else {
|
||||||
|
searchData.noResults = true;
|
||||||
|
searchData.suggestionKeyword = false;
|
||||||
|
}
|
||||||
|
widget.scheduleRerender();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchData.suggestionKeyword = false;
|
||||||
|
|
||||||
if (!isValidSearchTerm(term, widget.siteSettings)) {
|
if (!isValidSearchTerm(term, widget.siteSettings)) {
|
||||||
searchData.noResults = true;
|
searchData.noResults = true;
|
||||||
@ -73,10 +125,38 @@ const SearchHelper = {
|
|||||||
.catch(popupAjaxError)
|
.catch(popupAjaxError)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
searchData.loading = false;
|
searchData.loading = false;
|
||||||
|
searchData.afterAutocomplete = false;
|
||||||
widget.scheduleRerender();
|
widget.scheduleRerender();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
matchesSuggestions() {
|
||||||
|
if (searchData.term === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const simpleSuggestion = suggestionTriggers.find(
|
||||||
|
(mod) => searchData.term === mod || searchData.term.endsWith(` ${mod}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (simpleSuggestion) {
|
||||||
|
return simpleSuggestion;
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoriesMatch = searchData.term.match(CATEGORY_SLUG_REGEXP);
|
||||||
|
|
||||||
|
if (categoriesMatch) {
|
||||||
|
return { type: "category", categoriesMatch };
|
||||||
|
}
|
||||||
|
|
||||||
|
const usernamesMatch = searchData.term.match(USERNAME_REGEXP);
|
||||||
|
if (usernamesMatch) {
|
||||||
|
return { type: "username", usernamesMatch };
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createWidget("search-menu", {
|
export default createWidget("search-menu", {
|
||||||
@ -132,10 +212,14 @@ export default createWidget("search-menu", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
panelContents() {
|
panelContents() {
|
||||||
const contextEnabled = searchData.contextEnabled;
|
const { contextEnabled, afterAutocomplete } = searchData;
|
||||||
|
|
||||||
let searchInput = [
|
let searchInput = [
|
||||||
this.attach("search-term", { value: searchData.term, contextEnabled }),
|
this.attach(
|
||||||
|
"search-term",
|
||||||
|
{ value: searchData.term, contextEnabled },
|
||||||
|
{ state: { afterAutocomplete } }
|
||||||
|
),
|
||||||
];
|
];
|
||||||
if (searchData.term && searchData.loading) {
|
if (searchData.term && searchData.loading) {
|
||||||
searchInput.push(h("div.searching", h("div.spinner")));
|
searchInput.push(h("div.searching", h("div.spinner")));
|
||||||
@ -157,6 +241,8 @@ export default createWidget("search-menu", {
|
|||||||
results: searchData.results,
|
results: searchData.results,
|
||||||
invalidTerm: searchData.invalidTerm,
|
invalidTerm: searchData.invalidTerm,
|
||||||
searchContextEnabled: searchData.contextEnabled,
|
searchContextEnabled: searchData.contextEnabled,
|
||||||
|
suggestionKeyword: searchData.suggestionKeyword,
|
||||||
|
suggestionResults: searchData.suggestionResults,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -211,7 +297,7 @@ export default createWidget("search-menu", {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchData.loading || searchData.noResults) {
|
if (searchData.loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +392,11 @@ export default createWidget("search-menu", {
|
|||||||
this.triggerSearch();
|
this.triggerSearch();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
triggerAutocomplete(term) {
|
||||||
|
searchData.afterAutocomplete = true;
|
||||||
|
this.searchTermChanged(term);
|
||||||
|
},
|
||||||
|
|
||||||
fullSearch() {
|
fullSearch() {
|
||||||
if (!isValidSearchTerm(searchData.term, this.siteSettings)) {
|
if (!isValidSearchTerm(searchData.term, this.siteSettings)) {
|
||||||
return;
|
return;
|
||||||
|
@ -2,6 +2,7 @@ import {
|
|||||||
acceptance,
|
acceptance,
|
||||||
count,
|
count,
|
||||||
exists,
|
exists,
|
||||||
|
query,
|
||||||
queryAll,
|
queryAll,
|
||||||
} from "discourse/tests/helpers/qunit-helpers";
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
||||||
@ -247,3 +248,57 @@ acceptance("Search - with tagging enabled", function (needs) {
|
|||||||
assert.equal(tags, "dev slow");
|
assert.equal(tags, "dev slow");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
acceptance("Search - assistant", function (needs) {
|
||||||
|
needs.user();
|
||||||
|
|
||||||
|
test("shows category shortcuts when typing #", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
|
||||||
|
await click("#search-button");
|
||||||
|
|
||||||
|
await fillIn("#search-term", "#");
|
||||||
|
await triggerKeyEvent("#search-term", "keyup", 51);
|
||||||
|
|
||||||
|
const firstCategory =
|
||||||
|
".search-menu .results ul.search-menu-assistant .search-link";
|
||||||
|
assert.ok(exists(query(firstCategory)));
|
||||||
|
|
||||||
|
const firstResultSlug = query(
|
||||||
|
`${firstCategory} .category-name`
|
||||||
|
).innerText.trim();
|
||||||
|
|
||||||
|
await click(firstCategory);
|
||||||
|
assert.equal(query("#search-term").value, `#${firstResultSlug} `);
|
||||||
|
|
||||||
|
await fillIn("#search-term", "sam #");
|
||||||
|
await triggerKeyEvent("#search-term", "keyup", 51);
|
||||||
|
|
||||||
|
assert.ok(exists(query(firstCategory)));
|
||||||
|
assert.equal(
|
||||||
|
query(
|
||||||
|
".search-menu .results ul.search-menu-assistant .search-item-prefix"
|
||||||
|
).innerText,
|
||||||
|
"sam"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(firstCategory);
|
||||||
|
assert.equal(query("#search-term").value, `sam #${firstResultSlug} `);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows in: shortcuts", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
await click("#search-button");
|
||||||
|
|
||||||
|
const firstTarget =
|
||||||
|
".search-menu .results ul.search-menu-assistant .search-link .search-item-slug";
|
||||||
|
|
||||||
|
await fillIn("#search-term", "in:");
|
||||||
|
await triggerKeyEvent("#search-term", "keyup", 51);
|
||||||
|
assert.equal(query(firstTarget).innerText, "in:title");
|
||||||
|
|
||||||
|
await fillIn("#search-term", "sam in:");
|
||||||
|
await triggerKeyEvent("#search-term", "keyup", 51);
|
||||||
|
assert.equal(query(firstTarget).innerText, "sam in:title");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -251,6 +251,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-menu-assistant {
|
||||||
|
min-width: 100%;
|
||||||
|
margin-top: -1em;
|
||||||
|
|
||||||
|
.search-menu-assistant-item {
|
||||||
|
> span {
|
||||||
|
vertical-align: baseline;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-item-user .avatar,
|
||||||
|
.search-item-prefix {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.searching {
|
.searching {
|
||||||
|
Loading…
Reference in New Issue
Block a user