Skip to content

Commit 2edb373

Browse files
authored
Extract edit bounding box (Part 1) (#1147)
1 parent 7e6716d commit 2edb373

22 files changed

+229
-210
lines changed

web_ui/src/pages/annotator/annotation/labels/annotation-actions.component.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Annotation } from '../../../../core/annotations/annotation.interface';
99
import { isAnomalyDomain, isClassificationDomain } from '../../../../core/projects/domains';
1010
import { ANIMATION_PARAMETERS } from '../../../../shared/animation-parameters/animation-parameters';
1111
import { hasEqualId } from '../../../../shared/utils';
12-
import { AnnotationToolContext } from '../../core/annotation-tool-context.interface';
12+
import { useAnnotationToolContext } from '../../providers/annotation-tool-provider/annotation-tool-provider.component';
1313
import { useROI } from '../../providers/region-of-interest-provider/region-of-interest-provider.component';
1414
import { getGlobalAnnotations } from '../../providers/task-chain-provider/utils';
1515
import { useTask } from '../../providers/task-provider/task-provider.component';
@@ -18,21 +18,22 @@ import classes from './labels.module.scss';
1818

1919
interface LabelActionsProps {
2020
annotation: Annotation;
21-
annotationToolContext: AnnotationToolContext;
2221
setEditLabels: (editLabels: boolean) => void;
2322
}
2423

25-
const useOnRemoveLabels = (annotationToolContext: AnnotationToolContext, annotation: Annotation) => {
24+
const useOnRemoveLabels = (annotation: Annotation) => {
2625
const { selectedTask } = useTask();
2726
const { roi } = useROI();
28-
const { removeAnnotations, removeLabels } = annotationToolContext.scene;
27+
const {
28+
scene: { annotations, removeAnnotations, removeLabels },
29+
} = useAnnotationToolContext();
2930

3031
if (selectedTask === null) {
3132
return () => removeAnnotations([annotation]);
3233
}
3334

3435
if (isClassificationDomain(selectedTask.domain) || isAnomalyDomain(selectedTask.domain)) {
35-
const globalAnnotations = getGlobalAnnotations(annotationToolContext.scene.annotations, roi, selectedTask);
36+
const globalAnnotations = getGlobalAnnotations(annotations, roi, selectedTask);
3637

3738
if (!globalAnnotations.some(hasEqualId(annotation.id))) {
3839
return () => {
@@ -50,12 +51,8 @@ const useOnRemoveLabels = (annotationToolContext: AnnotationToolContext, annotat
5051
};
5152
};
5253

53-
export const AnnotationActions = ({
54-
annotation,
55-
annotationToolContext,
56-
setEditLabels,
57-
}: LabelActionsProps): JSX.Element => {
58-
const onRemoveLabels = useOnRemoveLabels(annotationToolContext, annotation);
54+
export const AnnotationActions = ({ annotation, setEditLabels }: LabelActionsProps): JSX.Element => {
55+
const onRemoveLabels = useOnRemoveLabels(annotation);
5956

6057
const onEditLabels = () => {
6158
setEditLabels(true);

web_ui/src/pages/annotator/annotation/labels/edit-labels.component.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@ import { isAnomalyDomain } from '../../../../core/projects/domains';
1010
import { TaskLabelTreeSearchPopover } from '../../../../shared/components/task-label-tree-search/task-label-tree-search-popover.component';
1111
import { hasEqualId, runWhenTruthy } from '../../../../shared/utils';
1212
import { SelectionIndicator } from '../../components/labels/label-search/selection-indicator.component';
13-
import { AnnotationToolContext } from '../../core/annotation-tool-context.interface';
13+
import { useAnnotationToolContext } from '../../providers/annotation-tool-provider/annotation-tool-provider.component';
1414
import { useROI } from '../../providers/region-of-interest-provider/region-of-interest-provider.component';
1515
import { getGlobalAnnotations } from '../../providers/task-chain-provider/utils';
1616
import { useTask } from '../../providers/task-provider/task-provider.component';
1717

1818
interface EditLabelsProps {
1919
setEditLabels: (editLabels: boolean) => void;
2020
annotation: Annotation;
21-
annotationToolContext: AnnotationToolContext;
2221
}
2322

24-
export const EditLabels = ({ annotation, annotationToolContext, setEditLabels }: EditLabelsProps): JSX.Element => {
23+
export const EditLabels = ({ annotation, setEditLabels }: EditLabelsProps): JSX.Element => {
2524
const { roi } = useROI();
2625
const { tasks, selectedTask } = useTask();
27-
const { addLabel, removeLabels, annotations } = annotationToolContext.scene;
26+
const {
27+
scene: { addLabel, removeLabels, annotations },
28+
} = useAnnotationToolContext();
2829

2930
const isAnomalyTask = selectedTask && isAnomalyDomain(selectedTask?.domain);
3031
const globalAnnotations = getGlobalAnnotations(annotations, roi, selectedTask);

web_ui/src/pages/annotator/annotation/labels/labels.component.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { useHover } from 'react-aria';
1111
import { Annotation } from '../../../../core/annotations/annotation.interface';
1212
import { ShapeType } from '../../../../core/annotations/shapetype.enum';
1313
import { isRect } from '../../../../core/annotations/utils';
14-
import { AnnotationToolContext, ToolType } from '../../core/annotation-tool-context.interface';
14+
import { ToolType } from '../../core/annotation-tool-context.interface';
15+
import { useAnnotationToolContext } from '../../providers/annotation-tool-provider/annotation-tool-provider.component';
1516
import { useTask } from '../../providers/task-provider/task-provider.component';
1617
import { AnnotationActions } from './annotation-actions.component';
1718
import { EditLabels } from './edit-labels.component';
@@ -27,7 +28,6 @@ export interface LabelsProps {
2728
showOptions?: boolean;
2829
annotation: Annotation;
2930
areLabelsInteractive?: boolean;
30-
annotationToolContext: AnnotationToolContext;
3131
canEditAnnotationLabel?: boolean;
3232
}
3333

@@ -67,12 +67,12 @@ export const Labels = ({
6767
annotation,
6868
isOverlap = false,
6969
showOptions = true,
70-
annotationToolContext,
7170
areLabelsInteractive = true,
7271
canEditAnnotationLabel = true,
7372
}: LabelsProps): JSX.Element => {
7473
const { selectedTask } = useTask();
7574
const { x: left, y, height } = useLabelPosition(annotation);
75+
const { tool } = useAnnotationToolContext();
7676

7777
const [editLabels, setEditLabels] = useState(false);
7878
const { hoverProps, isHovered } = useHover({ isDisabled: editLabels || !canEditAnnotationLabel });
@@ -84,9 +84,10 @@ export const Labels = ({
8484
return undefined;
8585
}
8686

87-
if (!annotation.isSelected && annotationToolContext.tool === ToolType.SelectTool) {
87+
if (!annotation.isSelected && tool === ToolType.SelectTool) {
8888
return isHovered ? 2 : 1;
8989
}
90+
9091
return undefined;
9192
};
9293

@@ -112,11 +113,7 @@ export const Labels = ({
112113
// Make sure the label search component overlaps labels from other annotations
113114
style={{ ...style, zIndex: 2 }}
114115
>
115-
<EditLabels
116-
annotation={annotation}
117-
setEditLabels={setEditLabels}
118-
annotationToolContext={annotationToolContext}
119-
/>
116+
<EditLabels annotation={annotation} setEditLabels={setEditLabels} />
120117
</div>
121118
</>
122119
);
@@ -167,11 +164,7 @@ export const Labels = ({
167164

168165
<AnimatePresence>
169166
{showOptions && isHovered && (
170-
<AnnotationActions
171-
setEditLabels={setEditLabels}
172-
annotation={annotation}
173-
annotationToolContext={annotationToolContext}
174-
/>
167+
<AnnotationActions setEditLabels={setEditLabels} annotation={annotation} />
175168
)}
176169
</AnimatePresence>
177170
</ul>

web_ui/src/pages/annotator/annotation/labels/labels.test.tsx

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import { getMockedLabel, labels, mockedLongLabels } from '../../../../test-utils
1515
import { getMockedTask, mockedTaskContextProps } from '../../../../test-utils/mocked-items-factory/mocked-tasks';
1616
import { providersRender as render } from '../../../../test-utils/required-providers-render';
1717
import { getMockedImage } from '../../../../test-utils/utils';
18-
import { AnnotationToolContext } from '../../core/annotation-tool-context.interface';
18+
import { AnnotationSceneProvider } from '../../providers/annotation-scene-provider/annotation-scene-provider.component';
19+
import { useAnnotationToolContext } from '../../providers/annotation-tool-provider/annotation-tool-provider.component';
1920
import { useROI } from '../../providers/region-of-interest-provider/region-of-interest-provider.component';
2021
import { useTaskChain } from '../../providers/task-chain-provider/task-chain-provider.component';
2122
import { TaskContextProps, useTask } from '../../providers/task-provider/task-provider.component';
@@ -44,26 +45,23 @@ jest.mock('../../providers/region-of-interest-provider/region-of-interest-provid
4445
})),
4546
}));
4647

48+
jest.mock('../../providers/annotation-tool-provider/annotation-tool-provider.component', () => ({
49+
...jest.requireActual('../../providers/annotation-tool-provider/annotation-tool-provider.component'),
50+
useAnnotationToolContext: jest.fn(),
51+
}));
52+
4753
describe('Labels', (): void => {
4854
jest.mocked(useTaskChain).mockImplementation(() => {
4955
return { inputs: [], outputs: [] };
5056
});
5157

52-
const renderApp = (
53-
annotation: Annotation,
54-
annotationToolContext: AnnotationToolContext,
55-
tasksHook: Partial<TaskContextProps> = {},
56-
showOptions = true
57-
) => {
58+
const renderApp = (annotation: Annotation, tasksHook: Partial<TaskContextProps> = {}, showOptions = true) => {
5859
jest.mocked(useTask).mockReturnValue(mockedTaskContextProps(tasksHook));
5960

6061
render(
61-
<Labels
62-
annotation={annotation}
63-
showOptions={showOptions}
64-
annotationToolContext={annotationToolContext}
65-
canEditAnnotationLabel
66-
/>
62+
<AnnotationSceneProvider annotations={[]} labels={[]}>
63+
<Labels annotation={annotation} showOptions={showOptions} canEditAnnotationLabel />)
64+
</AnnotationSceneProvider>
6765
);
6866
};
6967

@@ -73,7 +71,9 @@ describe('Labels', (): void => {
7371
});
7472
const annotationToolContext = fakeAnnotationToolContext({ annotations: [annotation], labels });
7573

76-
renderApp(annotation, annotationToolContext);
74+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
75+
76+
renderApp(annotation);
7777

7878
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
7979

@@ -123,12 +123,14 @@ describe('Labels', (): void => {
123123
labels,
124124
});
125125

126+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
127+
126128
jest.mocked(useROI).mockReturnValue({
127129
roi,
128130
image: getMockedImage(roi),
129131
});
130132

131-
renderApp(annotation, annotationToolContext, { tasks, selectedTask: tasks[0] });
133+
renderApp(annotation, { tasks, selectedTask: tasks[0] });
132134

133135
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
134136

@@ -168,12 +170,14 @@ describe('Labels', (): void => {
168170
labels,
169171
});
170172

173+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
174+
171175
jest.mocked(useROI).mockReturnValue({
172176
roi,
173177
image: getMockedImage(roi),
174178
});
175179

176-
renderApp(annotation, annotationToolContext, { tasks, selectedTask: tasks[0] });
180+
renderApp(annotation, { tasks, selectedTask: tasks[0] });
177181

178182
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
179183

@@ -211,12 +215,14 @@ describe('Labels', (): void => {
211215
labels,
212216
});
213217

218+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
219+
214220
jest.mocked(useROI).mockReturnValue({
215221
roi,
216222
image: getMockedImage(roi),
217223
});
218224

219-
renderApp(annotation, annotationToolContext, { tasks, selectedTask: tasks[0] });
225+
renderApp(annotation, { tasks, selectedTask: tasks[0] });
220226

221227
expect(screen.getByText('Empty')).toBeInTheDocument();
222228
expect(screen.queryByText('Empty (100%)')).not.toBeInTheDocument();
@@ -228,7 +234,9 @@ describe('Labels', (): void => {
228234
});
229235
const annotationToolContext = fakeAnnotationToolContext({ annotations: [annotation], labels });
230236

231-
renderApp(annotation, annotationToolContext, { tasks: [getMockedTask({ labels })] });
237+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
238+
239+
renderApp(annotation, { tasks: [getMockedTask({ labels })] });
232240

233241
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
234242

@@ -259,8 +267,9 @@ describe('Labels', (): void => {
259267
];
260268
const mockedAnnotation = getMockedAnnotation({ labels: mockedAnnotationLabels });
261269
const annotationToolContext = fakeAnnotationToolContext({ annotations: [mockedAnnotation], labels });
270+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
262271

263-
renderApp(mockedAnnotation, annotationToolContext, { tasks: [getMockedTask({ labels })] });
272+
renderApp(mockedAnnotation, { tasks: [getMockedTask({ labels })] });
264273

265274
expect(screen.getByText(mockedLongLabels[0].name)).toHaveStyle('text-overflow: ellipsis');
266275
});
@@ -276,8 +285,9 @@ describe('Labels', (): void => {
276285
];
277286
const mockedAnnotation = getMockedAnnotation({ labels: mockedAnnotationLabels });
278287
const annotationToolContext = fakeAnnotationToolContext({ annotations: [mockedAnnotation], labels });
288+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
279289

280-
renderApp(mockedAnnotation, annotationToolContext, { tasks: [getMockedTask({ labels })] });
290+
renderApp(mockedAnnotation, { tasks: [getMockedTask({ labels })] });
281291

282292
expect(screen.getByText('princess')).toBeInTheDocument();
283293
});
@@ -287,8 +297,9 @@ describe('Labels', (): void => {
287297
labels: [labelFromUser(labels[0]), labelFromUser(labels[1]), labelFromUser(labels[3])],
288298
});
289299
const annotationToolContext = fakeAnnotationToolContext({ annotations: [annotation], labels });
300+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
290301

291-
renderApp(annotation, annotationToolContext, { tasks: [getMockedTask({ labels })] }, false);
302+
renderApp(annotation, { tasks: [getMockedTask({ labels })] }, false);
292303

293304
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
294305

@@ -332,13 +343,14 @@ describe('Labels', (): void => {
332343
const annotationToolContext = fakeAnnotationToolContext({
333344
annotations: [annotation],
334345
});
346+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
335347

336348
jest.mocked(useROI).mockReturnValue({
337349
roi,
338350
image: getMockedImage(roi),
339351
});
340352

341-
renderApp(annotation, annotationToolContext, { tasks, selectedTask: tasks[0] });
353+
renderApp(annotation, { tasks, selectedTask: tasks[0] });
342354

343355
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
344356

@@ -362,8 +374,9 @@ describe('Labels', (): void => {
362374
const annotationToolContext = fakeAnnotationToolContext({
363375
annotations: [annotation],
364376
});
377+
jest.mocked(useAnnotationToolContext).mockReturnValue(annotationToolContext);
365378

366-
renderApp(annotation, annotationToolContext, { tasks, selectedTask: tasks[0] });
379+
renderApp(annotation, { tasks, selectedTask: tasks[0] });
367380

368381
expect(screen.getByRole('list')).toHaveAttribute('id', `${annotation.id}-labels`);
369382

web_ui/src/pages/annotator/annotation/labels/shape-label.component.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,7 @@ interface ShapeLabelProps extends LabelsProps {
99
isOverlap?: boolean;
1010
}
1111

12-
export const ShapeLabel = ({
13-
isOverlap,
14-
annotation,
15-
showOptions,
16-
areLabelsInteractive,
17-
annotationToolContext,
18-
}: ShapeLabelProps) => {
12+
export const ShapeLabel = ({ isOverlap, annotation, showOptions, areLabelsInteractive }: ShapeLabelProps) => {
1913
if (isPoseShape(annotation.shape)) {
2014
return annotation.shape.points.map((point) => (
2115
<ExpandablePointLabel isVisible key={point.label.id} point={point} isOverlap={isOverlap} />
@@ -28,7 +22,6 @@ export const ShapeLabel = ({
2822
showOptions={showOptions}
2923
annotation={annotation}
3024
areLabelsInteractive={areLabelsInteractive}
31-
annotationToolContext={annotationToolContext}
3225
/>
3326
);
3427
};

web_ui/src/pages/annotator/annotation/labels/shape-label.test.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,24 @@ import { screen } from '@testing-library/react';
66
import { Annotation } from '../../../../core/annotations/annotation.interface';
77
import { ShapeType } from '../../../../core/annotations/shapetype.enum';
88
import { SelectedProvider } from '../../../../providers/selected-provider/selected-provider.component';
9-
import { fakeAnnotationToolContext } from '../../../../test-utils/fake-annotator-context';
109
import { getMockedAnnotation } from '../../../../test-utils/mocked-items-factory/mocked-annotations';
1110
import { getMockedKeypointNode } from '../../../../test-utils/mocked-items-factory/mocked-keypoint';
1211
import { getMockedLabel } from '../../../../test-utils/mocked-items-factory/mocked-labels';
12+
import { AnnotationToolProvider } from '../../providers/annotation-tool-provider/annotation-tool-provider.component';
1313
import { TaskProvider } from '../../providers/task-provider/task-provider.component';
1414
import { annotatorRender } from '../../test-utils/annotator-render';
1515
import { ShapeLabel } from './shape-label.component';
1616

1717
describe('ShapeLabel', () => {
1818
const renderApp = async (annotation: Annotation) => {
1919
await annotatorRender(
20-
<TaskProvider>
21-
<SelectedProvider>
22-
<ShapeLabel annotation={annotation} annotationToolContext={fakeAnnotationToolContext({})} />
23-
</SelectedProvider>
24-
</TaskProvider>
20+
<AnnotationToolProvider>
21+
<TaskProvider>
22+
<SelectedProvider>
23+
<ShapeLabel annotation={annotation} />
24+
</SelectedProvider>
25+
</TaskProvider>
26+
</AnnotationToolProvider>
2527
);
2628
};
2729

0 commit comments

Comments
 (0)