fix(segmentation): restrict overlay segmentation menu to same frame of reference as viewport background display set (#5900)
- Add FrameOfReferenceUID to SEG and RTSTRUCT displaySet in SOP Class Handlers so the FOR is available for filtering - Sync optimisticOverlayDisplaySets when background display set changes so the overlay menu reflects the correct state after a background switch - Add FOR matching guard to the hydrate segmentation synchronizer to prevent the hydration synchronizer from blindly mirroring segmentations from a source viewport to a target viewport if their primary Frames of Reference do not align. - fix segmentation overlay order reversal on viewport re-render
This commit is contained in:
parent
a875d7de1f
commit
b9029ef6f8
@ -56,6 +56,7 @@ function _getDisplaySetsFromSeries(
|
||||
StudyInstanceUID,
|
||||
SOPClassHandlerId,
|
||||
SOPClassUID,
|
||||
frameOfReferenceUID: null,
|
||||
referencedImages: null,
|
||||
referencedSeriesInstanceUID: null,
|
||||
referencedDisplaySetInstanceUID: null,
|
||||
@ -95,6 +96,9 @@ function _getDisplaySetsFromSeries(
|
||||
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
|
||||
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
||||
|
||||
displaySet.frameOfReferenceUID =
|
||||
instance.ReferencedFrameOfReferenceSequence?.[0]?.FrameOfReferenceUID;
|
||||
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const referencedDisplaySets =
|
||||
displaySetService.getDisplaySetsForReferences(referencedSeriesSequence);
|
||||
@ -113,6 +117,7 @@ function _getDisplaySetsFromSeries(
|
||||
if (addedDisplaySet.SeriesInstanceUID === displaySet.referencedSeriesInstanceUID) {
|
||||
displaySet.referencedDisplaySetInstanceUID = addedDisplaySet.displaySetInstanceUID;
|
||||
displaySet.isReconstructable = addedDisplaySet.isReconstructable;
|
||||
displaySet.frameOfReferenceUID = addedDisplaySet.frameOfReferenceUID;
|
||||
unsubscribe();
|
||||
}
|
||||
}
|
||||
@ -121,6 +126,7 @@ function _getDisplaySetsFromSeries(
|
||||
const [referencedDisplaySet] = referencedDisplaySets;
|
||||
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
|
||||
displaySet.isReconstructable = referencedDisplaySet.isReconstructable;
|
||||
displaySet.frameOfReferenceUID = referencedDisplaySet.frameOfReferenceUID;
|
||||
}
|
||||
|
||||
displaySet.load = ({ headers, createSegmentation = true }) =>
|
||||
|
||||
@ -30,6 +30,7 @@ function _getDisplaySetsFromSeries(
|
||||
SeriesDate,
|
||||
StructureSetDate,
|
||||
SOPClassUID,
|
||||
FrameOfReferenceUID,
|
||||
wadoRoot,
|
||||
wadoUri,
|
||||
wadoUriRoot,
|
||||
@ -49,6 +50,7 @@ function _getDisplaySetsFromSeries(
|
||||
StudyInstanceUID,
|
||||
SOPClassHandlerId,
|
||||
SOPClassUID,
|
||||
frameOfReferenceUID: FrameOfReferenceUID,
|
||||
referencedImages: null,
|
||||
referencedSeriesInstanceUID: null,
|
||||
referencedDisplaySetInstanceUID: null,
|
||||
@ -104,6 +106,7 @@ function _getDisplaySetsFromSeries(
|
||||
if (addedDisplaySet.SeriesInstanceUID === displaySet.referencedSeriesInstanceUID) {
|
||||
displaySet.referencedDisplaySetInstanceUID = addedDisplaySet.displaySetInstanceUID;
|
||||
displaySet.isReconstructable = addedDisplaySet.isReconstructable;
|
||||
displaySet.frameOfReferenceUID = addedDisplaySet.frameOfReferenceUID;
|
||||
unsubscribe();
|
||||
}
|
||||
}
|
||||
@ -111,6 +114,7 @@ function _getDisplaySetsFromSeries(
|
||||
} else {
|
||||
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
|
||||
displaySet.isReconstructable = referencedDisplaySet.isReconstructable;
|
||||
displaySet.frameOfReferenceUID = referencedDisplaySet.frameOfReferenceUID;
|
||||
}
|
||||
|
||||
displaySet.load = async ({ headers }) =>
|
||||
|
||||
@ -230,7 +230,7 @@ async function _load(
|
||||
|
||||
function _measurementBelongsToDisplaySet({ measurement, displaySet }) {
|
||||
return (
|
||||
measurement.coords[0].ReferencedFrameOfReferenceSequence === displaySet.FrameOfReferenceUID
|
||||
measurement.coords[0].ReferencedFrameOfReferenceSequence === displaySet.frameOfReferenceUID
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ export default function hydrateStructuredReport(
|
||||
|
||||
for (const FrameOfReferenceUID of frameOfReferenceUIDs) {
|
||||
const displaySetsFOR = displaySetService.getDisplaySetsBy(
|
||||
ds => ds.FrameOfReferenceUID === FrameOfReferenceUID && !ds.isDerivedDisplaySet
|
||||
ds => ds.frameOfReferenceUID === FrameOfReferenceUID && !ds.isDerivedDisplaySet
|
||||
);
|
||||
const ds = chooseDisplaySet(displaySetsFOR, FrameOfReferenceUID);
|
||||
if (!ds) {
|
||||
@ -335,7 +335,7 @@ function getReferenceData3D(toolData, servicesManager: Types.ServicesManager) {
|
||||
const { points } = toolData.annotation.data.handles;
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const displaySetsFOR = displaySetService.getDisplaySetsBy(
|
||||
ds => ds.FrameOfReferenceUID === FrameOfReferenceUID
|
||||
ds => ds.frameOfReferenceUID === FrameOfReferenceUID
|
||||
);
|
||||
if (!displaySetsFOR.length || !points?.length) {
|
||||
return {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Icons,
|
||||
@ -43,6 +43,10 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
|
||||
|
||||
const [thresholdOpacityEnabled, setThresholdOpacityEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setOptimisticOverlayDisplaySets(overlayDisplaySets);
|
||||
}, [backgroundDisplaySet?.displaySetInstanceUID, overlayDisplaySets]);
|
||||
|
||||
/**
|
||||
* Change the background display set
|
||||
*/
|
||||
@ -482,7 +486,10 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectTrigger
|
||||
className="flex-1"
|
||||
data-cy={`overlay-background-ds-select-${viewportId}`}
|
||||
>
|
||||
<SelectValue>
|
||||
{(
|
||||
backgroundDisplaySet?.SeriesDescription ||
|
||||
|
||||
@ -80,8 +80,8 @@ export function getEnhancedDisplaySets({ viewportId, services }) {
|
||||
|
||||
// Check if Frame of Reference matches
|
||||
if (
|
||||
displaySet.FrameOfReferenceUID &&
|
||||
displaySet.FrameOfReferenceUID !== backgroundDisplaySet.FrameOfReferenceUID
|
||||
displaySet.frameOfReferenceUID &&
|
||||
displaySet.frameOfReferenceUID !== backgroundDisplaySet.frameOfReferenceUID
|
||||
) {
|
||||
return {
|
||||
...displaySet,
|
||||
|
||||
@ -82,7 +82,7 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
FrameOfReferenceUID,
|
||||
frameOfReferenceUID: FrameOfReferenceUID,
|
||||
SOPClassHandlerId,
|
||||
SOPClassUID,
|
||||
SeriesDescription: SeriesDescription || 'Microscopy Data',
|
||||
|
||||
@ -66,7 +66,15 @@ const segmentationRepresentationModifiedCallback = async (
|
||||
|
||||
const sharedDisplaySetExists = isAnyDisplaySetCommon(sourceDisplaySetUIDs, targetDisplaySetUIDs);
|
||||
|
||||
if (!sharedDisplaySetExists && !viewport.getFrameOfReferenceUID()) {
|
||||
const targetFrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
||||
const sourceFrameOfReferenceUID =
|
||||
getEnabledElementByViewportId(sourceViewportId)?.viewport?.getFrameOfReferenceUID();
|
||||
|
||||
if (!sharedDisplaySetExists && !targetFrameOfReferenceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sharedDisplaySetExists && targetFrameOfReferenceUID !== sourceFrameOfReferenceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -870,28 +870,21 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
initialImageIndexToUse = this._getInitialImageIndexForViewport(viewportInfo, imageIds) || 0;
|
||||
}
|
||||
|
||||
return viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
|
||||
viewport.setProperties({ ...properties });
|
||||
this.setPresentations(viewport.id, presentations, viewportInfo);
|
||||
await viewport.setStack(imageIds, initialImageIndexToUse);
|
||||
viewport.setProperties({ ...properties });
|
||||
this.setPresentations(viewport.id, presentations, viewportInfo);
|
||||
|
||||
if (overlayProcessingResults?.length) {
|
||||
overlayProcessingResults.forEach(overlayProcessingResult => {
|
||||
if (overlayProcessingResult?.addOverlayFn) {
|
||||
overlayProcessingResult.addOverlayFn();
|
||||
}
|
||||
});
|
||||
}
|
||||
await this._addOverlayRepresentations(overlayProcessingResults);
|
||||
|
||||
if (displayArea) {
|
||||
viewport.setDisplayArea(displayArea);
|
||||
}
|
||||
if (rotation) {
|
||||
viewport.setProperties({ rotation });
|
||||
}
|
||||
if (flipHorizontal) {
|
||||
viewport.setCamera({ flipHorizontal: true });
|
||||
}
|
||||
});
|
||||
if (displayArea) {
|
||||
viewport.setDisplayArea(displayArea);
|
||||
}
|
||||
if (rotation) {
|
||||
viewport.setProperties({ rotation });
|
||||
}
|
||||
if (flipHorizontal) {
|
||||
viewport.setCamera({ flipHorizontal: true });
|
||||
}
|
||||
}
|
||||
|
||||
private _getInitialImageIndexForViewport(
|
||||
@ -1120,14 +1113,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
}
|
||||
|
||||
await viewport.setVolumes(volumeInputArray);
|
||||
|
||||
if (overlayProcessingResults?.length) {
|
||||
overlayProcessingResults.forEach(({ addOverlayFn }) => {
|
||||
if (addOverlayFn) {
|
||||
addOverlayFn();
|
||||
}
|
||||
});
|
||||
}
|
||||
await this._addOverlayRepresentations(overlayProcessingResults);
|
||||
viewport.render();
|
||||
|
||||
volumesProperties.forEach(({ properties, volumeId }) => {
|
||||
@ -1193,7 +1179,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
private addOverlayRepresentationForDisplaySet(
|
||||
displaySet: OhifTypes.DisplaySet,
|
||||
viewport: Types.IViewport
|
||||
) {
|
||||
): Promise<void> {
|
||||
const { segmentationService } = this.servicesManager.services;
|
||||
const segmentationId = displaySet.displaySetInstanceUID;
|
||||
|
||||
@ -1203,18 +1189,36 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
: csToolsEnums.SegmentationRepresentations.Contour;
|
||||
|
||||
const { predecessorImageId } = displaySet;
|
||||
segmentationService.addSegmentationRepresentation(viewport.id, {
|
||||
segmentationId,
|
||||
predecessorImageId,
|
||||
type: representationType,
|
||||
config: {
|
||||
blendMode:
|
||||
viewport?.getBlendMode?.() === 1 ? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const segmentationRepresentationPromise = segmentationService.addSegmentationRepresentation(
|
||||
viewport.id,
|
||||
{
|
||||
segmentationId,
|
||||
predecessorImageId,
|
||||
type: representationType,
|
||||
config: {
|
||||
blendMode:
|
||||
viewport?.getBlendMode?.() === 1
|
||||
? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
);
|
||||
// store the segmentation presentation id in the viewport info
|
||||
this.storePresentation({ viewportId: viewport.id });
|
||||
return segmentationRepresentationPromise;
|
||||
}
|
||||
|
||||
private async _addOverlayRepresentations(
|
||||
overlayProcessingResults?: Array<{ addOverlayFn?: () => Promise<void> }>
|
||||
): Promise<void> {
|
||||
if (!overlayProcessingResults?.length) {
|
||||
return;
|
||||
}
|
||||
for (const overlayProcessingResult of overlayProcessingResults) {
|
||||
if (overlayProcessingResult?.addOverlayFn) {
|
||||
await overlayProcessingResult.addOverlayFn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: keepCamera is an interim solution until we have a better solution for
|
||||
@ -1272,7 +1276,10 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
}
|
||||
|
||||
if (viewport instanceof ECGViewport) {
|
||||
return this._setEcgViewport(viewport as unknown as Types.IECGViewport, viewportData as StackViewportData);
|
||||
return this._setEcgViewport(
|
||||
viewport as unknown as Types.IECGViewport,
|
||||
viewportData as StackViewportData
|
||||
);
|
||||
}
|
||||
|
||||
return this._setOtherViewport(
|
||||
|
||||
@ -113,7 +113,7 @@ const makeDisplaySet = (instances, index) => {
|
||||
label:
|
||||
instance.SeriesDescription ||
|
||||
`${i18n.t('Series')} ${instance.SeriesNumber} - ${i18n.t(instance.Modality)}`,
|
||||
FrameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
});
|
||||
|
||||
const imageIds = dataSource.getImageIdsForDisplaySet(imageSet);
|
||||
|
||||
@ -329,16 +329,16 @@ export default class MicroscopyService extends PubSubService {
|
||||
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
const smDisplaySet = displaySets.find(ds => ds.Modality === 'SM');
|
||||
|
||||
const { FrameOfReferenceUID, othersFrameOfReferenceUID } = smDisplaySet;
|
||||
const { frameOfReferenceUID, othersFrameOfReferenceUID } = smDisplaySet;
|
||||
|
||||
if (!studyMetadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
let derivedDisplaySets = FrameOfReferenceUID
|
||||
let derivedDisplaySets = frameOfReferenceUID
|
||||
? displaySets.filter(
|
||||
ds =>
|
||||
ds.ReferencedFrameOfReferenceUID === FrameOfReferenceUID ||
|
||||
ds.ReferencedFrameOfReferenceUID === frameOfReferenceUID ||
|
||||
// sometimes each depth instance has the different FrameOfReferenceID
|
||||
othersFrameOfReferenceUID.includes(ds.ReferencedFrameOfReferenceUID)
|
||||
)
|
||||
|
||||
@ -22,7 +22,7 @@ export default function getSourceDisplaySet(allDisplaySets, microscopySRDisplayS
|
||||
const referencedDisplaySet = otherDisplaySets.find(
|
||||
displaySet =>
|
||||
displaySet.Modality === 'SM' &&
|
||||
(displaySet.FrameOfReferenceUID === ReferencedFrameOfReferenceUID ||
|
||||
(displaySet.frameOfReferenceUID === ReferencedFrameOfReferenceUID ||
|
||||
// sometimes each depth instance has the different FrameOfReferenceID
|
||||
displaySet.othersFrameOfReferenceUID.includes(ReferencedFrameOfReferenceUID))
|
||||
);
|
||||
|
||||
@ -57,6 +57,12 @@ export type DisplaySet = {
|
||||
*/
|
||||
referencedDisplaySetInstanceUID?: string;
|
||||
|
||||
/**
|
||||
* The FrameOfReferenceUID shared by every frame within this display set.
|
||||
* It will be undefined if the frames do not all share the same Frame of Reference.
|
||||
*/
|
||||
frameOfReferenceUID?: string;
|
||||
|
||||
SeriesDate?: string;
|
||||
SeriesTime?: string;
|
||||
instance?: InstanceMetadata;
|
||||
|
||||
98
tests/DataOverlayMenuFrameOfReference.spec.ts
Normal file
98
tests/DataOverlayMenuFrameOfReference.spec.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { test, visitStudy, expect } from './utils';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const studyInstanceUID = '1.3.6.1.4.1.12201.1091.126683095609223531686845324113579088978';
|
||||
const mode = 'segmentation';
|
||||
await visitStudy(page, studyInstanceUID, mode, 2000);
|
||||
});
|
||||
|
||||
test('should only show segmentations matching the background Frame of Reference', async ({
|
||||
page,
|
||||
viewportPageObject,
|
||||
}) => {
|
||||
const segmentationLabelsFOR1 = ['Segmentation FOR1SEG', 'FoR 1 RTstructRTSTRUCT'];
|
||||
const segmentationLabelsFOR2 = ['Segmentation FOR2SEG', 'FoR 2 RTstructRTSTRUCT'];
|
||||
|
||||
const dataOverlay = (await viewportPageObject.getNth(0)).overlayMenu.dataOverlay;
|
||||
|
||||
await dataOverlay.toggle();
|
||||
await dataOverlay.openAddSegmentationDropdown();
|
||||
|
||||
const segmentationLabels = await dataOverlay.getDropdownOptionLabels();
|
||||
expect(segmentationLabels.sort()).toEqual([...segmentationLabelsFOR1].sort());
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
await dataOverlay.changeBackgroundDisplaySet('CT Std (FoR 2)-CT');
|
||||
await dataOverlay.clickSelectSegmentation();
|
||||
const segmentationLabelsAfterBgChange = await dataOverlay.getDropdownOptionLabels();
|
||||
expect(segmentationLabelsAfterBgChange).not.toEqual(segmentationLabelsFOR1);
|
||||
expect(segmentationLabelsAfterBgChange.sort()).toEqual([...segmentationLabelsFOR2].sort());
|
||||
});
|
||||
|
||||
test('should only sync segmentations to viewports with same Frame of Reference', async ({
|
||||
viewportPageObject,
|
||||
}) => {
|
||||
const segmentationFOR1Label = 'Segmentation FOR1';
|
||||
|
||||
const viewportA = await viewportPageObject.getNth(0);
|
||||
const viewportB = await viewportPageObject.getNth(1);
|
||||
const viewportC = await viewportPageObject.getNth(2);
|
||||
|
||||
const dataOverlayA = viewportA.overlayMenu.dataOverlay;
|
||||
const dataOverlayB = viewportB.overlayMenu.dataOverlay;
|
||||
const dataOverlayC = viewportC.overlayMenu.dataOverlay;
|
||||
|
||||
// In FOR1 viewport A: add a segmentation FOR1 overlay
|
||||
await viewportA.pane.hover();
|
||||
await dataOverlayA.toggle();
|
||||
await dataOverlayA.addSegmentation(segmentationFOR1Label);
|
||||
await expect(dataOverlayA.getOverlaySegmentationRow(segmentationFOR1Label)).toBeVisible();
|
||||
await dataOverlayA.toggle();
|
||||
await expect(dataOverlayA.menu).not.toBeVisible();
|
||||
|
||||
// In another FOR1 viewport B: segmentation FOR1 should be visible (synced)
|
||||
await viewportB.pane.hover();
|
||||
await dataOverlayB.toggle();
|
||||
const activeSegmentationViewportB = dataOverlayB.getOverlaySegmentationRow(segmentationFOR1Label);
|
||||
await expect(activeSegmentationViewportB).toBeVisible();
|
||||
await expect(activeSegmentationViewportB).toHaveText(segmentationFOR1Label.toUpperCase());
|
||||
await dataOverlayB.toggle();
|
||||
await expect(dataOverlayB.menu).not.toBeVisible();
|
||||
|
||||
// In FOR2 viewport C: segmentation FOR1 should NOT be visible
|
||||
await viewportC.pane.hover();
|
||||
await dataOverlayC.toggle();
|
||||
await expect(dataOverlayC.getOverlaySegmentationRow(segmentationFOR1Label)).not.toBeVisible();
|
||||
await dataOverlayC.toggle();
|
||||
await expect(dataOverlayC.menu).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('should preserve segmentation order after menu close and reopen', async ({
|
||||
viewportPageObject,
|
||||
}) => {
|
||||
const segmentation1Label = 'Segmentation FOR1';
|
||||
const segmentation2Label = 'FoR 1 RTstruct';
|
||||
|
||||
const dataOverlay = (await viewportPageObject.getNth(0)).overlayMenu.dataOverlay;
|
||||
|
||||
await dataOverlay.toggle();
|
||||
await dataOverlay.addSegmentation(segmentation1Label);
|
||||
await expect(dataOverlay.getOverlaySegmentationRow(segmentation1Label)).toBeVisible();
|
||||
|
||||
await dataOverlay.addSegmentation(segmentation2Label);
|
||||
await expect(dataOverlay.getOverlaySegmentationRow(segmentation2Label)).toBeVisible();
|
||||
|
||||
await dataOverlay.toggle();
|
||||
await expect(dataOverlay.menu).not.toBeVisible();
|
||||
|
||||
// Reopen menu and verify segmentation order is preserved
|
||||
await dataOverlay.toggle();
|
||||
await expect(dataOverlay.overlaySegmentationRows).toHaveCount(2);
|
||||
await expect(dataOverlay.overlaySegmentationRows.nth(0)).toHaveText(
|
||||
segmentation1Label.toUpperCase()
|
||||
);
|
||||
await expect(dataOverlay.overlaySegmentationRows.nth(1)).toHaveText(
|
||||
segmentation2Label.toUpperCase()
|
||||
);
|
||||
});
|
||||
@ -9,13 +9,16 @@ export class DataOverlayPageObject {
|
||||
this.viewportId = viewportId;
|
||||
}
|
||||
|
||||
get menu() {
|
||||
return this.page.getByTestId(`viewport-data-overlay-menu-${this.viewportId}`);
|
||||
}
|
||||
|
||||
async toggle() {
|
||||
await this.page.getByTestId(`dataOverlayMenu-${this.viewportId}-btn`).click();
|
||||
}
|
||||
|
||||
async addSegmentation(segmentation: string) {
|
||||
await this.page.getByTestId(`AddSegmentationDataOverlay-${this.viewportId}`).click();
|
||||
await this.page.getByText('SELECT A SEGMENTATION').click();
|
||||
await this.openAddSegmentationDropdown();
|
||||
await this.page.getByTestId(segmentation).click();
|
||||
}
|
||||
|
||||
@ -28,4 +31,32 @@ export class DataOverlayPageObject {
|
||||
await this.page.getByTestId(`overlay-ds-more-button-${segmentation}`).click();
|
||||
await this.page.getByTestId(`overlay-ds-remove-button-${segmentation}`).click();
|
||||
}
|
||||
|
||||
async openAddSegmentationDropdown() {
|
||||
await this.page.getByTestId(`AddSegmentationDataOverlay-${this.viewportId}`).click();
|
||||
await this.clickSelectSegmentation();
|
||||
}
|
||||
|
||||
async clickSelectSegmentation() {
|
||||
await this.page.getByText('SELECT A SEGMENTATION').click();
|
||||
}
|
||||
|
||||
async getDropdownOptionLabels(): Promise<string[]> {
|
||||
const options = this.page.getByRole('option');
|
||||
const labels = (await options.allTextContents()).map(text => text.trim()).filter(Boolean);
|
||||
return labels;
|
||||
}
|
||||
|
||||
getOverlaySegmentationRow(label: string) {
|
||||
return this.menu.getByTestId(`overlay-ds-select-value-${label.toUpperCase()}`);
|
||||
}
|
||||
|
||||
get overlaySegmentationRows() {
|
||||
return this.menu.locator('[data-cy^="overlay-ds-select-value-"]');
|
||||
}
|
||||
|
||||
async changeBackgroundDisplaySet(backgroundLabel: string) {
|
||||
await this.page.getByTestId(`overlay-background-ds-select-${this.viewportId}`).click();
|
||||
await this.page.getByTestId(backgroundLabel).click();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user