From 3c6362bb26ceead1f6a1c16fdfa1f28a97b1ae93 Mon Sep 17 00:00:00 2001 From: Mark VanLandingham Date: Wed, 20 Dec 2023 15:25:45 -0600 Subject: [PATCH] DEV: PluginApi function to customize search menu assistant item behavior (#24992) --- .../search-menu/results/assistant-item.hbs | 1 + .../search-menu/results/assistant-item.js | 35 ++++++++++++++++--- .../search-menu/results/initial-options.hbs | 4 ++- .../search-menu/results/random-quick-tip.js | 2 +- .../search-menu/results/recent-searches.hbs | 1 + .../discourse/app/lib/plugin-api.js | 23 +++++++++++- .../tests/acceptance/glimmer-search-test.js | 23 ++++++++++++ .../discourse/tests/helpers/qunit-helpers.js | 2 ++ docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md | 6 ++++ 9 files changed, 89 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.hbs b/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.hbs index 8f5f3263202..c94ff2d72b6 100644 --- a/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.hbs +++ b/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.hbs @@ -4,6 +4,7 @@ class="search-menu-assistant-item" {{on "keydown" this.onKeydown}} {{on "click" this.onClick}} + data-usage={{@usage}} > diff --git a/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.js b/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.js index cf6cf9eda89..98ca9001b21 100644 --- a/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.js +++ b/app/assets/javascripts/discourse/app/components/search-menu/results/assistant-item.js @@ -5,6 +5,15 @@ import { focusSearchInput } from "discourse/components/search-menu"; import getURL from "discourse-common/lib/get-url"; 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 { @service search; @service appEvents; @@ -85,16 +94,32 @@ export default class AssistantItem extends Component { @debounce(100) itemSelected() { - let updatedValue = ""; + let updatedTerm = ""; if (this.args.slug) { - updatedValue = this.prefix.concat(this.args.slug); + updatedTerm = this.prefix.concat(this.args.slug); } else { - updatedValue = this.prefix.trim(); + updatedTerm = this.prefix.trim(); } const inTopicContext = this.search.searchContext?.type === "topic"; - this.args.searchTermChanged(updatedValue, { - searchTopics: !inTopicContext || this.search.activeGlobalSearchTerm, + const 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 && !this.args.searchAllTopics && { setTopicContext: true }), }); diff --git a/app/assets/javascripts/discourse/app/components/search-menu/results/initial-options.hbs b/app/assets/javascripts/discourse/app/components/search-menu/results/initial-options.hbs index 096953443e4..e149ab71931 100644 --- a/app/assets/javascripts/discourse/app/components/search-menu/results/initial-options.hbs +++ b/app/assets/javascripts/discourse/app/components/search-menu/results/initial-options.hbs @@ -48,7 +48,9 @@ /> {{/if}} {{else}} - + {{#if (and this.currentUser this.siteSettings.log_search_queries)}} {{/each}} diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js index 377254ebf07..4fafc62a6c2 100644 --- a/app/assets/javascripts/discourse/app/lib/plugin-api.js +++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js @@ -18,6 +18,7 @@ import { } from "discourse/components/reviewable-item"; import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options"; import { addSearchSuggestion as addGlimmerSearchSuggestion } from "discourse/components/search-menu/results/assistant"; +import { addItemSelectCallback as addSearchMenuAssistantSelectCallback } from "discourse/components/search-menu/results/assistant-item"; import { addQuickSearchRandomTip, removeDefaultQuickSearchRandomTips, @@ -143,7 +144,7 @@ import { modifySelectKit } from "select-kit/mixins/plugin-api"; // docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version // 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. function canModify(klass, type, resolverName, changes) { @@ -1830,6 +1831,26 @@ class PluginApi { 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. * 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. diff --git a/app/assets/javascripts/discourse/tests/acceptance/glimmer-search-test.js b/app/assets/javascripts/discourse/tests/acceptance/glimmer-search-test.js index 1ff702fa2c7..9b868b310fb 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/glimmer-search-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/glimmer-search-test.js @@ -7,6 +7,7 @@ import { } from "@ember/test-helpers"; import { test } from "qunit"; 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 { acceptance, @@ -716,6 +717,28 @@ acceptance("Search - Glimmer - Authenticated", function (needs) { "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) { diff --git a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js index c428f56bf43..47f04e4ed4a 100644 --- a/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js +++ b/app/assets/javascripts/discourse/tests/helpers/qunit-helpers.js @@ -21,6 +21,7 @@ import { clearToolbarCallbacks } from "discourse/components/d-editor"; import { clearBulkButtons } from "discourse/components/modal/topic-bulk-actions"; import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget"; 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 { resetTopicTitleDecorators } from "discourse/components/topic-title"; import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/profile-tab-content"; @@ -229,6 +230,7 @@ export function testCleanup(container, app) { clearExtraHeaderIcons(); resetOnKeyDownCallbacks(); resetOnKeyUpCallbacks(); + resetItemSelectCallbacks(); resetUserMenuTabs(); resetLinkLookup(); resetModelTransformers(); diff --git a/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md b/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md index 4bd3a041e25..7d5ef2b216d 100644 --- a/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md +++ b/docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md @@ -7,6 +7,12 @@ in this file. 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). +## [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 ### Added