From afb7e05d7b94d79126a3e5ebcc56dab02a839ecd Mon Sep 17 00:00:00 2001 From: Michal Fox Date: Fri, 25 Sep 2026 12:57:39 +0200 Subject: [PATCH] Bug 2012474 - Persist the project search exclude patterns as they are edited. r?bomsy --- .../mochitest/browser_dbg-project-search.js | 53 +++++++++++++++++++ .../client/shared/components/SearchInput.js | 39 +++++++++++--- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js b/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js index f1b638d..074c574 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js @@ -274,6 +274,59 @@ add_task(async function testSearchExcludePatterns() { pressKey(dbg, "Enter"); }); +add_task(async function testClearedExcludePatternsAreNotKept() { + const dbg = await initDebugger("doc-react.html", "App.js"); + + await openProjectSearch(dbg); + await doProjectSearch(dbg, "console", 5); + + info("Exclude the node_modules files"); + await clickElement(dbg, "excludePatternsInput"); + type(dbg, "**/node_modules/**"); + pressKey(dbg, "Enter"); + await waitForSearchResults(dbg, 2); + + info("Clear the exclude input and blur it without pressing Enter"); + await clearElement(dbg, "excludePatternsInput"); + await clickElement(dbg, "projectSearchSearchInput"); + await waitForState( + dbg, + () => + dbg.selectors.getSearchOptions("project-search").excludePatterns === "", + "The cleared exclude pattern is stored" + ); + + info("Re-trigger the search from the search input"); + pressKey(dbg, "Enter"); + const fileResults = await waitForSearchResults(dbg, 5); + const resultsFromNodeModules = [...fileResults].filter(result => + result.innerText.includes("node_modules") + ); + is( + resultsFromNodeModules.length, + 3, + "Results from node_modules are no longer excluded" + ); + + info("Close and reopen the debugger"); + await dbg.toolbox.closeToolbox(); + const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger"); + const dbg2 = createDebuggerContext(toolbox); + await openProjectSearch(dbg2); + + const excludePatternsInputElement = await waitForElement( + dbg2, + "excludePatternsInput" + ); + is( + excludePatternsInputElement.value, + "", + "The exclude input is still empty after reopening the debugger" + ); + + await clearElement(dbg2, "projectSearchSearchInput"); +}); + async function assertProjectSearchModifier( dbg, searchModifierBtn, diff --git a/devtools/client/shared/components/SearchInput.js b/devtools/client/shared/components/SearchInput.js index 74a8e8d..e28b558 100644 --- a/devtools/client/shared/components/SearchInput.js +++ b/devtools/client/shared/components/SearchInput.js @@ -20,6 +20,7 @@ const DebuggerImage = require("devtools/client/shared/components/DebuggerImage") const CloseButton = require("devtools/client/shared/components/CloseButton"); const classnames = require("resource://devtools/client/shared/classnames.js"); +const { debounce } = require("resource://devtools/shared/debounce.js"); const SearchModifiers = require("resource://devtools/client/shared/components/SearchModifiers.js"); const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); @@ -95,6 +96,12 @@ class SearchInput extends Component { ? (props.searchOptions?.excludePatterns ?? "") : null, }; + // Persist the exclude patterns while the user types, without dispatching + // on every keystroke. + this.debouncedSetExcludePatterns = debounce( + excludePatterns => this.setExcludePatterns(excludePatterns), + 200 + ); } componentDidMount() { @@ -107,6 +114,10 @@ class SearchInput extends Component { } } + componentWillUnmount() { + this.debouncedSetExcludePatterns.cancel(); + } + setFocus() { if (this.$input) { const _input = this.$input; @@ -198,13 +209,29 @@ class SearchInput extends Component { onExcludeKeyDown = e => { if (e.key === "Enter") { - this.props.setSearchOptions(this.props.searchKey, { - excludePatterns: this.state.excludePatterns, - }); + this.setExcludePatterns(this.state.excludePatterns); this.props.onKeyDown(e); } }; + onExcludeChange = e => { + const excludePatterns = e.target.value; + this.setState({ excludePatterns }); + this.debouncedSetExcludePatterns(excludePatterns); + }; + + onExcludeBlur = () => { + this.setExcludePatterns(this.state.excludePatterns); + }; + + setExcludePatterns(excludePatterns) { + this.debouncedSetExcludePatterns.cancel(); + if (this.props.searchOptions?.excludePatterns === excludePatterns) { + return; + } + this.props.setSearchOptions(this.props.searchKey, { excludePatterns }); + } + saveEnteredTerm(query) { const { history } = this.state; const previousIndex = history.indexOf(query); @@ -279,10 +306,8 @@ class SearchInput extends Component { placeholder: this.props.excludePatternsPlaceholder, value: this.state.excludePatterns, onKeyDown: this.onExcludeKeyDown, - onChange: e => - this.setState({ - excludePatterns: e.target.value, - }), + onChange: this.onExcludeChange, + onBlur: this.onExcludeBlur, }) ); }