fix(dicom-tag-browser): Prevent long series names from overlapping - OHIF-2406 (#5809)

This commit is contained in:
Ghadeer Albattarni 2026-02-17 16:56:00 -05:00 committed by GitHub
parent f668848230
commit cca1a8683b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 140 additions and 5 deletions

View File

@ -3,7 +3,14 @@ import moment from 'moment';
import React, { useState, useMemo, useCallback } from 'react'; import React, { useState, useMemo, useCallback } from 'react';
import { classes, Types } from '@ohif/core'; import { classes, Types } from '@ohif/core';
import { InputFilter } from '@ohif/ui-next'; import { InputFilter } from '@ohif/ui-next';
import { Select, SelectTrigger, SelectContent, SelectItem, Slider } from '@ohif/ui-next'; import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
Slider,
} from '@ohif/ui-next';
import DicomTagTable from './DicomTagTable'; import DicomTagTable from './DicomTagTable';
import './DicomTagBrowser.css'; import './DicomTagBrowser.css';
@ -135,9 +142,11 @@ const DicomTagBrowser = ({
value={selectedDisplaySetInstanceUID} value={selectedDisplaySetInstanceUID}
onValueChange={value => onSelectChange({ value })} onValueChange={value => onSelectChange({ value })}
> >
<SelectTrigger> <SelectTrigger data-cy="dicom-tag-series-select-trigger">
{displaySetList.find(ds => ds.value === selectedDisplaySetInstanceUID)?.label || <SelectValue data-cy="dicom-tag-series-select-value">
'Select Series'} {displaySetList.find(ds => ds.value === selectedDisplaySetInstanceUID)?.label ||
'Select Series'}
</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{displaySetList.map(item => { {displaySetList.map(item => {

View File

@ -1,4 +1,5 @@
import { checkForScreenshot, screenShotPaths, test, visitStudy } from './utils'; import { checkForScreenshot, screenShotPaths, test, visitStudy, expect } from './utils';
import { assertBoundingBoxIsContainedWithin } from './utils/assertions';
test('should display the dicom tag browser', async ({ page, mainToolbarPageObject }) => { test('should display the dicom tag browser', async ({ page, mainToolbarPageObject }) => {
const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5'; const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5';
@ -28,3 +29,34 @@ test('should render the scroll bar with the correct look-and-feel', async ({
screenshotPath: screenShotPaths.dicomTagBrowser.scrollBarRenderedProperly, screenshotPath: screenShotPaths.dicomTagBrowser.scrollBarRenderedProperly,
}); });
}); });
test('should display the long series name properly within the series select button', async ({
page,
mainToolbarPageObject,
DOMOverlayPageObject,
}) => {
const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.5099.8010.217836670708542506360829799868';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
await mainToolbarPageObject.moreTools.tagBrowser.click();
const dicomTagBrowser = DOMOverlayPageObject.dialog.dicomTagBrowser;
await dicomTagBrowser.waitVisible();
const seriesSelect = dicomTagBrowser.seriesSelect;
const selectedOptionText = await seriesSelect.selectOption(6);
await expect(seriesSelect.value).toContainText(selectedOptionText);
const triggerBox = await seriesSelect.trigger.boundingBox();
const textBox = await seriesSelect.value.boundingBox();
await assertBoundingBoxIsContainedWithin({
innerBox: textBox,
outerBox: triggerBox,
innerBoxLabel: 'text',
outerBoxLabel: 'trigger',
});
});

View File

@ -1,4 +1,5 @@
import { Locator, Page } from '@playwright/test'; import { Locator, Page } from '@playwright/test';
import { DicomTagBrowserPageObject } from './DicomTagBrowserPageObject';
export class DOMOverlayPageObject { export class DOMOverlayPageObject {
readonly page: Page; readonly page: Page;
@ -46,6 +47,11 @@ export class DOMOverlayPageObject {
}, },
}; };
}, },
get dicomTagBrowser() {
return new DicomTagBrowserPageObject(page);
},
title: page.locator('[role="dialog"] h2'), title: page.locator('[role="dialog"] h2'),
}; };
} }

View File

@ -0,0 +1,43 @@
import { Page } from '@playwright/test';
export class DicomTagBrowserPageObject {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async waitVisible() {
const locator = this.page.locator('[role="dialog"]').filter({
has: this.page.getByTestId('dicom-tag-series-select-trigger'),
});
await locator.waitFor({ state: 'visible' });
}
get seriesSelect() {
const page = this.page;
const trigger = page.getByTestId('dicom-tag-series-select-trigger');
const value = page.getByTestId('dicom-tag-series-select-value');
const options = page.getByRole('option');
return {
trigger,
value,
options,
async click() {
await trigger.click();
},
async selectOption(index) {
await this.click();
const optionText = await options.nth(index).innerText();
const selectedText = optionText.split('\n')[0].trim();
await options.nth(index).click();
return selectedText;
},
};
}
}

View File

@ -4,6 +4,7 @@ import { LeftPanelPageObject } from './LeftPanelPageObject';
import { RightPanelPageObject } from './RightPanelPageObject'; import { RightPanelPageObject } from './RightPanelPageObject';
import { ViewportPageObject } from './ViewportPageObject'; import { ViewportPageObject } from './ViewportPageObject';
import { NotFoundStudyPageObject } from './NotFoundStudyPageObject'; import { NotFoundStudyPageObject } from './NotFoundStudyPageObject';
import { DicomTagBrowserPageObject } from './DicomTagBrowserPageObject';
export { export {
DOMOverlayPageObject, DOMOverlayPageObject,
@ -12,4 +13,5 @@ export {
RightPanelPageObject, RightPanelPageObject,
ViewportPageObject, ViewportPageObject,
NotFoundStudyPageObject, NotFoundStudyPageObject,
DicomTagBrowserPageObject,
}; };

View File

@ -18,3 +18,46 @@ export async function assertNumberOfModalityLoadBadges({
expect(count).toBe(expectedCount); expect(count).toBe(expectedCount);
} }
/**
* Asserts that one bounding box is completely contained within another.
* Checks that all edges of the inner box are within the outer box boundaries.
*/
export async function assertBoundingBoxIsContainedWithin({
innerBox,
outerBox,
innerBoxLabel = 'inner element',
outerBoxLabel = 'outer element',
}: {
innerBox: { x: number; y: number; width: number; height: number } | null;
outerBox: { x: number; y: number; width: number; height: number } | null;
innerBoxLabel?: string;
outerBoxLabel?: string;
}) {
expect(innerBox).not.toBeNull();
expect(outerBox).not.toBeNull();
if (!outerBox || !innerBox) {
return;
}
expect(
innerBox.x,
`${innerBoxLabel} left edge should be within ${outerBoxLabel}`
).toBeGreaterThanOrEqual(outerBox.x);
expect(
innerBox.y,
`${innerBoxLabel} top edge should be within ${outerBoxLabel}`
).toBeGreaterThanOrEqual(outerBox.y);
expect(
innerBox.x + innerBox.width,
`${innerBoxLabel} right edge should be within ${outerBoxLabel}`
).toBeLessThanOrEqual(outerBox.x + outerBox.width);
expect(
innerBox.y + innerBox.height,
`${innerBoxLabel} bottom edge should be within ${outerBoxLabel}`
).toBeLessThanOrEqual(outerBox.y + outerBox.height);
}