From aeb086d058dd1120b81574184207f005d0adce89 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:30:38 +0000 Subject: [PATCH] Presentation: Fix `Presentation.selection.selectionChange` event not being emitted for `BlankConnection` (backport #7002) [release/4.7.x] (#7022) Co-authored-by: Saulius Skliutas <24278440+saskliutas@users.noreply.github.com> Co-authored-by: Grigas Petraitis <35135765+grigasp@users.noreply.github.com> --- ...tation-selection-fix_2024-07-25-12-28.json | 10 ++++++++++ common/config/rush/pnpm-lock.yaml | 8 ++++---- full-stack-tests/presentation/package.json | 2 +- .../selection/SelectionManager.ts | 5 +++++ .../test/selection/SelectionManager.test.ts | 19 ++++++++++++++++++- 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 common/changes/@itwin/presentation-frontend/presentation-selection-fix_2024-07-25-12-28.json diff --git a/common/changes/@itwin/presentation-frontend/presentation-selection-fix_2024-07-25-12-28.json b/common/changes/@itwin/presentation-frontend/presentation-selection-fix_2024-07-25-12-28.json new file mode 100644 index 000000000000..8f574515be4b --- /dev/null +++ b/common/changes/@itwin/presentation-frontend/presentation-selection-fix_2024-07-25-12-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/presentation-frontend", + "comment": "Fix `Presentation.selection.selectionChange` event not being emitted for `BlankConnection`.", + "type": "none" + } + ], + "packageName": "@itwin/presentation-frontend" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 98676be8e599..fac9479c9e18 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1959,7 +1959,7 @@ importers: eslint-config-prettier: ^9.1.0 faker: ^4.1.0 fast-sort: ^3.0.2 - fast-xml-parser: ^4.3.5 + fast-xml-parser: ^4.4.1 i18next-http-backend: ^1.4.4 internal-tools: workspace:* jsdom: ^19.0.0 @@ -2007,7 +2007,7 @@ importers: deep-equal: 1.0.0 faker: 4.1.0 fast-sort: 3.0.2 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.4.1 mocha: 10.2.0 rimraf: 3.0.2 sinon: 17.0.1 @@ -8059,8 +8059,8 @@ packages: resolution: {integrity: sha512-DIKDkHBt+IubEEU44/kEulX3SbIuufEHIhRfw0MCh7f/HLGWmR/Dk7xg2tapT28pVFqnEV1ZDtl6SeViAyVe4Q==} dev: false - /fast-xml-parser/4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser/4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} hasBin: true dependencies: strnum: 1.0.5 diff --git a/full-stack-tests/presentation/package.json b/full-stack-tests/presentation/package.json index 78dc5f22bf96..f789505de1f9 100644 --- a/full-stack-tests/presentation/package.json +++ b/full-stack-tests/presentation/package.json @@ -57,7 +57,7 @@ "deep-equal": "^1", "faker": "^4.1.0", "fast-sort": "^3.0.2", - "fast-xml-parser": "^4.3.5", + "fast-xml-parser": "^4.4.1", "mocha": "^10.2.0", "rimraf": "^3.0.2", "sinon": "^17.0.1", diff --git a/presentation/frontend/src/presentation-frontend/selection/SelectionManager.ts b/presentation/frontend/src/presentation-frontend/selection/SelectionManager.ts index f85a63921408..693b2cabd1b9 100644 --- a/presentation/frontend/src/presentation-frontend/selection/SelectionManager.ts +++ b/presentation/frontend/src/presentation-frontend/selection/SelectionManager.ts @@ -160,6 +160,10 @@ export class SelectionManager implements ISelectionProvider { } private handleEvent(evt: SelectionChangeEventArgs): void { + if (!this._knownIModels.has(evt.imodel.key)) { + this._knownIModels.set(evt.imodel.key, evt.imodel); + } + switch (evt.changeType) { case SelectionChangeType.Add: this._selectionStorage.addToSelection({ @@ -370,6 +374,7 @@ export class SelectionManager implements ISelectionProvider { return this._currentSelection.computeSelection(args.iModelKey, args.level, currentSelectables, args.selectables).pipe( mergeMap(({ level, changedSelection }): Observable => { const imodel = this._knownIModels.get(args.iModelKey); + // istanbul ignore if if (!imodel) { return EMPTY; } diff --git a/presentation/frontend/src/test/selection/SelectionManager.test.ts b/presentation/frontend/src/test/selection/SelectionManager.test.ts index b260a3657585..be30c21386c8 100644 --- a/presentation/frontend/src/test/selection/SelectionManager.test.ts +++ b/presentation/frontend/src/test/selection/SelectionManager.test.ts @@ -6,7 +6,7 @@ import { expect } from "chai"; import * as sinon from "sinon"; import { assert, BeDuration, Id64, Id64Arg, Id64String, StopWatch, using } from "@itwin/core-bentley"; -import { IModelApp, IModelConnection, SelectionSet, SelectionSetEventType } from "@itwin/core-frontend"; +import { BlankConnection, IModelApp, IModelConnection, SelectionSet, SelectionSetEventType } from "@itwin/core-frontend"; import { InstanceKey, KeySet, NodeKey, SelectionScope, StandardNodeTypes } from "@itwin/presentation-common"; import { createRandomId, @@ -432,6 +432,23 @@ describe("SelectionManager", () => { selectionManager.replaceSelection(source, imodel, []); expect(raiseEventSpy, "Expected selectionChange.raiseEvent to not be called").to.not.have.been.called; }); + + it("fires `selectionChange` event after `addToSelection`, `replaceSelection`, `clearSelection`, `removeFromSelection` with `BlankConnection", async () => { + // creating blank connection does not raise `IModelConnection.onOpen` event. + const blankImodel = { key: "blank", name: "blankConnection" } as BlankConnection; + const raiseEventSpy = sinon.spy(selectionManager.selectionChange, "raiseEvent"); + selectionManager.addToSelection(source, blankImodel, baseSelection); + await waitFor(() => expect(raiseEventSpy, "Expected selectionChange.raiseEvent to be called").to.have.callCount(1)); + selectionManager.removeFromSelection(source, blankImodel, baseSelection); + await waitFor(() => expect(raiseEventSpy, "Expected selectionChange.raiseEvent to be called").to.have.callCount(2)); + selectionManager.replaceSelection(source, blankImodel, baseSelection); + await waitFor(() => expect(raiseEventSpy, "Expected selectionChange.raiseEvent to be called").to.have.callCount(3)); + selectionManager.clearSelection(source, blankImodel); + await waitFor(() => expect(raiseEventSpy, "Expected selectionChange.raiseEvent to be called").to.have.callCount(4)); + + // simulate connection closing + IModelConnection.onClose.raiseEvent(blankImodel); + }); }); describe("setSyncWithIModelToolSelection", () => {