diff --git a/common/constants.js b/common/constants.js index b3643c8150059735a508825aceb61cf0820f561a..c5f7c056767d940539781b8a96efeee7d321fb6f 100644 --- a/common/constants.js +++ b/common/constants.js @@ -1,6 +1,8 @@ (function(exports){ exports.ARIA_LABELS = { + // overlay specific + CONTEXT_MENU: `Viewer context menu`, // sharing module SHARE_BTN: `Share this view`, diff --git a/e2e/src/layout/viewerCtxMenu.prod.e2e-spec.js b/e2e/src/layout/viewerCtxMenu.prod.e2e-spec.js new file mode 100644 index 0000000000000000000000000000000000000000..f5f208a3fd4db4d1da08c280f9c02f4fff12f90f --- /dev/null +++ b/e2e/src/layout/viewerCtxMenu.prod.e2e-spec.js @@ -0,0 +1,53 @@ +const { AtlasPage } = require('../util') +const { ARIA_LABELS } = require('../../../common/constants') +const dict = { + "ICBM 2009c Nonlinear Asymmetric": { + "JuBrain Cytoarchitectonic Atlas": { + tests:[ + { + position: [550, 270], + expectedLabelName: 'Fastigial Nucleus (Cerebellum) - right hemisphere', + } + ] + } + } +} + +describe('> viewerCtxMenu', () => { + + for (const templateName in dict) { + for (const parcellationName in dict[templateName]) { + describe(`> on ${templateName} / ${parcellationName}`, () => { + beforeAll(async () => { + iavPage = new AtlasPage() + await iavPage.init() + await iavPage.goto() + await iavPage.selectTitleTemplateParcellation(templateName, parcellationName) + await iavPage.wait(500) + await iavPage.waitForAsync() + }) + + it('> does not appear on init', async () => { + const visible = await iavPage.isVisible(`[aria-label="${ARIA_LABELS.CONTEXT_MENU}"]`) + expect(visible).toBeFalse() + }) + + it('> appears on click', async () => { + const { tests } = dict[templateName][parcellationName] + const { position } = tests[0] + await iavPage.cursorMoveToAndClick({ position }) + await iavPage.wait(500) + const visible = await iavPage.isVisible(`[aria-label="${ARIA_LABELS.CONTEXT_MENU}"]`) + expect(visible).toBeTrue() + }) + + it('> disappear again on click of anywhere else', async () => { + await iavPage.cursorMoveToAndClick({ position: [10, 10] }) + await iavPage.wait(500) + const visible = await iavPage.isVisible(`[aria-label="${ARIA_LABELS.CONTEXT_MENU}"]`) + expect(visible).toBeFalse() + }) + }) + } + } +}) \ No newline at end of file diff --git a/e2e/src/util.js b/e2e/src/util.js index a5157471fc65a018a4441b862b3a18b0574e0b6b..457dfcdd6ab950ed001d732461ed34c6e4682827 100644 --- a/e2e/src/util.js +++ b/e2e/src/util.js @@ -113,6 +113,15 @@ class WdBase{ return text } + async isVisible(cssSelector) { + + if (!cssSelector) throw new Error(`getText needs to define css selector`) + const el = await this._browser.findElement( By.css(cssSelector) ) + const isDisplayed = await el.isDisplayed() + + return isDisplayed + } + historyBack() { return this._browser.navigate().back() } diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index e51d012d45ee51aa504e019ceaf42a7bfb9399bf..b349816e43b06113300323378a3985697ac76207 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -27,7 +27,6 @@ import { isDefined, safeFilter, } from "../services/stateStore.service"; -import { AtlasViewerAPIServices } from "./atlasViewer.apiService.service"; import { AtlasViewerConstantsServices, UNSUPPORTED_INTERVAL, UNSUPPORTED_PREVIEW } from "./atlasViewer.constantService.service"; import { WidgetServices } from "./widgetUnit/widgetService.service"; @@ -44,6 +43,7 @@ import { colorAnimation } from "./atlasViewer.animation" import { MouseHoverDirective } from "src/util/directives/mouseOver.directive"; import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar"; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; +import { ARIA_LABELS } from 'common/constants' export const NEHUBA_CLICK_OVERRIDE = 'NEHUBA_CLICK_OVERRIDE' @@ -67,11 +67,11 @@ const compareFn = (it, item) => it.name === item.name export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { + public CONTEXT_MENU_ARIA_LABEL = ARIA_LABELS.CONTEXT_MENU public compareFn = compareFn @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any> - private persistentStateNotifierMatDialogRef: MatDialogRef<any> @ViewChild('kgToS', {read: TemplateRef}) public kgTosComponent: TemplateRef<any> @ViewChild(LayoutMainSide) public layoutMainSide: LayoutMainSide @@ -382,11 +382,9 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { ) } - public mouseDownNehuba() { - this.rClContextualMenu.hide() - } + public mouseClickDocument(event) { - public mouseClickNehuba(event) { + const dismissRClCtxtMenu = this.rClContextualMenu.isShown const next = () => { @@ -396,7 +394,8 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { event.clientY, ] - this.rClContextualMenu.show() + if (dismissRClCtxtMenu) this.rClContextualMenu.hide() + else this.rClContextualMenu.show() } this.nehubaClickOverride(next) diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index bb25a9c67fab26f050a29f7533f3333ac5b23f00..4add13a9b03d29fdfb50d376224d0b2ba1421372 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -73,3 +73,8 @@ region-menu { display:inline-block; } + +.floating-container +{ + max-width: 350px; +} \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 21fc37f185e18d96341f3e4d12923a398daca6e2..0dc97ab18a8e00a1bc18f9796aaa494d4e1243cb 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -57,8 +57,8 @@ [currentOnHoverObs$]="iavMouseHoverEl.currentOnHoverObs$" [currentOnHover]="iavMouseHoverEl.currentOnHoverObs$ | async" iav-captureClickListenerDirective - (iav-captureClickListenerDirective-onMousedown)="mouseDownNehuba()" - (iav-captureClickListenerDirective-onClick)="mouseClickNehuba($event)"> + [iav-captureClickListenerDirective-captureDocument]="true" + (iav-captureClickListenerDirective-onClick)="mouseClickDocument($event)"> </ui-nehuba-container> <div class="z-index-10 position-absolute pe-none w-100 h-100"> @@ -165,7 +165,9 @@ <!-- TODO Potentially implementing plugin contextual info --> </div> - <div fixedMouseContextualContainerDirective + <div class="floating-container" + [attr.aria-label]="CONTEXT_MENU_ARIA_LABEL" + fixedMouseContextualContainerDirective #fixedContainer="iavFixedMouseCtxContainer"> <!-- on click segment menu --> diff --git a/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts b/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts index 756444017aa5bb9d1b178d21e24a13c54fd1c207..22ed4cd0eeca8e72b4a66b0c67ee9fb1e387363d 100644 --- a/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts +++ b/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts @@ -12,8 +12,6 @@ import { PluginHandler } from 'src/util/pluginHandler'; import { AtlasViewerConstantsServices } from "../atlasViewer.constantService.service"; import { WidgetUnit } from "../widgetUnit/widgetUnit.component"; -import './plugin_styles.css' - @Injectable({ providedIn : 'root', }) diff --git a/src/index.html b/src/index.html index 23558e3947d5e0d45617f3217ff3a4d1d0d07130..9b001d637cce6e1e344433bfeb5c0e3eb6a82e3a 100644 --- a/src/index.html +++ b/src/index.html @@ -8,7 +8,6 @@ <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"> <link rel="stylesheet" href="extra_styles.css"> - <link rel="stylesheet" href="plugin_styles.css"> <link rel="stylesheet" href="theme.css"> <link rel="stylesheet" href="version.css"> diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index e2f9b3b5685fb32f7d90f768427095b3f9aee649..fe7d4d4e320b24ee0149726a15a29781d31d646b 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -699,11 +699,6 @@ kg-dataset-previewer > img margin-right: -1rem!important; } -.mr-8 -{ - margin-right: 4rem!important; -} - .mat-card-sm > mat-dialog-container { padding-left: 16px!important; diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css index 278de03735e59ddbf0beabf6703665c6a0bac397..fd17d0f562db0fc6ada4ee123d1c3778822395de 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css @@ -8,4 +8,22 @@ mat-list.sm mat-list-item mat-icon { transform: scale(0.75); +} + +.action-list +{ + margin-left: -16px; + margin-right: -16px; +} + +.action-list mat-icon +{ + padding-left: 0px; +} + +.region-name +{ + display: inherit; + font-size: 95%; + line-height: normal; } \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html index 8c7ccf7e9fd3831f0aa88110677c02e746729a29..29daefc54deed588985927adf6e890805299d96b 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html @@ -1,9 +1,7 @@ <mat-card> <mat-card-title> - <div class="position-relative"> - <span class="mr-8"> - {{ region.name }} - </span> + <div class="position-relative region-name"> + {{ region.name }} </div> </mat-card-title> <mat-card-subtitle> @@ -28,7 +26,7 @@ [color]="isSelected ? 'primary' : 'default'" (click)="toggleRegionSelected()"> <i iav-v-button-icon class="far" [ngClass]="{'fa-check-square': isSelected, 'fa-square': !isSelected}"></i> - <span iav-v-button-text>{{isSelected ? 'Deselect' : 'Select'}}</span> + <span iav-v-button-text>Select</span> </iav-v-button> </mat-grid-tile> @@ -99,7 +97,7 @@ <mat-divider></mat-divider> - <mat-list class="sm"> + <mat-list class="action-list sm"> <!-- selected --> <mat-list-item @@ -109,7 +107,7 @@ (click)="toggleRegionSelected()"> <mat-icon scaled-down="" fontSet="far" [fontIcon]="isSelected ? 'fa-check-square' : 'fa-square'" mat-list-icon></mat-icon> <div mat-line> - {{ isSelected ? 'Selected' : 'Add to selected' }} + Select </div> </mat-list-item> diff --git a/src/ui/searchSideNav/searchSideNav.component.ts b/src/ui/searchSideNav/searchSideNav.component.ts index a0647a652a97b8e3c5315db58406c20064a5f3c3..fe9bcb9dcdb15201a97f9664fb9e94823a2084b9 100644 --- a/src/ui/searchSideNav/searchSideNav.component.ts +++ b/src/ui/searchSideNav/searchSideNav.component.ts @@ -25,8 +25,6 @@ import { MatSnackBar } from "@angular/material/snack-bar"; export class SearchSideNav implements OnDestroy { public availableDatasets: number = 0 - public showLayerBrowser: boolean = true - private subscriptions: Subscription[] = [] private layerBrowserDialogRef: MatDialogRef<any> diff --git a/src/ui/searchSideNav/searchSideNav.template.html b/src/ui/searchSideNav/searchSideNav.template.html index 765869826cba77a7218f5404a6656d6a4c3ae56f..b0bd20fff7f06b4c04dcb0659fb705f0f0a7f330 100644 --- a/src/ui/searchSideNav/searchSideNav.template.html +++ b/src/ui/searchSideNav/searchSideNav.template.html @@ -75,7 +75,10 @@ </div> <ng-template #layerBrowserTmpl> - <mat-dialog-content [hidden]="!showLayerBrowser"> + <mat-dialog-content + iav-switch + #showLayerBrowserSwitch="iavSwitch" + [hidden]="!showLayerBrowserSwitch.switchState"> <div class="d-flex flex-column"> <layer-browser></layer-browser> </div> @@ -84,12 +87,12 @@ <div class="d-flex justify-content-center position-static h-0 mt-4"> <button mat-mini-fab aria-label="Toggle expansion state of additional layer browser" - [matBadge]="showLayerBrowser ? null : (layerBrowser.nonBaseNgLayers$ | async).length" + [matBadge]="showLayerBrowserSwitch.switchState ? null : (layerBrowser.nonBaseNgLayers$ | async).length" class="position-absolute layerBrowserToggleBtn" matTooltip="Toggle the visiblity of the additional layer browser" - [attr.toggle-open]="showLayerBrowser" - (click)="showLayerBrowser = !showLayerBrowser"> - <i class="fas" [ngClass]="{'fa-chevron-down': !showLayerBrowser, 'fa-chevron-up': showLayerBrowser}"> + [attr.toggle-open]="showLayerBrowserSwitch.switchState" + (click)="showLayerBrowserSwitch.toggle()"> + <i class="fas" [ngClass]="{'fa-chevron-down': !showLayerBrowserSwitch.switchState, 'fa-chevron-up': showLayerBrowserSwitch.switchState}"> </i> </button> </div> diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0cf11aeb387d08a01a89f616e7095cc716fb63c5 --- /dev/null +++ b/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts @@ -0,0 +1,131 @@ +import { Component, ViewChild } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { FixedMouseContextualContainerDirective } from "./FixedMouseContextualContainerDirective.directive"; +import { By } from "@angular/platform-browser"; + +@Component({ + template: '' +}) + +class TestCmp{ + @ViewChild(FixedMouseContextualContainerDirective) directive: FixedMouseContextualContainerDirective +} + +describe('FixedMouseContextualContainerDirective', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + + ], + declarations: [ + TestCmp, + FixedMouseContextualContainerDirective + ] + }) + }) + + it('> can instantiate directive properly', () => { + TestBed.overrideComponent(TestCmp, { + set: { + template: ` + <div fixedMouseContextualContainerDirective> + </div> + ` + } + }).compileComponents() + + const fixture = TestBed.createComponent(TestCmp) + fixture.detectChanges() + const directive = fixture.debugElement.query( By.directive(FixedMouseContextualContainerDirective) ) + expect(directive).toBeTruthy() + + expect(fixture.componentInstance.directive).toBeTruthy() + }) + + describe('> hides if no content', () => { + it('> on #show, if content exists, isShown will be true', () => { + + TestBed.overrideComponent(TestCmp, { + set: { + template: ` + <div fixedMouseContextualContainerDirective> + <span>Hello World</span> + </div> + ` + } + }).compileComponents() + + const fixture = TestBed.createComponent(TestCmp) + fixture.detectChanges() + + const cmp = fixture.componentInstance + cmp.directive.show() + fixture.detectChanges() + expect(cmp.directive.isShown).toBeTrue() + }) + + it('> on #show, if only comment exists, isShown will be false', () => { + + TestBed.overrideComponent(TestCmp, { + set: { + template: ` + <div fixedMouseContextualContainerDirective> + <!-- hello world --> + </div> + ` + } + }).compileComponents() + + const fixture = TestBed.createComponent(TestCmp) + fixture.detectChanges() + + const cmp = fixture.componentInstance + cmp.directive.show() + fixture.detectChanges() + expect(cmp.directive.isShown).toBeFalse() + }) + + it('> on #show, if only text exists, isShown will be false', () => { + + TestBed.overrideComponent(TestCmp, { + set: { + template: ` + <div fixedMouseContextualContainerDirective> + hello world + </div> + ` + } + }).compileComponents() + + const fixture = TestBed.createComponent(TestCmp) + fixture.detectChanges() + + const cmp = fixture.componentInstance + cmp.directive.show() + fixture.detectChanges() + expect(cmp.directive.isShown).toBeFalse() + }) + + it('> on #show, if nothing exists, isShown will be false', () => { + + TestBed.overrideComponent(TestCmp, { + set: { + template: ` + <div fixedMouseContextualContainerDirective> + </div> + ` + } + }).compileComponents() + + const fixture = TestBed.createComponent(TestCmp) + fixture.detectChanges() + + const cmp = fixture.componentInstance + cmp.directive.show() + fixture.detectChanges() + expect(cmp.directive.isShown).toBeFalse() + }) + }) + + // TODO complete tests for FixedMouseContextualContainerDirective +}) \ No newline at end of file diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.ts index bf2293466e960dd98bb05020759f9ab0ddce9e8f..058a27918cdfd262ad2edfe6fa55325629043c1f 100644 --- a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts +++ b/src/util/directives/FixedMouseContextualContainerDirective.directive.ts @@ -43,6 +43,9 @@ export class FixedMouseContextualContainerDirective implements AfterContentCheck } ngAfterContentChecked(){ + if (this.el.nativeElement.childElementCount === 0) { + this.hide() + } this.recalculatePosition() this.cdr.markForCheck() } diff --git a/src/util/directives/switch.directive.ts b/src/util/directives/switch.directive.ts index cbdbe4dfc610d613eab509b9d4585e950a647cd7..adcb6a707b8c34109811b77ff23b233cc18f265b 100644 --- a/src/util/directives/switch.directive.ts +++ b/src/util/directives/switch.directive.ts @@ -5,7 +5,7 @@ import { Directive, Input } from "@angular/core"; exportAs: 'iavSwitch' }) export class SwitchDirective{ - @Input() switchState: boolean = false + @Input('iav-switch-initstate') switchState: boolean = false toggle(){ this.switchState = !this.switchState