DEV: PluginApi function to customize search menu assistant item behavior (#24992)
This commit is contained in:
parent
87883a1963
commit
3c6362bb26
|
@ -4,6 +4,7 @@
|
||||||
class="search-menu-assistant-item"
|
class="search-menu-assistant-item"
|
||||||
{{on "keydown" this.onKeydown}}
|
{{on "keydown" this.onKeydown}}
|
||||||
{{on "click" this.onClick}}
|
{{on "click" this.onClick}}
|
||||||
|
data-usage={{@usage}}
|
||||||
>
|
>
|
||||||
<a class="search-link" href={{this.href}}>
|
<a class="search-link" href={{this.href}}>
|
||||||
<span aria-label={{i18n "search.title"}}>
|
<span aria-label={{i18n "search.title"}}>
|
||||||
|
|
|
@ -5,6 +5,15 @@ import { focusSearchInput } from "discourse/components/search-menu";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { debounce } from "discourse-common/utils/decorators";
|
import { debounce } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
const _itemSelectCallbacks = [];
|
||||||
|
export function addItemSelectCallback(fn) {
|
||||||
|
_itemSelectCallbacks.push(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetItemSelectCallbacks() {
|
||||||
|
_itemSelectCallbacks.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
export default class AssistantItem extends Component {
|
export default class AssistantItem extends Component {
|
||||||
@service search;
|
@service search;
|
||||||
@service appEvents;
|
@service appEvents;
|
||||||
|
@ -85,16 +94,32 @@ export default class AssistantItem extends Component {
|
||||||
|
|
||||||
@debounce(100)
|
@debounce(100)
|
||||||
itemSelected() {
|
itemSelected() {
|
||||||
let updatedValue = "";
|
let updatedTerm = "";
|
||||||
if (this.args.slug) {
|
if (this.args.slug) {
|
||||||
updatedValue = this.prefix.concat(this.args.slug);
|
updatedTerm = this.prefix.concat(this.args.slug);
|
||||||
} else {
|
} else {
|
||||||
updatedValue = this.prefix.trim();
|
updatedTerm = this.prefix.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
const inTopicContext = this.search.searchContext?.type === "topic";
|
const inTopicContext = this.search.searchContext?.type === "topic";
|
||||||
this.args.searchTermChanged(updatedValue, {
|
const searchTopics = !inTopicContext || this.search.activeGlobalSearchTerm;
|
||||||
searchTopics: !inTopicContext || this.search.activeGlobalSearchTerm,
|
|
||||||
|
if (
|
||||||
|
_itemSelectCallbacks.length &&
|
||||||
|
!_itemSelectCallbacks.some((fn) =>
|
||||||
|
fn({
|
||||||
|
updatedTerm,
|
||||||
|
searchTermChanged: this.args.searchTermChanged,
|
||||||
|
usage: this.args.usage,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Return early if any callbacks return false
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.args.searchTermChanged(updatedTerm, {
|
||||||
|
searchTopics,
|
||||||
...(inTopicContext &&
|
...(inTopicContext &&
|
||||||
!this.args.searchAllTopics && { setTopicContext: true }),
|
!this.args.searchAllTopics && { setTopicContext: true }),
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,7 +48,9 @@
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<SearchMenu::Results::RandomQuickTip />
|
<SearchMenu::Results::RandomQuickTip
|
||||||
|
@searchTermChanged={{@searchTermChanged}}
|
||||||
|
/>
|
||||||
{{#if (and this.currentUser this.siteSettings.log_search_queries)}}
|
{{#if (and this.currentUser this.siteSettings.log_search_queries)}}
|
||||||
<SearchMenu::Results::RecentSearches
|
<SearchMenu::Results::RecentSearches
|
||||||
@closeSearchMenu={{@closeSearchMenu}}
|
@closeSearchMenu={{@closeSearchMenu}}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default class RandomQuickTip extends Component {
|
||||||
@action
|
@action
|
||||||
tipSelected(e) {
|
tipSelected(e) {
|
||||||
if (e.target.classList.contains("tip-clickable")) {
|
if (e.target.classList.contains("tip-clickable")) {
|
||||||
this.search.activeGlobalSearchTerm = this.randomTip.label;
|
this.args.searchTermChanged(this.randomTip.label);
|
||||||
focusSearchInput();
|
focusSearchInput();
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
@slug={{slug}}
|
@slug={{slug}}
|
||||||
@closeSearchMenu={{@closeSearchMenu}}
|
@closeSearchMenu={{@closeSearchMenu}}
|
||||||
@searchTermChanged={{@searchTermChanged}}
|
@searchTermChanged={{@searchTermChanged}}
|
||||||
|
@usage="recent-search"
|
||||||
/>
|
/>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
} from "discourse/components/reviewable-item";
|
} from "discourse/components/reviewable-item";
|
||||||
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
|
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
|
||||||
import { addSearchSuggestion as addGlimmerSearchSuggestion } from "discourse/components/search-menu/results/assistant";
|
import { addSearchSuggestion as addGlimmerSearchSuggestion } from "discourse/components/search-menu/results/assistant";
|
||||||
|
import { addItemSelectCallback as addSearchMenuAssistantSelectCallback } from "discourse/components/search-menu/results/assistant-item";
|
||||||
import {
|
import {
|
||||||
addQuickSearchRandomTip,
|
addQuickSearchRandomTip,
|
||||||
removeDefaultQuickSearchRandomTips,
|
removeDefaultQuickSearchRandomTips,
|
||||||
|
@ -143,7 +144,7 @@ import { modifySelectKit } from "select-kit/mixins/plugin-api";
|
||||||
// docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version
|
// docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version
|
||||||
// using the format described at https://keepachangelog.com/en/1.0.0/.
|
// using the format described at https://keepachangelog.com/en/1.0.0/.
|
||||||
|
|
||||||
export const PLUGIN_API_VERSION = "1.19.0";
|
export const PLUGIN_API_VERSION = "1.20.0";
|
||||||
|
|
||||||
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
||||||
function canModify(klass, type, resolverName, changes) {
|
function canModify(klass, type, resolverName, changes) {
|
||||||
|
@ -1830,6 +1831,26 @@ class PluginApi {
|
||||||
addGlimmerSearchSuggestion(value);
|
addGlimmerSearchSuggestion(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a callback that will be evaluated when search menu assistant-items are clicked. Function
|
||||||
|
* takes an object as it's only argument. This object includes the updated term, searchTermChanged function,
|
||||||
|
* and the usage. If any callbacks return false, the core logic will be halted
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* api.addSearchMenuAssistantSelectCallback((args) => {
|
||||||
|
* if (args.usage !== "recent-search") {
|
||||||
|
* return true;
|
||||||
|
* }
|
||||||
|
* args.searchTermChanged(args.updatedTerm)
|
||||||
|
* return false;
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
addSearchMenuAssistantSelectCallback(fn) {
|
||||||
|
addSearchMenuAssistantSelectCallback(fn);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force a given menu panel (search-menu, user-menu) to be displayed as dropdown if ANY of the passed `classNames` are included in the `classList` of a menu panel.
|
* Force a given menu panel (search-menu, user-menu) to be displayed as dropdown if ANY of the passed `classNames` are included in the `classList` of a menu panel.
|
||||||
* This can be useful for plugins as the default behavior is to add a 'slide-in' behavior to a menu panel if you are viewing on a small screen. eg. mobile.
|
* This can be useful for plugins as the default behavior is to add a 'slide-in' behavior to a menu panel if you are viewing on a small screen. eg. mobile.
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
} from "@ember/test-helpers";
|
} from "@ember/test-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
import { DEFAULT_TYPE_FILTER } from "discourse/components/search-menu";
|
import { DEFAULT_TYPE_FILTER } from "discourse/components/search-menu";
|
||||||
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
import searchFixtures from "discourse/tests/fixtures/search-fixtures";
|
import searchFixtures from "discourse/tests/fixtures/search-fixtures";
|
||||||
import {
|
import {
|
||||||
acceptance,
|
acceptance,
|
||||||
|
@ -716,6 +717,28 @@ acceptance("Search - Glimmer - Authenticated", function (needs) {
|
||||||
"shows second recent search"
|
"shows second recent search"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("initial options - overriding behavior with addSearchMenuAssistantSelectCallback", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
await click("#search-button");
|
||||||
|
|
||||||
|
withPluginApi("1.20.0", (api) => {
|
||||||
|
api.addSearchMenuAssistantSelectCallback((args) => {
|
||||||
|
if (args.usage === "recent-search") {
|
||||||
|
args.searchTermChanged("hijacked!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await click(
|
||||||
|
".search-menu .search-menu-recent li:nth-of-type(1) .search-link"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(query("#search-term").value, "hijacked!");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
acceptance("Search - Glimmer - with tagging enabled", function (needs) {
|
acceptance("Search - Glimmer - with tagging enabled", function (needs) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { clearToolbarCallbacks } from "discourse/components/d-editor";
|
||||||
import { clearBulkButtons } from "discourse/components/modal/topic-bulk-actions";
|
import { clearBulkButtons } from "discourse/components/modal/topic-bulk-actions";
|
||||||
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
|
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
|
||||||
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
|
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
|
||||||
|
import { resetItemSelectCallbacks } from "discourse/components/search-menu/results/assistant-item";
|
||||||
import { resetOnKeyUpCallbacks } from "discourse/components/search-menu/search-term";
|
import { resetOnKeyUpCallbacks } from "discourse/components/search-menu/search-term";
|
||||||
import { resetTopicTitleDecorators } from "discourse/components/topic-title";
|
import { resetTopicTitleDecorators } from "discourse/components/topic-title";
|
||||||
import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/profile-tab-content";
|
import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/profile-tab-content";
|
||||||
|
@ -229,6 +230,7 @@ export function testCleanup(container, app) {
|
||||||
clearExtraHeaderIcons();
|
clearExtraHeaderIcons();
|
||||||
resetOnKeyDownCallbacks();
|
resetOnKeyDownCallbacks();
|
||||||
resetOnKeyUpCallbacks();
|
resetOnKeyUpCallbacks();
|
||||||
|
resetItemSelectCallbacks();
|
||||||
resetUserMenuTabs();
|
resetUserMenuTabs();
|
||||||
resetLinkLookup();
|
resetLinkLookup();
|
||||||
resetModelTransformers();
|
resetModelTransformers();
|
||||||
|
|
|
@ -7,6 +7,12 @@ in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.20.0] - 2023-12-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `addSearchMenuAssistantSelectCallback` function, which is used to override the behavior of clicking a search menu assistant item. If any callback returns false, the core behavior will not be executed.
|
||||||
|
|
||||||
## [1.19.0] - 2023-12-13
|
## [1.19.0] - 2023-12-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
Loading…
Reference in New Issue