fix(ArrowAnnotate): Allow free-form text for arrow annotations (#5259)
This commit is contained in:
parent
4e6530d1d8
commit
3c8446a5e5
@ -637,10 +637,22 @@ function commandsModule({
|
||||
|
||||
viewportGridService.setActiveViewportId(viewportId);
|
||||
},
|
||||
arrowTextCallback: async ({ callback }) => {
|
||||
arrowTextCallback: async ({ callback, data }) => {
|
||||
const labelConfig = customizationService.getCustomization('measurementLabels');
|
||||
const renderContent = customizationService.getCustomization('ui.labellingComponent');
|
||||
|
||||
if (!labelConfig) {
|
||||
const label = await callInputDialog({
|
||||
uiDialogService,
|
||||
title: 'Edit Arrow Text',
|
||||
placeholder: data?.data?.label || 'Enter new text',
|
||||
defaultValue: data?.data?.label || '',
|
||||
});
|
||||
|
||||
callback?.(label);
|
||||
return;
|
||||
}
|
||||
|
||||
const value = await callInputDialogAutoComplete({
|
||||
uiDialogService,
|
||||
labelConfig,
|
||||
|
||||
@ -409,10 +409,6 @@ const connectMeasurementServiceToTools = ({
|
||||
setAnnotationLabel(sourceAnnotation, element, label);
|
||||
}
|
||||
|
||||
if (metadata.toolName === 'ArrowAnnotate') {
|
||||
data.text = label;
|
||||
}
|
||||
|
||||
// update the isLocked state
|
||||
annotation.locking.setAnnotationLocked(uid, isLocked);
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ const ArrowAnnotate = {
|
||||
frameNumber: mappedAnnotations[0]?.frameNumber || 1,
|
||||
toolName: metadata.toolName,
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
label: data.text,
|
||||
label: data.label,
|
||||
displayText: displayText,
|
||||
data: data.cachedStats,
|
||||
type: getValueTypeFromToolType(toolName),
|
||||
|
||||
@ -295,6 +295,7 @@ export const DataRow: React.FC<DataRowProps> = ({
|
||||
: 'opacity-0 group-hover:opacity-100'
|
||||
}`}
|
||||
aria-label="Actions"
|
||||
dataCY="actionsMenuTrigger"
|
||||
onClick={e => e.stopPropagation()} // Prevent row selection on button click
|
||||
>
|
||||
<Icons.More className="h-6 w-6" />
|
||||
@ -308,21 +309,41 @@ export const DataRow: React.FC<DataRowProps> = ({
|
||||
<>
|
||||
<DropdownMenuItem onClick={e => handleAction('Rename', e)}>
|
||||
<Icons.Rename className="text-foreground" />
|
||||
<span className="pl-2">Rename</span>
|
||||
<span
|
||||
className="pl-2"
|
||||
data-cy="Rename"
|
||||
>
|
||||
Rename
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={e => handleAction('Delete', e)}>
|
||||
<Icons.Delete className="text-foreground" />
|
||||
<span className="pl-2">Delete</span>
|
||||
<span
|
||||
className="pl-2"
|
||||
data-cy="Delete"
|
||||
>
|
||||
Delete
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
{onColor && (
|
||||
<DropdownMenuItem onClick={e => handleAction('Color', e)}>
|
||||
<Icons.ColorChange className="text-foreground" />
|
||||
<span className="pl-2">Change Color</span>
|
||||
<span
|
||||
className="pl-2"
|
||||
data-cy="Change Color"
|
||||
>
|
||||
Change Color
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem onClick={e => handleAction('Lock', e)}>
|
||||
<Icons.Lock className="text-foreground" />
|
||||
<span className="pl-2">{isLocked ? 'Unlock' : 'Lock'}</span>
|
||||
<span
|
||||
className="pl-2"
|
||||
data-cy="LockToggle"
|
||||
>
|
||||
{isLocked ? 'Unlock' : 'Lock'}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@ -125,6 +125,7 @@ const InputDialogInput = React.forwardRef<HTMLInputElement, InputDialogInputProp
|
||||
<InputComponent
|
||||
ref={inputRef}
|
||||
id={id}
|
||||
data-cy={id}
|
||||
value={value}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
|
||||
@ -24,6 +24,8 @@ test.describe('3D four up Test', async () => {
|
||||
|
||||
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
|
||||
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
await checkForScreenshot(
|
||||
page,
|
||||
page,
|
||||
|
||||
@ -22,6 +22,7 @@ test.describe('3D main Test', async () => {
|
||||
.first()
|
||||
.click();
|
||||
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
|
||||
await page.waitForTimeout(5000);
|
||||
await checkForScreenshot(page, page, screenShotPaths.threeDMain.threeDMainDisplayedCorrectly);
|
||||
});
|
||||
});
|
||||
|
||||
@ -22,6 +22,7 @@ test.describe('3D only Test', async () => {
|
||||
.first()
|
||||
.click();
|
||||
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
|
||||
await page.waitForTimeout(5000);
|
||||
// Use a 4 percent diff pixel ratio to account for slight color differences in the 3D viewport
|
||||
await checkForScreenshot({
|
||||
page,
|
||||
|
||||
@ -23,6 +23,7 @@ test.describe('3D primary Test', async () => {
|
||||
.click();
|
||||
|
||||
await attemptAction(() => reduce3DViewportSize(page), 10, 100);
|
||||
await page.waitForTimeout(5000);
|
||||
await checkForScreenshot(
|
||||
page,
|
||||
page,
|
||||
|
||||
86
tests/ArrowAnnotate.spec.ts
Normal file
86
tests/ArrowAnnotate.spec.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { test } from 'playwright-test-coverage';
|
||||
import {
|
||||
visitStudy,
|
||||
checkForScreenshot,
|
||||
screenShotPaths,
|
||||
simulateClicksOnElement,
|
||||
simulateDoubleClickOnElement,
|
||||
} from './utils';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5';
|
||||
const mode = 'viewer';
|
||||
await visitStudy(page, studyInstanceUID, mode, 2000);
|
||||
});
|
||||
|
||||
test('should display the arrow tool and allow free-form text to be entered', async ({ page }) => {
|
||||
await page.getByTestId('trackedMeasurements-btn').click();
|
||||
|
||||
await page.getByTestId('MeasurementTools-split-button-secondary').click();
|
||||
await page.getByTestId('ArrowAnnotate').click();
|
||||
|
||||
const locator = page.getByTestId('viewport-pane').locator('canvas');
|
||||
await simulateClicksOnElement({
|
||||
locator,
|
||||
points: [
|
||||
{
|
||||
x: 164,
|
||||
y: 234,
|
||||
},
|
||||
{
|
||||
x: 344,
|
||||
y: 232,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await page.getByTestId('dialog-input').fill('Ringo Starr was the drummer for The Beatles');
|
||||
await page.getByTestId('input-dialog-save-button').click();
|
||||
|
||||
await page.getByTestId('prompt-begin-tracking-yes-btn').click();
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await checkForScreenshot({
|
||||
page,
|
||||
maxDiffPixelRatio: 0.0075,
|
||||
screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly0,
|
||||
});
|
||||
|
||||
// Now edit the arrow text and the label should not change.
|
||||
|
||||
await simulateDoubleClickOnElement({
|
||||
locator,
|
||||
point: {
|
||||
x: 164,
|
||||
y: 234,
|
||||
},
|
||||
});
|
||||
|
||||
await page.getByTestId('dialog-input').fill('Neil Peart was the drummer for Rush');
|
||||
await page.getByTestId('input-dialog-save-button').click();
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await checkForScreenshot({
|
||||
page,
|
||||
maxDiffPixelRatio: 0.0075,
|
||||
screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly1,
|
||||
});
|
||||
|
||||
// Now edit the label and the text should not change.
|
||||
|
||||
await page.getByTestId('actionsMenuTrigger').click();
|
||||
await page.getByTestId('Rename').click();
|
||||
|
||||
await page.getByTestId('dialog-input').fill('Drummer annotation arrow');
|
||||
await page.getByTestId('input-dialog-save-button').click();
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await checkForScreenshot({
|
||||
page,
|
||||
maxDiffPixelRatio: 0.0075,
|
||||
screenshotPath: screenShotPaths.arrowAnnotate.arrowAnnotateDisplayedCorrectly2,
|
||||
});
|
||||
});
|
||||
@ -14,12 +14,16 @@ test('should render scroll bars with the correct look-and-feel', async ({ page }
|
||||
);
|
||||
|
||||
await studyRowHeader.scrollIntoViewIfNeeded();
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await studyRowHeader.click();
|
||||
|
||||
const expandedStudyRow = await page.getByTestId(
|
||||
'studyRow-1.3.6.1.4.1.14519.5.2.1.5099.8010.217836670708542506360829799868'
|
||||
);
|
||||
|
||||
await expandedStudyRow.scrollIntoViewIfNeeded();
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await checkForScreenshot({
|
||||
page,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 251 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 250 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 247 KiB |
@ -3,6 +3,7 @@ import { checkForScreenshot } from './checkForScreenshot';
|
||||
import { screenShotPaths } from './screenShotPaths';
|
||||
import {
|
||||
simulateClicksOnElement,
|
||||
simulateDoubleClickOnElement,
|
||||
simulateNormalizedClickOnElement,
|
||||
simulateNormalizedClicksOnElement,
|
||||
} from './simulateClicksOnElement';
|
||||
@ -20,6 +21,7 @@ export {
|
||||
checkForScreenshot,
|
||||
screenShotPaths,
|
||||
simulateClicksOnElement,
|
||||
simulateDoubleClickOnElement,
|
||||
simulateNormalizedClickOnElement,
|
||||
simulateNormalizedClicksOnElement,
|
||||
reduce3DViewportSize,
|
||||
|
||||
@ -2,6 +2,11 @@
|
||||
* Paths to the screenshots of the tests.
|
||||
*/
|
||||
const screenShotPaths = {
|
||||
arrowAnnotate: {
|
||||
arrowAnnotateDisplayedCorrectly0: 'arrowAnnotateDisplayedCorrectly0.png',
|
||||
arrowAnnotateDisplayedCorrectly1: 'arrowAnnotateDisplayedCorrectly1.png',
|
||||
arrowAnnotateDisplayedCorrectly2: 'arrowAnnotateDisplayedCorrectly2.png',
|
||||
},
|
||||
angle: {
|
||||
angleDisplayedCorrectly: 'angleDisplayedCorrectly.png',
|
||||
},
|
||||
|
||||
@ -9,15 +9,28 @@ import { Locator } from 'playwright';
|
||||
export async function simulateClicksOnElement({
|
||||
locator,
|
||||
points,
|
||||
button = 'left',
|
||||
}: {
|
||||
locator: Locator;
|
||||
points: { x: number; y: number }[];
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
}) {
|
||||
for (const { x, y } of points) {
|
||||
await locator.click({ delay: 100, position: { x, y } });
|
||||
await locator.click({ delay: 100, position: { x, y }, button });
|
||||
}
|
||||
}
|
||||
|
||||
export async function simulateDoubleClickOnElement({
|
||||
locator,
|
||||
point,
|
||||
}: {
|
||||
locator: Locator;
|
||||
point: { x: number; y: number };
|
||||
}) {
|
||||
const { x, y } = point;
|
||||
await locator.dblclick({ delay: 100, position: { x, y } });
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates clicks on an element at a normalized point.
|
||||
*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user