From f5c5ae49ad47a488acf7b64775c3f89dfb7c0d53 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 23 Apr 2026 08:33:20 +0200 Subject: [PATCH 1/6] stop starting terriajs-server as part of dev gulp script --- gulpfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index a77781d9b4..082f31d312 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -292,9 +292,9 @@ serveTests.description = "Start jasmine-browser-runner server for interactive testing."; serveTests.displayName = "serve-tests"; -const dev = gulp.parallel(terriajsServer, watch, serveTests); +const dev = gulp.parallel(watch, serveTests); dev.description = - "Start TerriaJS server, watch for source changes, and serve tests."; + "Watch for source changes, and serve tests with jasmine-browser-runner."; const postNpmInstall = copyCesiumAssets; postNpmInstall.description = "Copy Cesium assets after installation."; From db4ce663ab65ddd8d2b4507420d809fdde90d5d0 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 23 Apr 2026 08:39:58 +0200 Subject: [PATCH 2/6] update changes.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index da11d21d08..b1a47cc7ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ - Upgrade babel packages to latest versions (7.28.0/7.29.0). - Upgrade webpack to version 5.105.4. - Upgrade eslint to v10 and migrate to eslint flat config. +- We no longer start terriajs-server when running `yarn gulp dev` as it is not needed for running tests in the browser with jasmine-browser-runner. If you need terriajs-server, you can start it separately by running `yarn gulp terriajs-server` in another terminal. #### 8.12.2 - 2026-03-27 From c23f9b2d8b813431ee2ceeea79eaf1360b14d0b5 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 23 Apr 2026 09:03:24 +0200 Subject: [PATCH 3/6] move catalog search provider to catalog --- lib/Models/Catalog/Catalog.ts | 17 +++++++++- .../SearchProviders/CatalogSearchProvider.ts | 8 ++--- lib/Models/SearchProviders/SearchBarModel.ts | 4 --- lib/Models/Terria.ts | 2 +- lib/ReactViewModels/SearchState.ts | 17 +++------- lib/ReactViewModels/ViewState.ts | 31 ++++++++++--------- test/Map/StyledHtmlSpec.tsx | 3 +- test/Models/TerriaSpec.ts | 9 ++---- .../SelectableDimensionWorkflowSpec.ts | 3 +- test/ReactViewModels/ViewStateSpec.ts | 9 +----- test/ReactViews/BottomDock/BottomDockSpec.tsx | 3 +- .../BottomDock/MapDataCountSpec.tsx | 3 +- .../Custom/Chart/BottomDockChartSpec.tsx | 3 +- .../Chart/FeatureInfoPanelChartSpec.tsx | 2 +- .../Custom/Chart/PointOnMapSpec.tsx | 3 +- .../SettingsPanelCustomComponentSpec.ts | 3 +- .../DataCatalog/DataCatalogItemSpec.tsx | 3 +- test/ReactViews/DataCatalogSpec.tsx | 3 +- test/ReactViews/DisclaimerSpec.tsx | 3 +- test/ReactViews/FeatureInfoPanelSpec.tsx | 3 +- test/ReactViews/FeatureInfoSectionSpec.tsx | 3 +- test/ReactViews/Generic/PromptSpec.tsx | 3 +- .../Map/Navigation/Compass/CompassSpec.tsx | 3 +- .../Compass/GyroscopeGuidanceSpec.tsx | 3 +- .../Map/Panels/HelpPanel/HelpPanelSpec.tsx | 3 +- .../Map/Panels/HelpPanel/VideoGuideSpec.tsx | 3 +- .../Panels/SharePanel/BuildShareLinkSpec.ts | 3 +- .../TerriaViewerWrapperSpec.tsx | 2 +- test/ReactViews/Mobile/MobileHeaderSpec.tsx | 3 +- .../Notification/NotificationSpec.tsx | 3 +- test/ReactViews/Search/BreadcrumbsSpec.tsx | 3 +- .../Search/SearchBoxAndResultsSpec.tsx | 9 ++++-- test/ReactViews/Search/SearchBoxSpec.tsx | 3 +- test/ReactViews/SidePanel/BrandingSpec.tsx | 3 +- .../TrainerBar/TrainerBarSpec.tsx | 3 +- test/ReactViews/StandardUserInterfaceSpec.tsx | 3 +- test/ReactViews/Story/StoryBuilderSpec.tsx | 3 +- test/ReactViews/ToolSpec.tsx | 3 +- .../ItemSearchTool/ItemSearchToolSpec.tsx | 3 +- test/ReactViews/Tour/TourPortalSpec.tsx | 3 +- test/ReactViews/WelcomeMessageSpec.tsx | 3 +- .../Workbench/Controls/IdealZoomSpec.tsx | 8 ++--- .../Controls/ViewingControlsSpec.tsx | 3 +- .../Controls/WorkbenchItemControlsSpec.tsx | 3 +- .../Workflows/WorkflowPanelSpec.tsx | 3 +- test/ViewModels/FeatureInfoPanelSpec.ts | 3 +- .../MapNavigation/MapToolbarSpec.ts | 3 +- test/ViewModels/ViewingControlsMenuSpec.ts | 3 +- 48 files changed, 90 insertions(+), 136 deletions(-) diff --git a/lib/Models/Catalog/Catalog.ts b/lib/Models/Catalog/Catalog.ts index beb1f40033..a6212fb1ce 100644 --- a/lib/Models/Catalog/Catalog.ts +++ b/lib/Models/Catalog/Catalog.ts @@ -7,18 +7,33 @@ import Terria from "../Terria"; import Group from "./Group"; import { BaseModel } from "../Definition/Model"; import isDefined from "../../Core/isDefined"; +import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; +import CatalogSearchProvider from "../SearchProviders/CatalogSearchProvider"; export default class Catalog { @observable group: Group & BaseModel; + @observable + searchProvider: CatalogSearchProviderMixin.Instance | undefined; + readonly terria: Terria; private _disposeCreateUserAddedGroup: () => void; - constructor(terria: Terria) { + constructor( + terria: Terria, + searchProvider?: CatalogSearchProviderMixin.Instance + ) { makeObservable(this); this.terria = terria; + this.searchProvider = + searchProvider ?? + new CatalogSearchProvider( + "catalog-search-provider", + terria, + terria.searchBarModel.minCharacters + ); this.group = new CatalogGroup("/", this.terria); this.terria.addModel(this.group); diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 47f96f7665..4e476c8d7f 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -110,16 +110,12 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( @observable isSearching: boolean = false; @observable debounceDurationOnceLoaded: number = 300; - constructor(id: string | undefined, terria: Terria) { + constructor(id: string | undefined, terria: Terria, minCharacters?: number) { super(id, terria); makeObservable(this); - this.setTrait( - CommonStrata.defaults, - "minCharacters", - terria.searchBarModel.minCharacters - ); + this.setTrait(CommonStrata.defaults, "minCharacters", minCharacters); } get type() { diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index 36454353ca..cfbde110e3 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -10,7 +10,6 @@ import RuntimeError from "terriajs-cesium/Source/Core/RuntimeError"; import { JsonObject } from "../../Core/Json"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; -import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; import { SearchBarTraits } from "../../Traits/SearchProviders/SearchBarTraits"; import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; @@ -26,9 +25,6 @@ import upsertSearchProviderFromJson from "./upsertSearchProviderFromJson"; export class SearchBarModel extends CreateModel(SearchBarTraits) { private locationSearchProviders = observable.map(); - @observable - catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; - constructor(readonly terria: Terria) { super("search-bar-model", terria); diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 4431cded08..04290fde81 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -459,9 +459,9 @@ export default class Terria { readonly indeterminateTileLoadProgressEvent = new CesiumEvent(); readonly workbench = new Workbench(); readonly overlays = new Workbench(); - readonly catalog = new Catalog(this); readonly baseMapsModel = new BaseMapsModel("basemaps", this); readonly searchBarModel = new SearchBarModel(this); + readonly catalog = new Catalog(this); readonly timelineClock = new Clock({ shouldAnimate: false }); // readonly overrides: any = overrides; // TODO: add options.functionOverrides like in master diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index fe71584bdf..776e646a8f 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -2,22 +2,19 @@ import { action, computed, IReactionDisposer, - observable, - reaction, makeObservable, - runInAction + observable, + reaction } from "mobx"; import filterOutUndefined from "../Core/filterOutUndefined"; +import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; import LocationSearchProviderMixin from "../ModelMixins/SearchProviders/LocationSearchProviderMixin"; import SearchProviderMixin from "../ModelMixins/SearchProviders/SearchProviderMixin"; -import CatalogSearchProvider from "../Models/SearchProviders/CatalogSearchProvider"; import SearchProviderResults from "../Models/SearchProviders/SearchProviderResults"; import Terria from "../Models/Terria"; -import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; interface SearchStateOptions { terria: Terria; - catalogSearchProvider?: CatalogSearchProviderMixin.Instance; } export default class SearchState { @@ -50,12 +47,6 @@ export default class SearchState { this.terria = options.terria; - runInAction(() => { - this.terria.searchBarModel.catalogSearchProvider = - options.catalogSearchProvider || - new CatalogSearchProvider("catalog-search-provider", options.terria); - }); - const self = this; this._catalogSearchDisposer = reaction( @@ -121,7 +112,7 @@ export default class SearchState { @computed get catalogSearchProvider(): CatalogSearchProviderMixin.Instance | undefined { - return this.terria.searchBarModel.catalogSearchProvider; + return this.terria.catalog.searchProvider; } @computed diff --git a/lib/ReactViewModels/ViewState.ts b/lib/ReactViewModels/ViewState.ts index b6bc502009..ced7f1a7df 100644 --- a/lib/ReactViewModels/ViewState.ts +++ b/lib/ReactViewModels/ViewState.ts @@ -2,12 +2,12 @@ import { action, computed, IReactionDisposer, + makeObservable, observable, reaction, - runInAction, - makeObservable + runInAction } from "mobx"; -import { ReactNode, MouseEvent, ComponentType, Ref } from "react"; +import { ComponentType, MouseEvent, ReactNode, Ref } from "react"; import defined from "terriajs-cesium/Source/Core/defined"; import addedByUser from "../Core/addedByUser"; import { @@ -22,9 +22,12 @@ import CatalogMemberMixin, { getName } from "../ModelMixins/CatalogMemberMixin"; import GroupMixin from "../ModelMixins/GroupMixin"; import MappableMixin from "../ModelMixins/MappableMixin"; import ReferenceMixin from "../ModelMixins/ReferenceMixin"; +import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; +import CzmlCatalogItem from "../Models/Catalog/CatalogItems/CzmlCatalogItem"; import CommonStrata from "../Models/Definition/CommonStrata"; import { BaseModel } from "../Models/Definition/Model"; import getAncestors from "../Models/getAncestors"; +import { getMarkerCatalogItem } from "../Models/LocationMarkerUtils"; import { SelectableDimension } from "../Models/SelectableDimensions/SelectableDimensions"; import Terria from "../Models/Terria"; import { ViewingControl } from "../Models/ViewingControls"; @@ -37,9 +40,6 @@ import { TourPoint } from "./defaultTourPoints"; import SearchState from "./SearchState"; -import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; -import { getMarkerCatalogItem } from "../Models/LocationMarkerUtils"; -import CzmlCatalogItem from "../Models/Catalog/CatalogItems/CzmlCatalogItem"; export const DATA_CATALOG_NAME = "data-catalog"; export const USER_DATA_NAME = "my-data"; @@ -50,8 +50,10 @@ export const WORKBENCH_RESIZE_ANIMATION_DURATION = 500; interface ViewStateOptions { terria: Terria; - catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; - errorHandlingProvider?: any; + /** + * @deprecated + */ + catalogSearchProvider?: CatalogSearchProviderMixin.Instance; } /** @@ -203,8 +205,6 @@ export default class ViewState { } } - errorProvider: any | null = null; - // default value is null, because user has not made decision to show or // not show story // will be explicitly set to false when user 1. dismiss story @@ -391,13 +391,14 @@ export default class ViewState { makeObservable(this); const terria = options.terria; this.searchState = new SearchState({ - terria, - catalogSearchProvider: options.catalogSearchProvider + terria }); + if (options.catalogSearchProvider) { + runInAction(() => { + this.terria.catalog.searchProvider = options.catalogSearchProvider; + }); + } - this.errorProvider = options.errorHandlingProvider - ? options.errorHandlingProvider - : null; this.terria = terria; // When features are picked, show the feature info panel. diff --git a/test/Map/StyledHtmlSpec.tsx b/test/Map/StyledHtmlSpec.tsx index 39b5b67a5a..007e08a720 100644 --- a/test/Map/StyledHtmlSpec.tsx +++ b/test/Map/StyledHtmlSpec.tsx @@ -16,8 +16,7 @@ describe("StyledHtml", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/Models/TerriaSpec.ts b/test/Models/TerriaSpec.ts index 5054d38aeb..6d029fdd35 100644 --- a/test/Models/TerriaSpec.ts +++ b/test/Models/TerriaSpec.ts @@ -565,8 +565,7 @@ describe("TerriaSpec", function () { beforeEach(function () { newTerria = new Terria({ appBaseHref: "/", baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); UrlToCatalogMemberMapping.register( @@ -793,8 +792,7 @@ describe("TerriaSpec", function () { )}`; newTerria = new Terria({ baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); await Promise.all( @@ -904,8 +902,7 @@ describe("TerriaSpec", function () { "https://magda.example.com/api/v0/registry/records/map-config-example?optionalAspect=terria-config&optionalAspect=terria-init&optionalAspect=group&dereference=true"; viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); newTerria = new Terria({ baseUrl: "./" }); diff --git a/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts b/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts index dcac113c67..4b72142761 100644 --- a/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts +++ b/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts @@ -13,8 +13,7 @@ describe("SelectableDimensionWorkflow", function () { beforeEach(function () { terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ReactViewModels/ViewStateSpec.ts b/test/ReactViewModels/ViewStateSpec.ts index 8874ba2bc2..ae41d3bd35 100644 --- a/test/ReactViewModels/ViewStateSpec.ts +++ b/test/ReactViewModels/ViewStateSpec.ts @@ -16,8 +16,7 @@ describe("ViewState", function () { beforeEach(function () { terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); @@ -61,12 +60,6 @@ describe("ViewState", function () { }); }); - describe("error provider", function () { - it("creates an empty error provider by default", function () { - expect(viewState.errorProvider).toBeNull(); - }); - }); - describe("tourPointsWithValidRefs", function () { it("returns tourPoints ordered by priority", function () { runInAction(() => { diff --git a/test/ReactViews/BottomDock/BottomDockSpec.tsx b/test/ReactViews/BottomDock/BottomDockSpec.tsx index 641087177a..5d10fb07b0 100644 --- a/test/ReactViews/BottomDock/BottomDockSpec.tsx +++ b/test/ReactViews/BottomDock/BottomDockSpec.tsx @@ -12,8 +12,7 @@ describe("BottomDock", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/BottomDock/MapDataCountSpec.tsx b/test/ReactViews/BottomDock/MapDataCountSpec.tsx index c3d387fa50..dec6af627e 100644 --- a/test/ReactViews/BottomDock/MapDataCountSpec.tsx +++ b/test/ReactViews/BottomDock/MapDataCountSpec.tsx @@ -14,8 +14,7 @@ describe("MapDataCount", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Custom/Chart/BottomDockChartSpec.tsx b/test/ReactViews/Custom/Chart/BottomDockChartSpec.tsx index 0a98a9e6cc..6f560112aa 100644 --- a/test/ReactViews/Custom/Chart/BottomDockChartSpec.tsx +++ b/test/ReactViews/Custom/Chart/BottomDockChartSpec.tsx @@ -15,8 +15,7 @@ describe("BottomDockChart", function () { baseUrl: "./" }); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); chartItems = [ { diff --git a/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx b/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx index 8335a999a2..91ca9d8533 100644 --- a/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx +++ b/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx @@ -33,7 +33,7 @@ describe("FeatureInfoPanelChart", function () { context = { terria: new Terria(), - viewState: new ViewState({ terria, catalogSearchProvider: undefined }), + viewState: new ViewState({ terria }), feature: new TerriaFeature({}), catalogItem }; diff --git a/test/ReactViews/Custom/Chart/PointOnMapSpec.tsx b/test/ReactViews/Custom/Chart/PointOnMapSpec.tsx index 3244a32d7d..7fecd941a1 100644 --- a/test/ReactViews/Custom/Chart/PointOnMapSpec.tsx +++ b/test/ReactViews/Custom/Chart/PointOnMapSpec.tsx @@ -13,8 +13,7 @@ describe("PointOnMap", function () { baseUrl: "./" }); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ReactViews/Custom/SettingsPanelCustomComponentSpec.ts b/test/ReactViews/Custom/SettingsPanelCustomComponentSpec.ts index 2b182c41fc..abbe9b106c 100644 --- a/test/ReactViews/Custom/SettingsPanelCustomComponentSpec.ts +++ b/test/ReactViews/Custom/SettingsPanelCustomComponentSpec.ts @@ -12,8 +12,7 @@ describe("SettingsPanelCustomComponent", function () { beforeEach(function () { viewState = new ViewState({ - terria: new Terria(), - catalogSearchProvider: undefined + terria: new Terria() }); registerCustomComponentTypes(viewState.terria); diff --git a/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx b/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx index b384c9ba95..a5f318f0d2 100644 --- a/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx +++ b/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx @@ -27,8 +27,7 @@ describe("DataCatalogItem", () => { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); wmsItem = new WebMapServiceCatalogItem("test", terria); diff --git a/test/ReactViews/DataCatalogSpec.tsx b/test/ReactViews/DataCatalogSpec.tsx index 3e3ccbcbdf..07ca7754de 100644 --- a/test/ReactViews/DataCatalogSpec.tsx +++ b/test/ReactViews/DataCatalogSpec.tsx @@ -24,8 +24,7 @@ describe("DataCatalog", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/DisclaimerSpec.tsx b/test/ReactViews/DisclaimerSpec.tsx index 36dc731982..3e9728ee57 100644 --- a/test/ReactViews/DisclaimerSpec.tsx +++ b/test/ReactViews/DisclaimerSpec.tsx @@ -14,8 +14,7 @@ describe("Disclaimer", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/FeatureInfoPanelSpec.tsx b/test/ReactViews/FeatureInfoPanelSpec.tsx index 011a469b4c..9203102963 100644 --- a/test/ReactViews/FeatureInfoPanelSpec.tsx +++ b/test/ReactViews/FeatureInfoPanelSpec.tsx @@ -24,8 +24,7 @@ describe("FeatureInfoPanel", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/FeatureInfoSectionSpec.tsx b/test/ReactViews/FeatureInfoSectionSpec.tsx index 7202e5a23c..13e3751aa1 100644 --- a/test/ReactViews/FeatureInfoSectionSpec.tsx +++ b/test/ReactViews/FeatureInfoSectionSpec.tsx @@ -64,8 +64,7 @@ describe("FeatureInfoSection", function () { catalogItem = new TestModel("teststrata", terria); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); const properties = { name: "Kay", diff --git a/test/ReactViews/Generic/PromptSpec.tsx b/test/ReactViews/Generic/PromptSpec.tsx index ebe39de0c6..7f00b691f1 100644 --- a/test/ReactViews/Generic/PromptSpec.tsx +++ b/test/ReactViews/Generic/PromptSpec.tsx @@ -13,8 +13,7 @@ describe("HelpPrompt", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx b/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx index edf0ddd37d..8f671c57e1 100644 --- a/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx +++ b/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx @@ -14,8 +14,7 @@ describe("Compass", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx b/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx index bfabb5330c..25e1db8069 100644 --- a/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx +++ b/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx @@ -13,8 +13,7 @@ describe("GyroscopeGuidance", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx b/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx index 80a571364e..315e032c83 100644 --- a/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx +++ b/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx @@ -14,8 +14,7 @@ describe("HelpPanel", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx b/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx index 1e465a398f..3d5de2c68e 100644 --- a/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx +++ b/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx @@ -17,8 +17,7 @@ describe("VideoGuide", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts b/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts index fcebf80911..921fd9f03f 100644 --- a/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts +++ b/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts @@ -36,8 +36,7 @@ beforeEach(function () { // }; viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Map/TerriaViewerWrapper/TerriaViewerWrapperSpec.tsx b/test/ReactViews/Map/TerriaViewerWrapper/TerriaViewerWrapperSpec.tsx index 4c318bab30..15b6a00d09 100644 --- a/test/ReactViews/Map/TerriaViewerWrapper/TerriaViewerWrapperSpec.tsx +++ b/test/ReactViews/Map/TerriaViewerWrapper/TerriaViewerWrapperSpec.tsx @@ -13,7 +13,7 @@ describe("TerriaViewerWrapper", function () { beforeEach(function () { terria = new Terria(); - viewState = new ViewState({ terria, catalogSearchProvider: undefined }); + viewState = new ViewState({ terria }); }); describe("when main viewer is in leaflet mode", function () { diff --git a/test/ReactViews/Mobile/MobileHeaderSpec.tsx b/test/ReactViews/Mobile/MobileHeaderSpec.tsx index 29b8a2c040..3dd595a426 100644 --- a/test/ReactViews/Mobile/MobileHeaderSpec.tsx +++ b/test/ReactViews/Mobile/MobileHeaderSpec.tsx @@ -18,8 +18,7 @@ describe("MobileHeader", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Notification/NotificationSpec.tsx b/test/ReactViews/Notification/NotificationSpec.tsx index adff14828f..4ca6dd83fc 100644 --- a/test/ReactViews/Notification/NotificationSpec.tsx +++ b/test/ReactViews/Notification/NotificationSpec.tsx @@ -14,8 +14,7 @@ describe("Notification", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Search/BreadcrumbsSpec.tsx b/test/ReactViews/Search/BreadcrumbsSpec.tsx index efa915bd28..f57f76e047 100644 --- a/test/ReactViews/Search/BreadcrumbsSpec.tsx +++ b/test/ReactViews/Search/BreadcrumbsSpec.tsx @@ -15,8 +15,7 @@ describe("Breadcrumbs", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); catalogGroup = new CatalogGroup("group-of-geospatial-cats", terria); terria.addModel(catalogGroup); diff --git a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx index 37eacc604a..7f77d53dda 100644 --- a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx +++ b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx @@ -18,9 +18,12 @@ describe("SearchBoxAndResults", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: new CatalogSearchProvider("catalog", terria) + terria: terria }); + terria.catalog.searchProvider = new CatalogSearchProvider( + "catalog-search-provider", + terria + ); }); it("renders with an input(SearchBox), but no SearchInDataCatalog without showLocationSearchResults", function () { @@ -74,7 +77,7 @@ describe("SearchBoxAndResults", function () { viewState.searchState.locationSearchText = searchText; viewState.searchState.showLocationSearchResults = true; viewState.searchState.locationSearchResults = []; - viewState.terria.searchBarModel.catalogSearchProvider = undefined; + viewState.terria.catalog.searchProvider = undefined; }); renderWithContexts( diff --git a/test/ReactViews/Search/SearchBoxSpec.tsx b/test/ReactViews/Search/SearchBoxSpec.tsx index e288fa1a3d..e3e101c6f3 100644 --- a/test/ReactViews/Search/SearchBoxSpec.tsx +++ b/test/ReactViews/Search/SearchBoxSpec.tsx @@ -14,8 +14,7 @@ describe("SearchBox", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/SidePanel/BrandingSpec.tsx b/test/ReactViews/SidePanel/BrandingSpec.tsx index c96ba00555..f58c6f9cf9 100644 --- a/test/ReactViews/SidePanel/BrandingSpec.tsx +++ b/test/ReactViews/SidePanel/BrandingSpec.tsx @@ -11,8 +11,7 @@ describe("Branding", function () { beforeEach(function () { terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx b/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx index 16a35fb8bd..ad6ec7a0bf 100644 --- a/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx +++ b/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx @@ -15,8 +15,7 @@ describe("TrainerBar", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/StandardUserInterfaceSpec.tsx b/test/ReactViews/StandardUserInterfaceSpec.tsx index a524724b1b..46ae776ddd 100644 --- a/test/ReactViews/StandardUserInterfaceSpec.tsx +++ b/test/ReactViews/StandardUserInterfaceSpec.tsx @@ -22,8 +22,7 @@ describe("StandardUserInterface", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Story/StoryBuilderSpec.tsx b/test/ReactViews/Story/StoryBuilderSpec.tsx index 9952287cd1..0d66bdbd1e 100644 --- a/test/ReactViews/Story/StoryBuilderSpec.tsx +++ b/test/ReactViews/Story/StoryBuilderSpec.tsx @@ -14,8 +14,7 @@ describe("StoryBuilder", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); runInAction(() => { viewState.storyBuilderShown = true; diff --git a/test/ReactViews/ToolSpec.tsx b/test/ReactViews/ToolSpec.tsx index aec545b2c5..f03254cef3 100644 --- a/test/ReactViews/ToolSpec.tsx +++ b/test/ReactViews/ToolSpec.tsx @@ -12,8 +12,7 @@ describe("Tool", function () { beforeEach(function () { const terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx b/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx index 3ebc35fce1..b8067fe92a 100644 --- a/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx +++ b/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx @@ -47,8 +47,7 @@ describe("ItemSearchTool", function () { registerItemSearchProvider("testProvider", TestItemSearchProvider); const terria: Terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); item = new MockSearchableItem("test", terria); item.setTrait(CommonStrata.user, "search", { diff --git a/test/ReactViews/Tour/TourPortalSpec.tsx b/test/ReactViews/Tour/TourPortalSpec.tsx index f48b8bfdcf..154ec76f91 100644 --- a/test/ReactViews/Tour/TourPortalSpec.tsx +++ b/test/ReactViews/Tour/TourPortalSpec.tsx @@ -16,8 +16,7 @@ describe("TourPortal", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/WelcomeMessageSpec.tsx b/test/ReactViews/WelcomeMessageSpec.tsx index e7876f0d5c..61a2784344 100644 --- a/test/ReactViews/WelcomeMessageSpec.tsx +++ b/test/ReactViews/WelcomeMessageSpec.tsx @@ -14,8 +14,7 @@ describe("WelcomeMessage", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); }); diff --git a/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx b/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx index a6eb0d5f28..4b25dcb884 100644 --- a/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx +++ b/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx @@ -25,11 +25,9 @@ describe("Ideal Zoom", function () { theItem = new Cesium3DTilesCatalogItem("my3dtiles", terria); theItem.setTrait("definition", "url", "/test/Cesium3DTiles/tileset.json"); - const options = { - terria: terria, - catalogSearchProvider: undefined - }; - viewState = new ViewState(options); + viewState = new ViewState({ + terria: terria + }); }); it("should use default camera view if no parameters are given.", async function () { diff --git a/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx b/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx index 0cb0cff7a8..67fd408918 100644 --- a/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx +++ b/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx @@ -15,8 +15,7 @@ describe("ViewingControls", function () { beforeEach(function () { terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ReactViews/Workbench/Controls/WorkbenchItemControlsSpec.tsx b/test/ReactViews/Workbench/Controls/WorkbenchItemControlsSpec.tsx index 09403b547f..458f271d1a 100644 --- a/test/ReactViews/Workbench/Controls/WorkbenchItemControlsSpec.tsx +++ b/test/ReactViews/Workbench/Controls/WorkbenchItemControlsSpec.tsx @@ -19,8 +19,7 @@ describe("WorkbenchItemControls", function () { baseUrl: "./" }); viewState = new ViewState({ - terria: terria, - catalogSearchProvider: undefined + terria: terria }); item = new WebMapServiceCatalogItem("test-item", terria); diff --git a/test/ReactViews/Workflows/WorkflowPanelSpec.tsx b/test/ReactViews/Workflows/WorkflowPanelSpec.tsx index a0b7fbd594..6cc8dd408d 100644 --- a/test/ReactViews/Workflows/WorkflowPanelSpec.tsx +++ b/test/ReactViews/Workflows/WorkflowPanelSpec.tsx @@ -14,8 +14,7 @@ describe("WorkflowPanel", function () { terria.configParameters.regionMappingDefinitionsUrl = "./data/regionMapping.json"; viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ViewModels/FeatureInfoPanelSpec.ts b/test/ViewModels/FeatureInfoPanelSpec.ts index 749a645b62..a53c2c2315 100644 --- a/test/ViewModels/FeatureInfoPanelSpec.ts +++ b/test/ViewModels/FeatureInfoPanelSpec.ts @@ -11,8 +11,7 @@ describe("FeatureInfoPanelViewModel", function () { beforeEach(function () { terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ViewModels/MapNavigation/MapToolbarSpec.ts b/test/ViewModels/MapNavigation/MapToolbarSpec.ts index 5a77c7ea66..4df698e3a6 100644 --- a/test/ViewModels/MapNavigation/MapToolbarSpec.ts +++ b/test/ViewModels/MapNavigation/MapToolbarSpec.ts @@ -10,8 +10,7 @@ describe("MapToolbar", function () { beforeEach(function () { terria = new Terria(); viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); }); diff --git a/test/ViewModels/ViewingControlsMenuSpec.ts b/test/ViewModels/ViewingControlsMenuSpec.ts index 2aafbdfec9..28f526c40d 100644 --- a/test/ViewModels/ViewingControlsMenuSpec.ts +++ b/test/ViewModels/ViewingControlsMenuSpec.ts @@ -9,8 +9,7 @@ describe("ViewingControlsMenu", function () { it("adds the menu item generator function to `viewState.globalViewingControlOptions` array", function () { const terria = new Terria(); const viewState = new ViewState({ - terria, - catalogSearchProvider: undefined + terria }); expect(viewState.globalViewingControlOptions.length).toEqual(0); const generateFunction = () => ({ From 6e098bc685696fd3ec14e715df86a5480fcd2219 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 23 Apr 2026 09:08:04 +0200 Subject: [PATCH 4/6] update changes.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index b1a47cc7ec..04b5c73b2d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ - Upgrade webpack to version 5.105.4. - Upgrade eslint to v10 and migrate to eslint flat config. - We no longer start terriajs-server when running `yarn gulp dev` as it is not needed for running tests in the browser with jasmine-browser-runner. If you need terriajs-server, you can start it separately by running `yarn gulp terriajs-server` in another terminal. +- Move catalog-search-provider instance from SearchBarModel to Catalog class. #### 8.12.2 - 2026-03-27 From a4f7c2e55f944c06d2e7ea5e315461d2bdce4d12 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 24 Apr 2026 08:35:54 +0200 Subject: [PATCH 5/6] sync min characters between models --- lib/Models/Catalog/Catalog.ts | 17 +++++++------ .../SearchProviders/CatalogSearchProvider.ts | 24 +++++++++++++++++-- lib/Models/SearchProviders/SearchBarModel.ts | 14 ++++++++++- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/Models/Catalog/Catalog.ts b/lib/Models/Catalog/Catalog.ts index a6212fb1ce..52062c9e4f 100644 --- a/lib/Models/Catalog/Catalog.ts +++ b/lib/Models/Catalog/Catalog.ts @@ -23,17 +23,20 @@ export default class Catalog { constructor( terria: Terria, - searchProvider?: CatalogSearchProviderMixin.Instance + options: { searchProvider?: CatalogSearchProviderMixin.Instance } = {} ) { makeObservable(this); this.terria = terria; - this.searchProvider = - searchProvider ?? - new CatalogSearchProvider( - "catalog-search-provider", + if ("searchProvider" in options) { + this.searchProvider = options.searchProvider; + } else { + this.searchProvider = CatalogSearchProvider.fromOptions({ + id: "catalog-search-provider", terria, - terria.searchBarModel.minCharacters - ); + minCharacters: this.terria.searchBarModel.minCharacters + }); + } + this.group = new CatalogGroup("/", this.terria); this.terria.addModel(this.group); diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 4e476c8d7f..ebe92c2f8e 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -110,12 +110,32 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( @observable isSearching: boolean = false; @observable debounceDurationOnceLoaded: number = 300; - constructor(id: string | undefined, terria: Terria, minCharacters?: number) { + constructor(id: string | undefined, terria: Terria) { super(id, terria); makeObservable(this); + } + + static fromOptions({ + id, + terria, + minCharacters + }: { + id: string | undefined; + terria: Terria; + minCharacters?: number; + }) { + const searchProvider = new CatalogSearchProvider(id, terria); + + if (minCharacters !== undefined) { + searchProvider.setTrait( + CommonStrata.defaults, + "minCharacters", + minCharacters + ); + } - this.setTrait(CommonStrata.defaults, "minCharacters", minCharacters); + return searchProvider; } get type() { diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index cfbde110e3..c978174225 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -3,7 +3,8 @@ import { computed, isObservableArray, makeObservable, - observable + observable, + reaction } from "mobx"; import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; import RuntimeError from "terriajs-cesium/Source/Core/RuntimeError"; @@ -28,6 +29,17 @@ export class SearchBarModel extends CreateModel(SearchBarTraits) { constructor(readonly terria: Terria) { super("search-bar-model", terria); + reaction( + () => this.minCharacters, + (minCharacters) => { + this.terria.catalog.searchProvider?.setTrait( + CommonStrata.defaults, + "minCharacters", + minCharacters + ); + } + ); + makeObservable(this); } From ae8c3795c1205c6715480e13cc5b23a1196f9be5 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 25 Apr 2026 20:57:44 +0200 Subject: [PATCH 6/6] move debounce duration to traits --- lib/Models/SearchProviders/CatalogSearchProvider.ts | 1 - lib/ReactViews/ExplorerWindow/Tabs/DataCatalogTab.tsx | 2 +- lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts | 8 ++++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index ebe92c2f8e..8a40c318a0 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -108,7 +108,6 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( ) { static readonly type = "catalog-search-provider"; @observable isSearching: boolean = false; - @observable debounceDurationOnceLoaded: number = 300; constructor(id: string | undefined, terria: Terria) { super(id, terria); diff --git a/lib/ReactViews/ExplorerWindow/Tabs/DataCatalogTab.tsx b/lib/ReactViews/ExplorerWindow/Tabs/DataCatalogTab.tsx index 7f40a83149..0bbaf3d1b0 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/DataCatalogTab.tsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/DataCatalogTab.tsx @@ -58,7 +58,7 @@ const DataCatalogTab = observer(function DataCatalogTab( terria.catalogReferencesLoaded ? ( searchState.catalogSearchProvider as CatalogSearchProvider - ).debounceDurationOnceLoaded + ).debounceDuration : DEBOUNCE_INTERVAL } /> diff --git a/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts b/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts index bac1973480..91cbda0821 100644 --- a/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts @@ -11,4 +11,12 @@ export default class CatalogSearchProviderTraits extends mixTraits( description: "Name of the search provider." }) name: string = "Catalog items"; + + @primitiveTrait({ + type: "number", + name: "Debounce duration", + description: + "The debounce duration (in milliseconds) to apply to search input once the provider is loaded. This is applied to prevent excessive searching while the user is typing. The default value is 300ms." + }) + debounceDuration?: number = 300; }