diff --git a/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx b/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx
index 5e76c2046..209d190a6 100644
--- a/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx
+++ b/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx
@@ -67,10 +67,9 @@ function CustomizableViewportOverlay({
}) {
const { cornerstoneViewportService, customizationService, toolGroupService, displaySetService } =
servicesManager.services;
- const [voi, setVOI] = useState({ windowCenter: null, windowWidth: null });
const [scale, setScale] = useState(1);
const [annotationState, setAnnotationState] = useState(0);
- const { isViewportBackgroundLight: isLight } = useViewportRendering(viewportId);
+ const { isViewportBackgroundLight: isLight, windowLevel: voi } = useViewportRendering(viewportId);
const { imageIndex } = imageSliceData;
// Historical usage defined the overlays as separate items due to lack of
@@ -110,30 +109,6 @@ function CustomizableViewportOverlay({
};
}, [viewportData, viewportId, instanceNumber, cornerstoneViewportService]);
- /**
- * Updating the VOI when the viewport changes its voi
- */
- useEffect(() => {
- const updateVOI = eventDetail => {
- const { range } = eventDetail.detail;
-
- if (!range) {
- return;
- }
-
- const { lower, upper } = range;
- const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel(lower, upper);
-
- setVOI({ windowCenter, windowWidth });
- };
-
- element.addEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
-
- return () => {
- element.removeEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
- };
- }, [viewportId, viewportData, voi, element]);
-
const annotationModified = useCallback(evt => {
if (evt.detail.annotation.metadata.toolName === UltrasoundPleuraBLineTool.toolName) {
// Update the annotation state to trigger a re-render
@@ -226,6 +201,7 @@ function CustomizableViewportOverlay({
scale,
instanceNumber,
annotationState,
+ isLight,
]
);
@@ -402,6 +378,7 @@ function OverlayItem(props) {
*/
function VOIOverlayItem({ voi, customization }: OverlayItemProps) {
const { windowWidth, windowCenter } = voi;
+ const { title } = customization;
if (typeof windowCenter !== 'number' || typeof windowWidth !== 'number') {
return null;
}
@@ -410,6 +387,7 @@ function VOIOverlayItem({ voi, customization }: OverlayItemProps) {
W:
{windowWidth.toFixed(0)}
@@ -443,11 +421,13 @@ function InstanceNumberOverlayItem({
customization,
}: OverlayItemProps) {
const { imageIndex, numberOfSlices } = imageSliceData;
+ const { title } = customization;
return (
{instanceNumber !== undefined && instanceNumber !== null ? (
diff --git a/extensions/cornerstone/src/customizations/viewportOverlayCustomization.tsx b/extensions/cornerstone/src/customizations/viewportOverlayCustomization.tsx
index 9d3e2e1b2..a26cd6ada 100644
--- a/extensions/cornerstone/src/customizations/viewportOverlayCustomization.tsx
+++ b/extensions/cornerstone/src/customizations/viewportOverlayCustomization.tsx
@@ -25,6 +25,7 @@ export default {
{
id: 'WindowLevel',
inheritsFrom: 'ohif.overlayItem.windowLevel',
+ title: 'Window Level',
},
{
id: 'ZoomLevel',
@@ -39,6 +40,7 @@ export default {
{
id: 'InstanceNumber',
inheritsFrom: 'ohif.overlayItem.instanceNumber',
+ title: 'Instance Number',
},
],
};
diff --git a/extensions/cornerstone/src/hooks/useViewportRendering.tsx b/extensions/cornerstone/src/hooks/useViewportRendering.tsx
index 08538623e..a7a12f353 100644
--- a/extensions/cornerstone/src/hooks/useViewportRendering.tsx
+++ b/extensions/cornerstone/src/hooks/useViewportRendering.tsx
@@ -10,6 +10,7 @@ import {
BaseVolumeViewport,
cache,
} from '@cornerstonejs/core';
+import { getDataIdForViewport } from '../utils/getDataIdForViewport';
import { WindowLevelPreset } from '../types/WindowLevel';
import { ColorbarPositionType, ColorbarOptions, ColorbarProperties } from '../types/Colorbar';
import { VolumeRenderingConfig } from '../types/VolumeRenderingConfig';
@@ -104,11 +105,6 @@ const opacityToLinear = (opacityValue: number): number => {
return Math.pow(opacityValue, 1.0 / GAMMA);
};
-const is3DViewport = ({ viewportId, cornerstoneViewportService }) => {
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
- return viewport instanceof VolumeViewport3D;
-};
-
/**
* Hook to access window level functionality for a specific viewport
*
@@ -124,15 +120,17 @@ export function useViewportRendering(
const { cornerstoneViewportService, colorbarService, customizationService } =
servicesManager.services;
- const [is3DVolume, setIs3DVolume] = useState(
- is3DViewport({ viewportId, cornerstoneViewportService })
- );
const [hasColorbar, setHasColorbar] = useState(colorbarService.hasColorbar(viewportId));
const [colorbarPosition, setColorbarPosition] = useState(
options?.location ? getPosition(options.location) : 'bottom'
);
const [voiRange, setVoiRange] = useState<{ lower: number; upper: number } | undefined>();
const voiRangeRef = React.useRef<{ lower: number; upper: number } | undefined>();
+ // Viewport from service; kept in state so we can subscribe to VIEWPORT_DATA_CHANGED when null and re-run effects when it becomes available
+ const [viewport, setViewport] = useState(() =>
+ viewportId ? (cornerstoneViewportService.getCornerstoneViewport(viewportId) ?? null) : null
+ );
+ const [is3DVolume, setIs3DVolume] = useState(viewport instanceof VolumeViewport3D);
const [opacity, setOpacityState] = useState();
const [opacityLinear, setOpacityLinearState] = useState();
const [threshold, setThresholdState] = useState();
@@ -178,6 +176,34 @@ export function useViewportRendering(
);
}, [viewportDisplaySets, presets]);
+ // Keep viewport in state; when not available, subscribe to VIEWPORT_DATA_CHANGED so we set it when the viewport is ready
+ useEffect(() => {
+ if (!viewportId) {
+ setViewport(null);
+ return;
+ }
+ const vp = cornerstoneViewportService.getCornerstoneViewport(viewportId);
+ setViewport(vp ?? null);
+ if (vp) {
+ return;
+ }
+ const { unsubscribe } = cornerstoneViewportService.subscribe(
+ cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
+ ({ viewportId: eventViewportId }) => {
+ if (eventViewportId !== viewportId) {
+ return;
+ }
+ const next = cornerstoneViewportService.getCornerstoneViewport(viewportId);
+ if (next) {
+ setViewport(next);
+ }
+ }
+ );
+ return () => {
+ unsubscribe();
+ };
+ }, [viewportId, cornerstoneViewportService]);
+
// Calculate pixel value range for the active display set
useEffect(() => {
if (!activeDisplaySetInstanceUID) {
@@ -189,17 +215,15 @@ export function useViewportRendering(
return;
}
- const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
-
- if (!csViewport) {
+ if (!viewport) {
return;
}
- if (!(csViewport instanceof BaseVolumeViewport)) {
+ if (!(viewport instanceof BaseVolumeViewport)) {
return;
}
- const volumeIds = csViewport.getAllVolumeIds();
+ const volumeIds = viewport.getAllVolumeIds();
const volumeId = volumeIds.find(id => id.includes(activeDisplaySetInstanceUID));
if (!volumeId) {
@@ -207,7 +231,7 @@ export function useViewportRendering(
}
// only handle volume viewports for now
- const imageData = csViewport.getImageData(volumeId);
+ const imageData = viewport.getImageData(volumeId);
if (!imageData) {
return;
@@ -220,7 +244,7 @@ export function useViewportRendering(
const range = voxelManager.getRange();
setPixelValueRange({ min: range[0], max: range[1] });
- }, [activeDisplaySetInstanceUID, displaySetService, cornerstoneViewportService, viewportId]);
+ }, [activeDisplaySetInstanceUID, displaySetService, viewport]);
// Get the presets specifically for the active display set
const activeDisplaySetPresets = useMemo(() => {
@@ -240,55 +264,47 @@ export function useViewportRendering(
}, [allWindowLevelPresets, activeDisplaySetInstanceUID]);
useEffect(() => {
- setIs3DVolume(is3DViewport({ viewportId, cornerstoneViewportService }));
+ setIs3DVolume(viewport instanceof VolumeViewport3D);
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
-
- // Initialize the VOI range from the viewport
- if (viewport && activeDisplaySetInstanceUID) {
- try {
- let properties;
-
- if (viewport instanceof StackViewport) {
- properties = viewport.getProperties();
- if (properties.voiRange) {
- setVoiRange(properties.voiRange);
- voiRangeRef.current = properties.voiRange;
- }
- } else if (viewport instanceof BaseVolumeViewport) {
- // For volume viewports, find the actor for the active display set
- const volumeIds = viewport.getAllVolumeIds();
- const volumeId = volumeIds.find(id => id.includes(activeDisplaySetInstanceUID));
-
- if (volumeId) {
- properties = viewport.getProperties(volumeId);
- if (properties?.voiRange) {
- setVoiRange(properties.voiRange);
- voiRangeRef.current = properties.voiRange;
- }
-
- // Get opacity from colormap if available
- if (properties?.colormap?.opacity !== undefined) {
- const isArray = Array.isArray(properties.colormap.opacity);
- const opacity = isArray
- ? properties.colormap.opacity.reduce((max, current) => Math.max(max, current), 0)
- : properties.colormap.opacity;
-
- setOpacityState(opacity);
- setOpacityLinearState(opacityToLinear(opacity));
- }
-
- // Get threshold from colormap if available
- if (properties?.colormap && properties.colormap.threshold !== undefined) {
- setThresholdState(properties.colormap.threshold);
- }
- }
- }
- } catch (error) {
- console.error('Error initializing VOI range:', error);
- }
+ if (!viewport || !activeDisplaySetInstanceUID) {
+ return;
}
- }, [cornerstoneViewportService, viewportId, activeDisplaySetInstanceUID]);
+ try {
+ const dataId = getDataIdForViewport(viewport as unknown, activeDisplaySetInstanceUID);
+
+ const properties =
+ dataId != null
+ ? (viewport as Types.IBaseVolumeViewport).getProperties(dataId)
+ : viewport.getProperties();
+
+ if (!properties) {
+ return;
+ }
+
+ if (properties.voiRange) {
+ setVoiRange(properties.voiRange);
+ voiRangeRef.current = properties.voiRange;
+ }
+
+ if (properties.colormap?.opacity !== undefined) {
+ const opacityVal = properties.colormap.opacity;
+ const opacity = Array.isArray(opacityVal)
+ ? (opacityVal as unknown as number[]).reduce(
+ (max, current) => Math.max(max, current),
+ 0
+ )
+ : opacityVal;
+ setOpacityState(opacity);
+ setOpacityLinearState(opacityToLinear(opacity));
+ }
+
+ if (properties.colormap?.threshold !== undefined) {
+ setThresholdState(properties.colormap.threshold);
+ }
+ } catch (error) {
+ console.error('Error initializing VOI range:', error);
+ }
+ }, [activeDisplaySetInstanceUID, viewport]);
useEffect(() => {
if (!viewportId) {
@@ -313,19 +329,11 @@ export function useViewportRendering(
}, [colorbarService, viewportId]);
useEffect(() => {
- if (!viewportId || !activeDisplaySetInstanceUID) {
- return;
- }
-
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
- if (!viewport) {
+ if (!activeDisplaySetInstanceUID || !viewport?.element) {
return;
}
const element = viewport.element;
- if (!element) {
- return;
- }
const updateVOI = eventDetail => {
const { range } = eventDetail.detail;
@@ -369,7 +377,7 @@ export function useViewportRendering(
element.removeEventListener(Enums.Events.VOI_MODIFIED, updateVOI);
element.removeEventListener(Enums.Events.COLORMAP_MODIFIED, updateColormap);
};
- }, [viewportId, activeDisplaySetInstanceUID, cornerstoneViewportService, opacityToLinear]);
+ }, [activeDisplaySetInstanceUID, viewport, opacityToLinear]);
const validateActiveDisplaySet = useCallback(() => {
if (!activeDisplaySetInstanceUID) {
@@ -560,16 +568,11 @@ export function useViewportRendering(
context: 'CORNERSTONE',
});
},
- [commandsManager, viewportId, validateActiveDisplaySet]
+ [commandsManager, validateActiveDisplaySet, viewport, viewportId]
);
const setOpacity = useCallback(
(opacityValue: number) => {
- if (!viewportId) {
- return;
- }
-
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (!viewport || !(viewport instanceof BaseVolumeViewport)) {
return;
}
@@ -607,7 +610,7 @@ export function useViewportRendering(
viewport.render();
},
- [cornerstoneViewportService, viewportId, validateActiveDisplaySet, opacityToLinear]
+ [validateActiveDisplaySet, opacityToLinear, viewport]
);
const setOpacityLinear = useCallback(
@@ -621,11 +624,6 @@ export function useViewportRendering(
const setThreshold = useCallback(
(thresholdValue: number) => {
- if (!viewportId) {
- return;
- }
-
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (!viewport || !(viewport instanceof BaseVolumeViewport)) {
return;
}
@@ -653,18 +651,16 @@ export function useViewportRendering(
viewport.render();
},
- [cornerstoneViewportService, viewportId, validateActiveDisplaySet]
+ [validateActiveDisplaySet, viewport]
);
// Get the current colormap for the active display set
const colormap = useMemo(() => {
- if (!viewportId || !activeDisplaySetInstanceUID || !viewportDisplaySets?.length) {
+ if (!activeDisplaySetInstanceUID || !viewportDisplaySets?.length) {
return null;
}
try {
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
-
if (!viewport) {
return null;
}
@@ -711,13 +707,7 @@ export function useViewportRendering(
colorbarProperties?.colormaps?.[0]
);
}
- }, [
- cornerstoneViewportService,
- viewportId,
- activeDisplaySetInstanceUID,
- viewportDisplaySets,
- colorbarProperties?.colormaps,
- ]);
+ }, [activeDisplaySetInstanceUID, viewportDisplaySets, colorbarProperties?.colormaps, viewport]);
// 3D volume rendering functions
const setVolumeRenderingPreset = useCallback(
@@ -801,7 +791,9 @@ export function useViewportRendering(
setWindowLevel,
setVOIRange,
voiRange,
- windowLevel: utilities.windowLevel.toWindowLevel(voiRange?.lower, voiRange?.upper),
+ windowLevel: voiRange
+ ? utilities.windowLevel.toWindowLevel(voiRange?.lower, voiRange?.upper)
+ : { windowCenter: null, windowWidth: null },
// Colorbar functions
hasColorbar,
diff --git a/extensions/cornerstone/src/services/ColorbarService/ColorbarService.ts b/extensions/cornerstone/src/services/ColorbarService/ColorbarService.ts
index 638a7132f..7e1705631 100644
--- a/extensions/cornerstone/src/services/ColorbarService/ColorbarService.ts
+++ b/extensions/cornerstone/src/services/ColorbarService/ColorbarService.ts
@@ -1,6 +1,7 @@
import { PubSubService, Types as OhifTypes } from '@ohif/core';
import { RENDERING_ENGINE_ID } from '../ViewportService/constants';
import { getRenderingEngine } from '@cornerstonejs/core';
+import { getDataIdForViewport } from '../../utils/getDataIdForViewport';
import { ColorbarOptions, ChangeTypes } from '../../types/Colorbar';
export default class ColorbarService extends PubSubService {
@@ -38,25 +39,6 @@ export default class ColorbarService extends PubSubService {
this.servicesManager = servicesManager;
}
- /**
- * Gets the appropriate data ID for a viewport and display set
- * @param viewport - The viewport instance
- * @param displaySetInstanceUID - The display set instance UID to identify data
- * @returns The appropriate data ID for the viewport type (volumeId for volume viewports, undefined for stack)
- */
- private getDataIdForViewport(viewport, displaySetInstanceUID: string): string | undefined {
- // For volume viewports, find the matching volumeId
- if (viewport.getAllVolumeIds) {
- const volumeIds = viewport.getAllVolumeIds() || [];
- return volumeIds.length > 0
- ? volumeIds.find(id => id.includes(displaySetInstanceUID)) || undefined
- : undefined;
- }
-
- // For other viewports, no specific dataId is needed for now
- return undefined;
- }
-
/**
* Adds a colorbar to a specific viewport identified by `viewportId`, using the provided `displaySetInstanceUIDs` and `options`.
* This method prepares the colorbar state that will be used by the ViewportColorbarsContainer component.
@@ -92,7 +74,7 @@ export default class ColorbarService extends PubSubService {
return;
}
- const dataId = this.getDataIdForViewport(viewport, displaySetInstanceUID);
+ const dataId = getDataIdForViewport(viewport, displaySetInstanceUID);
const properties = dataId ? viewport.getProperties(dataId) : viewport.getProperties();
const colormap = properties?.colormap;
@@ -246,7 +228,7 @@ export default class ColorbarService extends PubSubService {
}
// Get the appropriate dataId for this viewport/displaySet combination
- const dataId = this.getDataIdForViewport(viewport, displaySetInstanceUID);
+ const dataId = getDataIdForViewport(viewport, displaySetInstanceUID);
// Set properties with or without dataId based on what the viewport supports
viewport.setProperties({ colormap }, dataId);
diff --git a/extensions/cornerstone/src/utils/getDataIdForViewport.ts b/extensions/cornerstone/src/utils/getDataIdForViewport.ts
new file mode 100644
index 000000000..3f9111c14
--- /dev/null
+++ b/extensions/cornerstone/src/utils/getDataIdForViewport.ts
@@ -0,0 +1,22 @@
+/**
+ * Resolves the data ID (e.g. volumeId) for a viewport and display set.
+ * For viewports with multiple volumes/actors, returns the id that matches the display set; otherwise undefined.
+ * Use this to call viewport.getProperties(dataId) in a viewport-type-agnostic way.
+ *
+ * @param viewport - Viewport instance (stack, volume, or future types with optional getAllVolumeIds)
+ * @param displaySetInstanceUID - Display set instance UID to match
+ * @returns volumeId (or equivalent) for multi-actor viewports, undefined for single-actor
+ */
+export function getDataIdForViewport(
+ viewport: unknown,
+ displaySetInstanceUID: string
+): string | undefined {
+ const vp = viewport as { getAllVolumeIds?: () => string[] };
+ if (typeof vp.getAllVolumeIds !== 'function') {
+ return undefined;
+ }
+ const volumeIds = vp.getAllVolumeIds() || [];
+ return volumeIds.length > 0
+ ? volumeIds.find(id => id.includes(displaySetInstanceUID)) ?? undefined
+ : undefined;
+}
diff --git a/tests/MeasurementPanel.spec.ts b/tests/MeasurementPanel.spec.ts
index 6b686fa7c..86de99424 100644
--- a/tests/MeasurementPanel.spec.ts
+++ b/tests/MeasurementPanel.spec.ts
@@ -117,7 +117,7 @@ test('checks if image would jump when clicked on a measurement item', async ({
rightPanelPageObject,
viewportPageObject,
}) => {
- const viewportInfoBottomRight = viewportPageObject.active.overlayText.bottomRight;
+ const viewportInfoBottomRight = viewportPageObject.active.overlayText.bottomRight.instanceNumber;
// Image loads on slice 1, confirm on slice 1 then add measurement
await expect(viewportInfoBottomRight).toContainText('1/', { timeout: 10000 });
diff --git a/tests/SegmentationPanel.spec.ts b/tests/SegmentationPanel.spec.ts
index 98ede3f90..046f46bb2 100644
--- a/tests/SegmentationPanel.spec.ts
+++ b/tests/SegmentationPanel.spec.ts
@@ -44,7 +44,7 @@ test('checks saved segmentations loads and jumps to slices', async ({
rightPanelPageObject,
viewportPageObject,
}) => {
- const viewportInfoBottomRight = viewportPageObject.active.overlayText.bottomRight;
+ const viewportInfoBottomRight = viewportPageObject.active.overlayText.bottomRight.instanceNumber;
// Image loads on slice 1, confirm on slice 1
await expect(viewportInfoBottomRight).toContainText('1/', { timeout: 10000 });
diff --git a/tests/WindowLevelOverlayText.spec.ts b/tests/WindowLevelOverlayText.spec.ts
new file mode 100644
index 000000000..2a7145d6d
--- /dev/null
+++ b/tests/WindowLevelOverlayText.spec.ts
@@ -0,0 +1,40 @@
+import { expect, test, visitStudy } from './utils';
+
+test('should display the window level text as an overlay in various viewport layouts', async ({
+ page,
+ mainToolbarPageObject,
+ viewportPageObject,
+}) => {
+ const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095258.1';
+ const mode = 'viewer';
+ await visitStudy(page, studyInstanceUID, mode, 2000);
+
+ await expect(viewportPageObject.getNth(0).overlayText.bottomLeft.windowLevel).toBeVisible();
+
+ await mainToolbarPageObject.layoutSelection.click();
+ await page.getByTestId('Layout-3-0').click();
+
+ for (let i = 0; i < 3; i++) {
+ await expect(viewportPageObject.getNth(i).overlayText.bottomLeft.windowLevel).toBeVisible();
+ }
+
+ await mainToolbarPageObject.layoutSelection.click();
+ await page.getByTestId('Layout-1-1').click();
+
+ for (let i = 0; i < 3; i++) {
+ await expect(viewportPageObject.getNth(i).overlayText.bottomLeft.windowLevel).toBeVisible();
+ }
+});
+
+test('should display the window level text as an overlay in the various TMTV viewports', async ({
+ page,
+ viewportPageObject,
+}) => {
+ const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.7009.2403.871108593056125491804754960339';
+ const mode = 'tmtv';
+ await visitStudy(page, studyInstanceUID, mode, 2000);
+
+ for (let i = 0; i < 9; i++) {
+ await expect(viewportPageObject.getNth(i).overlayText.bottomLeft.windowLevel).toBeVisible();
+ }
+});
diff --git a/tests/pages/ViewportPageObject.ts b/tests/pages/ViewportPageObject.ts
index 138ac6cf8..690b7eea6 100644
--- a/tests/pages/ViewportPageObject.ts
+++ b/tests/pages/ViewportPageObject.ts
@@ -17,6 +17,23 @@ type NormalizedDragParams = {
config?: { button?: 'left' | 'right' | 'middle'; delay?: number; steps?: number };
};
+export interface IOverlayText {
+ get windowLevel(): Locator;
+ get instanceNumber(): Locator;
+}
+
+function overlayTextFactory(viewport: Locator, id: string): IOverlayText {
+ const locator = viewport.getByTestId(id);
+ return {
+ get windowLevel() {
+ return locator.getByTitle('Window Level');
+ },
+ get instanceNumber() {
+ return locator.getByTitle('Instance Number');
+ },
+ };
+}
+
export interface IViewportPageObject {
nthAnnotation(nth: number): {
locator: Locator;
@@ -46,10 +63,10 @@ export interface IViewportPageObject {
bottomMid: Locator;
};
overlayText: {
- topLeft: Locator;
- topRight: Locator;
- bottomLeft: Locator;
- bottomRight: Locator;
+ topLeft: IOverlayText;
+ topRight: IOverlayText;
+ bottomLeft: IOverlayText;
+ bottomRight: IOverlayText;
};
overlayMenu: {
dataOverlay: DataOverlayPageObject;
@@ -122,10 +139,10 @@ export class ViewportPageObject {
private getOverlayText(viewport: Locator) {
return {
- topLeft: viewport.getByTestId('viewport-overlay-top-left'),
- topRight: viewport.getByTestId('viewport-overlay-top-right'),
- bottomLeft: viewport.getByTestId('viewport-overlay-bottom-left'),
- bottomRight: viewport.getByTestId('viewport-overlay-bottom-right'),
+ topLeft: overlayTextFactory(viewport, 'viewport-overlay-top-left'),
+ topRight: overlayTextFactory(viewport, 'viewport-overlay-top-right'),
+ bottomLeft: overlayTextFactory(viewport, 'viewport-overlay-bottom-left'),
+ bottomRight: overlayTextFactory(viewport, 'viewport-overlay-bottom-right'),
};
}