diff --git a/deploy/datasets/util.js b/deploy/datasets/util.js index ae9a088df528fbd27db3d64b15e96976f1af68d8..e473e3299f69c0b4a12bc5617be5b4d0e4a849b6 100644 --- a/deploy/datasets/util.js +++ b/deploy/datasets/util.js @@ -11,7 +11,8 @@ const KG_IDS = { PARCELLATIONS: { LONG_BUNDLE: 'juelich/iav/atlas/v1.0.0/5', SHORT_BUNDLE: 'juelich/iav/atlas/v1.0.0/6', - JULICH_BRAIN: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579' + JULICH_BRAIN: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579', + JULICH_BRAIN_V24_BIGBRAIN: 'juelich/iav/atlas/v1.0.0/7' } } @@ -147,7 +148,7 @@ initPrArray.push( for (const p of json.parcellations) { processParc(json, p) } - const bigbrainCyto = flattenRegions(json.parcellations.find(({ name }) => name === 'Cytoarchitectonic Maps').regions) + const bigbrainCyto = flattenRegions(json.parcellations.find(({ ['@id']: id }) => id === KG_IDS.PARCELLATIONS.JULICH_BRAIN_V24_BIGBRAIN).regions) bigbrainCytoSet = populateSet(bigbrainCyto) }) .catch(console.error) diff --git a/e2e/util/selenium/layout.js b/e2e/util/selenium/layout.js index e437604ceab44cd68904925cb9461db890560c8d..99b7e97c667b9847a96b8475609778199e31f6d5 100644 --- a/e2e/util/selenium/layout.js +++ b/e2e/util/selenium/layout.js @@ -337,6 +337,10 @@ class WdLayoutPage extends WdBase{ throw new Error(`changeParc NYI`) } + async changeParcVersion(parcVerion) { + throw new Error(`changeParcVersion NYI`) + } + async selectAtlasTemplateParcellation(atlasName, templateName, parcellationName, parcVersion) { if (!atlasName) throw new Error(`atlasName needs to be provided`) try { @@ -365,6 +369,12 @@ class WdLayoutPage extends WdBase{ await this.changeParc(parcellationName) } + if (parcVersion) { + await this.wait(1000) + await this.waitUntilAllChunksLoaded() + await this.changeParcVersion(parcVersion) + } + try { await this._setAtlasSelectorExpanded(false) } catch (e) { diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index f815f53133b508c68a30eb7d19fa16ced9bb5a83..0d07ed8dae5ba71a1ccca9abd2f8ecbca4fb7c1f 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -306,6 +306,10 @@ markdown-dom pre code { width: 10em!important; } +.w-20em +{ + width: 20em!important; +} .mw-100 { @@ -399,6 +403,11 @@ markdown-dom pre code height:20em!important; } +.overflow-x-scroll +{ + overflow-x: scroll; +} + .overflow-x-hidden { overflow-x:hidden!important; diff --git a/src/res/ext/atlas/atlas_multiLevelHuman.json b/src/res/ext/atlas/atlas_multiLevelHuman.json index 33fb9a8135dfe6cd2c8ff8f4c4adf6b711b02c06..fdbb4afc693d56c5f4dfcdb902b4d72b6243630f 100644 --- a/src/res/ext/atlas/atlas_multiLevelHuman.json +++ b/src/res/ext/atlas/atlas_multiLevelHuman.json @@ -87,7 +87,7 @@ "baseLayer": true, "@version": { "@next": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", - "this": "juelich/iav/atlas/v1.0.0/8", + "@this": "juelich/iav/atlas/v1.0.0/8", "name": "v1.18", "@previous": null }, @@ -114,7 +114,7 @@ "baseLayer": true, "@version": { "@next": null, - "this": "juelich/iav/atlas/v1.0.0/7", + "@this": "juelich/iav/atlas/v1.0.0/7", "name": "current", "@previous": null }, @@ -131,7 +131,7 @@ "baseLayer": true, "@version": { "@next": null, - "this": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", + "@this": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "v2.4", "@previous": "juelich/iav/atlas/v1.0.0/8" }, diff --git a/src/services/state/viewerState.store.helper.spec.ts b/src/services/state/viewerState.store.helper.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c31955d5498e5a2b37f1e78fb26c84a3337b0ec3 --- /dev/null +++ b/src/services/state/viewerState.store.helper.spec.ts @@ -0,0 +1,100 @@ +import { isNewerThan } from "./viewerState.store.helper" + +describe('> viewerState.store.helper.ts', () => { + describe('> isNewerThan', () => { + describe('> ill formed versions', () => { + it('> in circular references, throws', () => { + + const parc0Circular = { + + "@version": { + "@next": "aaa-bbb", + "@this": "ccc-ddd", + "name": "", + "@previous": null, + } + } + const parc1Circular = { + + "@version": { + "@next": "ccc-ddd", + "@this": "aaa-bbb", + "name": "", + "@previous": null, + } + } + const p2 = { + ["@id"]: "foo-bar" + } + const p3 = { + ["@id"]: "baz" + } + expect(() => { + isNewerThan([parc0Circular, parc1Circular], p2, p3) + }).toThrow() + }) + + it('> if not found, will throw', () => { + + const parc0Circular = { + + "@version": { + "@next": "aaa-bbb", + "@this": "ccc-ddd", + "name": "", + "@previous": null, + } + } + const parc1Circular = { + + "@version": { + "@next": null, + "@this": "aaa-bbb", + "name": "", + "@previous": null, + } + } + const p2 = { + ["@id"]: "foo-bar" + } + const p3 = { + ["@id"]: "baz" + } + expect(() => { + isNewerThan([parc0Circular, parc1Circular], p2, p3) + }).toThrow() + }) + }) + + it('> works on well formed versions', () => { + + const parc0 = { + "@version": { + "@next": null, + "@this": "aaa-bbb", + "name": "", + "@previous": "ccc-ddd", + } + } + const parc1 = { + "@version": { + "@next": "aaa-bbb", + "@this": "ccc-ddd", + "name": "", + "@previous": null, + } + } + + const p0 = { + ['@id']: 'aaa-bbb' + } + const p1 = { + ['@id']: 'ccc-ddd' + } + expect( + isNewerThan([parc0, parc1], p0, p1) + ).toBeTrue() + }) + + }) +}) \ No newline at end of file diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index de45f8a073249ff6c2d2de0b7b9505fb02fb58bb..d69751026a665900376790f645a887beaf2fa6b0 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -147,4 +147,52 @@ export function viewerStateFleshOutDetail(reducer: ActionReducer<any>): ActionRe } } -export const defaultState = initialState \ No newline at end of file +export const defaultState = initialState + +interface IVersion{ + "@next": string + "@this": string + "name": string + "@previous": string +} + +interface IHasVersion{ + ['@version']: IVersion +} + +interface IHasId{ + ['@id']: string +} + +export function isNewerThan(arr: IHasVersion[], srcObj: IHasId, compObj: IHasId): boolean { + + function* GenNewerVersions(flag){ + let it = 0 + const newest = arr.find((v => v['@version'] && v['@version']['@this'] === srcObj['@id'])) + if (!newest) throw new Error(`GenNewerVersions error newest element isn't found`) + yield newest + let currPreviousId = newest['@version'][ flag ? '@next' : '@previous' ] + while (currPreviousId) { + it += 1 + if (it>100) throw new Error(`iteration excced 100, did you include a loop?`) + + const curr = arr.find(v => v['@version']['@this'] === currPreviousId) + if (!curr) throw new Error(`GenNewerVersions error, version id ${currPreviousId} not found`) + currPreviousId = curr['@version'][ flag ? '@next' : '@previous' ] + yield curr + } + } + for (const obj of GenNewerVersions(true)) { + if (obj['@version']['@this'] === compObj['@id']) { + return false + } + } + + for (const obj of GenNewerVersions(false)) { + if (obj['@version']['@this'] === compObj['@id']) { + return true + } + } + + throw new Error(`isNewerThan error, neither srcObj nor compObj exist in array`) +} diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts index 3148ed164d4a99b9170e1de1c92a9377502e852c..934f6e003573d0b0955a3ac613d7da3e1261fd15 100644 --- a/src/services/state/viewerState/selectors.ts +++ b/src/services/state/viewerState/selectors.ts @@ -1,5 +1,4 @@ import { createSelector } from "@ngrx/store" -import { create } from "domain" import { viewerStateHelperStoreName } from "../viewerState.store.helper" export const viewerStateSelectedRegionsSelector = createSelector( @@ -174,3 +173,17 @@ export const viewerStateParcVersionSelector = createSelector( return returnParc } ) + + +export const viewerStateSelectedTemplateFullInfoSelector = createSelector( + viewerStateGetSelectedAtlas, + viewerStateFetchedTemplatesSelector, + ({ templateSpaces }, fetchedTemplates) => templateSpaces.map(templateSpace => { + const fullTemplateInfo = fetchedTemplates.find(t => t['@id'] === templateSpace['@id']) + return { + ...templateSpace, + ...(fullTemplateInfo || {}), + darktheme: (fullTemplateInfo || {}).useTheme === 'dark' + } + }) +) diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts index fe54b4608c26d5acb2d88274c9625bd46721d802..0b2944c0f3d0d8b04207d6da4c17d2440a8ddb66 100644 --- a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts +++ b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts @@ -5,7 +5,7 @@ import { distinctUntilChanged, map, withLatestFrom, shareReplay, groupBy, mergeM import { Observable, Subscription, from, zip, of, combineLatest } from "rxjs"; import { viewerStateSelectTemplateWithId, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper"; import { MatMenuTrigger } from "@angular/material/menu"; -import { viewerStateGetSelectedAtlas, viewerStateAtlasLatestParcellationSelector } from "src/services/state/viewerState/selectors"; +import { viewerStateGetSelectedAtlas, viewerStateAtlasLatestParcellationSelector, viewerStateSelectedTemplateFullInfoSelector } from "src/services/state/viewerState/selectors"; import { ARIA_LABELS } from 'common/constants' @Component({ @@ -47,25 +47,8 @@ export class AtlasLayerSelector implements OnInit { shareReplay(1) ) - this.availableTemplates$ = combineLatest( - this.selectedAtlas$.pipe( - filter(v => !!v) - ), - this.store$.pipe( - select('viewerState'), - select('fetchedTemplates') - ) - ).pipe( - map(([ { templateSpaces }, fetchedTemplates ]) => { - return templateSpaces.map(templateSpace => { - const fullTemplateInfo = fetchedTemplates.find(t => t['@id'] === templateSpace['@id']) - return { - ...templateSpace, - ...(fullTemplateInfo || {}), - darktheme: (fullTemplateInfo || {}).useTheme === 'dark' - } - }) - }), + this.availableTemplates$ = this.store$.pipe( + select(viewerStateSelectedTemplateFullInfoSelector) ) this.selectedTemplate$ = this.store$.pipe( diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts index 49c2e9316963e3e19932f0da6e7af6c868ac91ca..cb3f929af8b4724dbbf97d1e649932a06a77bc15 100644 --- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts +++ b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts @@ -1,4 +1,4 @@ -import { Directive, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef, OnInit, OnDestroy, Output, EventEmitter } from "@angular/core"; +import { Directive, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef, OnInit, OnDestroy, Output, EventEmitter, Optional } from "@angular/core"; import { NehubaViewerUnit, INehubaLifecycleHook } from "../nehubaViewer/nehubaViewer.component"; import { Store, select } from "@ngrx/store"; import { IavRootStoreInterface } from "src/services/stateStore.service"; @@ -13,6 +13,7 @@ import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/acti import { viewerStateMouseOverCustomLandmarkInPerspectiveView } from "src/services/state/viewerState/actions"; import { viewerStateStandAloneVolumes, viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors"; import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors"; +import { LoggingService } from "src/logging"; const defaultNehubaConfig = { "configName": "", @@ -272,6 +273,7 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{ private el: ViewContainerRef, private cfr: ComponentFactoryResolver, private store$: Store<IavRootStoreInterface>, + @Optional() private log: LoggingService, ){ this.nehubaViewerFactory = this.cfr.resolveComponentFactory(NehubaViewerUnit) @@ -313,7 +315,9 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{ ).subscribe(flag =>{ const showPerspectiveSliceViews = this.nehubaViewerInstance?.nehubaViewer?.ngviewer?.showPerspectiveSliceViews if (showPerspectiveSliceViews) showPerspectiveSliceViews.restoreState(flag) - else console.warn(`showPerspectiveSliceViews not defined`) + else { + this.log && this.log.warn(`showPerspectiveSliceViews not defined`) + } }) ) diff --git a/src/ui/parcellationRegion/region.base.spec.ts b/src/ui/parcellationRegion/region.base.spec.ts index a89baf7b08a98e0260ab68387055e84579c94188..e70c0f3b99b57ce573f7e3cd96b0a1dcf9e474b8 100644 --- a/src/ui/parcellationRegion/region.base.spec.ts +++ b/src/ui/parcellationRegion/region.base.spec.ts @@ -166,6 +166,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) => mp1h, mr0lh, mt3, + mr0, mr0rh } @@ -316,6 +317,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) => mp1h, mr0lh, mt3, + mr0, mr0rh } } @@ -329,13 +331,13 @@ describe('> region.base.ts', () => { for (const enumKey of Object.keys(EnumParcRegVersion)) { describe(`> selector version for ${enumKey}`, () => { - const { mockFetchedTemplates, mt2, mt0, mp0, mt1, mp1h, mr0lh, mt3, mr0rh } = getRegionInOtherTemplateSelectorBundle(enumKey as EnumParcRegVersion) + const { mockFetchedTemplates, mr0, mt2, mt0, mp0, mt1, mp1h, mr0lh, mt3, mr0rh } = getRegionInOtherTemplateSelectorBundle(enumKey as EnumParcRegVersion) describe('> no hemisphere selected, simulates big brain cyto map', () => { let result: any[] beforeAll(() => { - result = regionInOtherTemplateSelector.projector({ fetchedTemplates: mockFetchedTemplates, templateSelected: mt0 }, { region: mr0 }) + result = regionInOtherTemplateSelector.projector(mockFetchedTemplates, mt0, { region: mr0 }) }) it('> length checks out', () => { @@ -407,7 +409,7 @@ describe('> region.base.ts', () => { describe('> hemisphere data selected (left hemisphere), simulates julich-brain in mni152', () => { let result beforeAll(() => { - result = regionInOtherTemplateSelector.projector({ fetchedTemplates: mockFetchedTemplates, templateSelected: mt2 }, { region: mr0lh }) + result = regionInOtherTemplateSelector.projector(mockFetchedTemplates, mt2, { region: mr0lh }) }) it('> length checks out', () => { diff --git a/src/ui/parcellationRegion/region.base.ts b/src/ui/parcellationRegion/region.base.ts index 3beb2c7f7ab8780b229cbc04866451dacda9db80..3f9c62d1e86418065171fd681afe460e59c6de78 100644 --- a/src/ui/parcellationRegion/region.base.ts +++ b/src/ui/parcellationRegion/region.base.ts @@ -1,12 +1,12 @@ import { EventEmitter, Input, Output, Pipe, PipeTransform } from "@angular/core"; import { select, Store, createSelector } from "@ngrx/store"; import { uiStateOpenSidePanel, uiStateExpandSidePanel, uiActionShowSidePanelConnectivity } from 'src/services/state/uiState.store.helper' -import { distinctUntilChanged, switchMap, filter, map, tap } from "rxjs/operators"; +import { distinctUntilChanged, switchMap, filter, map, withLatestFrom } from "rxjs/operators"; import { Observable, BehaviorSubject, combineLatest } from "rxjs"; import { ARIA_LABELS } from 'common/constants' import { flattenRegions, getIdFromFullId, rgbToHsl } from 'common/util' -import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect, viewerStateNewViewer } from "src/services/state/viewerState.store.helper"; -import { viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors"; +import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect, viewerStateNewViewer, isNewerThan } from "src/services/state/viewerState.store.helper"; +import { viewerStateFetchedTemplatesSelector, viewerStateGetSelectedAtlas, viewerStateSelectedTemplateFullInfoSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors"; import { intToRgb, verifyPositionArg } from 'common/util' export class RegionBase { @@ -58,11 +58,20 @@ export class RegionBase { public sameRegionTemplate: any[] = [] public regionInOtherTemplates$: Observable<any[]> public regionOriginDatasetLabels$: Observable<{ name: string }[]> + public selectedAtlas$: Observable<any> = this.store$.pipe( + select(viewerStateGetSelectedAtlas) + ) + + public selectedTemplateFullInfo$: Observable<any[]> constructor( private store$: Store<any>, ) { + this.selectedTemplateFullInfo$ = this.store$.pipe( + select(viewerStateSelectedTemplateFullInfoSelector), + ) + this.regionInOtherTemplates$ = this.region$.pipe( distinctUntilChanged(), filter(v => !!v), @@ -70,7 +79,36 @@ export class RegionBase { select( regionInOtherTemplateSelector, { region } - ) + ), + withLatestFrom( + this.store$.pipe( + select(viewerStateGetSelectedAtlas) + ) + ), + map(([ regionsInOtherTemplates, selectedatlas ]) => { + const { parcellations } = selectedatlas + const filteredRsInOtherTmpls = [] + for (const bundledObj of regionsInOtherTemplates) { + const { template, parcellation, region } = bundledObj + const idx = filteredRsInOtherTmpls.findIndex(({ template: _template, region: _region }) => { + return _template['@id'] === template['@id'] + && getRegionHemisphere(_region) !== getRegionHemisphere(region) + }) + if ( idx < 0 ) { + filteredRsInOtherTmpls.push(bundledObj) + } else { + const { parcellation: currentParc } = filteredRsInOtherTmpls[idx] + /** + * if the new element is newer than existing item + */ + if (isNewerThan(parcellations, parcellation, currentParc)) { + filteredRsInOtherTmpls.splice(idx, 1) + filteredRsInOtherTmpls.push(bundledObj) + } + } + } + return filteredRsInOtherTmpls + }) )) ) @@ -193,6 +231,11 @@ export const getRegionParentParcRefSpace = createSelector( } ) +enum EnumHemisphere{ + LEFT_HEMISPHERE = 'left hemisphere', + RIGHT_HEMISPHERE = 'right hemisphere', +} + @Pipe({ name: 'renderViewOriginDatasetlabel' }) @@ -207,20 +250,16 @@ export class RenderViewOriginDatasetLabelPipe implements PipeTransform{ } export const regionInOtherTemplateSelector = createSelector( - (state: any) => state.viewerState, - (viewerState, prop) => { + viewerStateFetchedTemplatesSelector, + viewerStateSelectedTemplateSelector, + (fetchedTemplates, templateSelected, prop) => { const { region: regionOfInterest } = prop const returnArr = [] // const regionOfInterestHemisphere = regionOfInterest.status - const regionOfInterestHemisphere = (regionOfInterest.name.includes('- right hemisphere') || (!!regionOfInterest.status && regionOfInterest.status.includes('right hemisphere'))) - ? 'right hemisphere' - : (regionOfInterest.name.includes('- left hemisphere') || (!!regionOfInterest.status && regionOfInterest.status.includes('left hemisphere'))) - ? 'left hemisphere' - : null + const regionOfInterestHemisphere = getRegionHemisphere(regionOfInterest) const regionOfInterestId = getIdFromFullId(regionOfInterest.fullId) - const { fetchedTemplates, templateSelected } = viewerState if (!templateSelected) return [] const selectedTemplateId = getIdFromFullId(templateSelected.fullId) const otherTemplates = fetchedTemplates.filter(({ fullId }) => getIdFromFullId(fullId) !== selectedTemplateId) @@ -231,35 +270,29 @@ export const regionInOtherTemplateSelector = createSelector( for (const region of selectableRegions) { const id = getIdFromFullId(region.fullId) - if (!!id) { - const regionHemisphere = (region.name.includes('- right hemisphere') || (!!region.status && region.status.includes('right hemisphere'))) - ? 'right hemisphere' - : (region.name.includes('- left hemisphere') || (!!region.status && region.status.includes('left hemisphere'))) - ? 'left hemisphere' - : null - if (id === regionOfInterestId) { - /** - * if both hemisphere metadatas are defined - */ - if ( - !!regionOfInterestHemisphere && - !!regionHemisphere - ) { - if (regionHemisphere === regionOfInterestHemisphere) { - returnArr.push({ - template, - parcellation, - region, - }) - } - } else { + if (!!id && id === regionOfInterestId) { + const regionHemisphere = getRegionHemisphere(region) + /** + * if both hemisphere metadatas are defined + */ + if ( + !!regionOfInterestHemisphere && + !!regionHemisphere + ) { + if (regionHemisphere === regionOfInterestHemisphere) { returnArr.push({ template, parcellation, region, - hemisphere: regionHemisphere }) } + } else { + returnArr.push({ + template, + parcellation, + region, + hemisphere: regionHemisphere + }) } } } @@ -267,4 +300,12 @@ export const regionInOtherTemplateSelector = createSelector( } return returnArr } -) \ No newline at end of file +) + +export function getRegionHemisphere(region: any): EnumHemisphere{ + return (region.name.includes('- right hemisphere') || (!!region.status && region.status.includes('right hemisphere'))) + ? EnumHemisphere.RIGHT_HEMISPHERE + : (region.name.includes('- left hemisphere') || (!!region.status && region.status.includes('left hemisphere'))) + ? EnumHemisphere.LEFT_HEMISPHERE + : null +} diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts b/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts index 3cf92bedded94d7222bbdf48813fb34f4d14d9ef..fceeb0c7046afe38195967825e12cbd82097dde2 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts @@ -9,6 +9,7 @@ import { ARIA_LABELS } from 'common/constants' import { By } from "@angular/platform-browser" import { Directive, Input } from "@angular/core" import { NoopAnimationsModule } from "@angular/platform-browser/animations" +import { viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors" const mt0 = { name: 'mt0' @@ -113,6 +114,13 @@ describe('> regionMenu.component.ts', () => { }) describe('> regionInOtherTemplatesTmpl', () => { + beforeEach(() => { + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector( + viewerStateGetSelectedAtlas, + { parcellations: [] } + ) + }) it('> if selector returns empty array, data-available-in-tmpl-count == 0', () => {