Skip to content

Commit 1c07b41

Browse files
feat(annotation-thread): annotation thread events handling (#3196)
* feat(annotation-thread): annotation thread events handling * feat(annotation-thread): annotation thread events handling
1 parent 2b316db commit 1c07b41

File tree

5 files changed

+116
-68
lines changed

5 files changed

+116
-68
lines changed

src/elements/content-sidebar/activity-feed/annotation-thread/AnnotationThread.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
.bcs-ActivityThread {
88
box-shadow: none;
9+
10+
&:hover {
11+
background: none;
12+
}
913
}
1014

1115
.bcs-ActivityThread-content {

src/elements/content-sidebar/activity-feed/annotation-thread/__mocks__/useAnnotationThread.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ import { annotation as mockAnnotation } from '../../../../../__mocks__/annotatio
33
export default () => ({
44
annotation: mockAnnotation,
55
annotationActions: {
6+
handleCreate: jest.fn(),
67
handleDelete: jest.fn(),
78
handleEdit: jest.fn(),
89
handleResolve: jest.fn(),
910
},
10-
annotationEvents: {
11-
handleAnnotationCreateStart: jest.fn(),
12-
handleAnnotationCreateEnd: jest.fn(),
13-
},
1411
error: {},
1512
isLoading: false,
1613
replies: [],

src/elements/content-sidebar/activity-feed/annotation-thread/__tests__/AnnotationThreadCreate.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ describe('elements/content-sidebar/activity-feed/annotation-thread/AnnotationThr
5050
expect(getByTestId('annotation-create')).toHaveClass('is-pending');
5151
});
5252

53-
test('Should handle create', () => {
53+
test('Should call onFormSubmit on create', () => {
5454
const onFormSubmit = jest.fn();
5555

5656
const { getByText } = getWrapper({ onFormSubmit });
@@ -60,7 +60,7 @@ describe('elements/content-sidebar/activity-feed/annotation-thread/AnnotationThr
6060
expect(onFormSubmit).toBeCalledWith('example message');
6161
});
6262

63-
test('Should call handleCancel on cancel', () => {
63+
test('Should call onFormCancel on cancel', () => {
6464
const onFormCancel = jest.fn();
6565

6666
const { getByText } = getWrapper({ onFormCancel });

src/elements/content-sidebar/activity-feed/annotation-thread/__tests__/useAnnotationThread.test.js

Lines changed: 76 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('src/elements/content-sidebar/activity-feed/useAnnotationThread', () =>
1818
const mockUseAnnotatorEventsResult = {
1919
emitAddAnnotationEndEvent: jest.fn(),
2020
emitAddAnnotationStartEvent: jest.fn(),
21+
emitAnnotationActiveChangeEvent: jest.fn(),
2122
emitDeleteAnnotationEndEvent: jest.fn(),
2223
emitDeleteAnnotationStartEvent: jest.fn(),
2324
emitUpdateAnnotationEndEvent: jest.fn(),
@@ -40,16 +41,19 @@ describe('src/elements/content-sidebar/activity-feed/useAnnotationThread', () =>
4041
const filePermissions = { can_annotate: true, can_view_annotations: true };
4142
const errorCallback = jest.fn();
4243

44+
const getFileProps = props => ({
45+
id: 'fileId',
46+
file_version: { id: '123' },
47+
permissions: filePermissions,
48+
...props,
49+
});
50+
4351
const getHook = props =>
4452
renderHook(() =>
4553
useAnnotationThread({
4654
api: {},
4755
currentUser: {},
48-
file: {
49-
id: 'fileId',
50-
file_version: { id: '123' },
51-
permissions: filePermissions,
52-
},
56+
file: getFileProps(),
5357
annotationId: annotation.id,
5458
errorCallback,
5559
eventEmitter: {},
@@ -77,20 +81,85 @@ describe('src/elements/content-sidebar/activity-feed/useAnnotationThread', () =>
7781
expect(result.current.replies).toEqual([]);
7882
});
7983

80-
test('should return correct values after fetch', () => {
84+
test('should return correct values after fetch and call emitAnnotationActiveChangeEvent', () => {
8185
const { replies, ...normalizedAnnotation } = annotation;
8286
const mockHandleFetch = jest.fn().mockImplementation(({ successCallback }) => successCallback(annotation));
8387
useAnnotationAPI.mockImplementation(() => ({
8488
...mockUseAnnotationAPIResult,
8589
handleFetch: mockHandleFetch,
8690
}));
91+
const fileVersionId = '456';
92+
const file = getFileProps({ file_version: { id: fileVersionId } });
8793

88-
const { result } = getHook();
94+
const { result } = getHook({ file });
8995

9096
expect(result.current.annotation).toEqual(normalizedAnnotation);
9197
expect(result.current.isLoading).toEqual(false);
9298
expect(result.current.error).toEqual(undefined);
9399
expect(result.current.replies).toEqual(replies);
100+
expect(mockUseAnnotatorEventsResult.emitAnnotationActiveChangeEvent).toBeCalledWith(
101+
annotation.id,
102+
fileVersionId,
103+
);
104+
});
105+
106+
describe('handleAnnotationEdit', () => {
107+
test('should call handleEdit from useAnnotationAPI and call emitUpdateAnnotationStartEvent + emitUpdateAnnotationEndEvent', () => {
108+
const updatedText = 'new text';
109+
const updatedAnnotation = {
110+
id: annotation.id,
111+
description: { message: updatedText },
112+
};
113+
const mockHandleEdit = jest.fn().mockImplementation(({ successCallback }) => {
114+
successCallback(updatedAnnotation);
115+
});
116+
useAnnotationAPI.mockImplementation(() => ({
117+
...mockUseAnnotationAPIResult,
118+
handleEdit: mockHandleEdit,
119+
}));
120+
121+
const { result } = getHook();
122+
act(() => {
123+
result.current.annotationActions.handleAnnotationEdit(annotation.id, updatedText, { can_edit: true });
124+
});
125+
126+
expect(mockHandleEdit).toBeCalledWith({
127+
id: annotation.id,
128+
permissions: { can_edit: true },
129+
text: updatedText,
130+
successCallback: expect.any(Function),
131+
});
132+
expect(mockUseAnnotatorEventsResult.emitUpdateAnnotationStartEvent).toBeCalledWith(updatedAnnotation);
133+
expect(mockUseAnnotatorEventsResult.emitUpdateAnnotationEndEvent).toBeCalledWith(updatedAnnotation);
134+
});
135+
});
136+
137+
describe('handleAnnotationDelete', () => {
138+
test('should call handleDelete from useAnnotationAPI and call emitDeleteAnnotationStartEvent + emitDeleteAnnotationEndEvent', () => {
139+
const mockHandleDelete = jest.fn().mockImplementation(({ successCallback }) => {
140+
successCallback();
141+
});
142+
useAnnotationAPI.mockImplementation(() => ({
143+
...mockUseAnnotationAPIResult,
144+
handleDelete: mockHandleDelete,
145+
}));
146+
147+
const { result } = getHook();
148+
act(() => {
149+
result.current.annotationActions.handleAnnotationDelete({
150+
id: annotation.id,
151+
permissions: { can_delete: true },
152+
});
153+
});
154+
155+
expect(mockHandleDelete).toBeCalledWith({
156+
id: annotation.id,
157+
permissions: { can_delete: true },
158+
successCallback: expect.any(Function),
159+
});
160+
expect(mockUseAnnotatorEventsResult.emitDeleteAnnotationStartEvent).toBeCalledWith(annotation.id);
161+
expect(mockUseAnnotatorEventsResult.emitDeleteAnnotationEndEvent).toBeCalledWith(annotation.id);
162+
});
94163
});
95164

96165
describe('useAnnotationAPI', () => {
@@ -143,48 +212,6 @@ describe('src/elements/content-sidebar/activity-feed/useAnnotationThread', () =>
143212
expect(mockOnAnnotationCreate).toBeCalledWith(createdAnnotation);
144213
});
145214

146-
test('should call handleAnnotationEdit with correct params', () => {
147-
const mockHandleEdit = jest.fn();
148-
useAnnotationAPI.mockImplementation(() => ({
149-
...mockUseAnnotationAPIResult,
150-
handleEdit: mockHandleEdit,
151-
}));
152-
153-
const { result } = getHook();
154-
act(() => {
155-
result.current.annotationActions.handleAnnotationEdit(annotation.id, 'new text', { can_edit: true });
156-
});
157-
158-
expect(mockHandleEdit).toBeCalledWith({
159-
id: annotation.id,
160-
permissions: { can_edit: true },
161-
text: 'new text',
162-
successCallback: expect.any(Function),
163-
});
164-
});
165-
166-
test('should call handleAnnotationDelete with correct params', () => {
167-
const mockHandleDelete = jest.fn();
168-
useAnnotationAPI.mockImplementation(() => ({
169-
...mockUseAnnotationAPIResult,
170-
handleDelete: mockHandleDelete,
171-
}));
172-
173-
const { result } = getHook();
174-
act(() => {
175-
result.current.annotationActions.handleAnnotationDelete({
176-
id: annotation.id,
177-
permissions: { can_delete: true },
178-
});
179-
});
180-
181-
expect(mockHandleDelete).toBeCalledWith({
182-
id: annotation.id,
183-
permissions: { can_delete: true },
184-
successCallback: expect.any(Function),
185-
});
186-
});
187-
188215
test('should call handleAnnotationStatusChange with correct params and set annotation state to pending', () => {
189216
const mockHandleStatusChange = jest.fn();
190217
useAnnotationAPI.mockImplementation(() => ({

src/elements/content-sidebar/activity-feed/annotation-thread/useAnnotationThread.js

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const useAnnotationThread = ({
8686
const [error, setError] = React.useState<AnnotationThreadError | typeof undefined>();
8787
const [isLoading, setIsLoading] = React.useState<boolean>(false);
8888

89-
const { id: fileId, permissions: filePermissions = {} } = file;
89+
const { file_version: { id: fileVersionId } = {}, id: fileId, permissions: filePermissions = {} } = file;
9090

9191
const setAnnotationPending = () => {
9292
if (annotation) {
@@ -100,7 +100,15 @@ const useAnnotationThread = ({
100100
}
101101
};
102102

103-
const { emitAddAnnotationEndEvent, emitAddAnnotationStartEvent } = useAnnotatorEvents({
103+
const {
104+
emitAddAnnotationEndEvent,
105+
emitAddAnnotationStartEvent,
106+
emitAnnotationActiveChangeEvent,
107+
emitDeleteAnnotationEndEvent,
108+
emitDeleteAnnotationStartEvent,
109+
emitUpdateAnnotationEndEvent,
110+
emitUpdateAnnotationStartEvent,
111+
} = useAnnotatorEvents({
104112
eventEmitter,
105113
onAnnotationDeleteStart: setAnnotationPending,
106114
onAnnotationUpdateEnd: handleAnnotationUpdateEnd,
@@ -129,13 +137,6 @@ const useAnnotationThread = ({
129137
setAnnotation(prevAnnotation => ({ ...prevAnnotation, ...updatedValues }));
130138
};
131139

132-
const handleFetchAnnotationSuccess = ({ replies: fetchedReplies, ...normalizedAnnotation }: Annotation) => {
133-
setAnnotation({ ...normalizedAnnotation });
134-
setReplies(normalizeReplies(fetchedReplies));
135-
setError(undefined);
136-
setIsLoading(false);
137-
};
138-
139140
const annotationErrorCallback = React.useCallback(
140141
(e: ElementsXhrError, code: string): void => {
141142
setIsLoading(false);
@@ -158,12 +159,21 @@ const useAnnotationThread = ({
158159
});
159160

160161
React.useEffect(() => {
161-
if (!annotationId || (annotation && annotation.id === annotationId)) {
162+
if (!annotationId || isLoading || (annotation && annotation.id === annotationId)) {
162163
return;
163164
}
164165
setIsLoading(true);
165-
handleFetch({ id: annotationId, successCallback: handleFetchAnnotationSuccess });
166-
}, [annotation, annotationId, handleFetch]);
166+
handleFetch({
167+
id: annotationId,
168+
successCallback: ({ replies: fetchedReplies, ...normalizedAnnotation }: Annotation) => {
169+
setAnnotation({ ...normalizedAnnotation });
170+
setReplies(normalizeReplies(fetchedReplies));
171+
setError(undefined);
172+
setIsLoading(false);
173+
emitAnnotationActiveChangeEvent(normalizedAnnotation.id, fileVersionId);
174+
},
175+
});
176+
}, [annotation, annotationId, emitAnnotationActiveChangeEvent, fileVersionId, handleFetch, isLoading]);
167177

168178
const { handleReplyCreate, handleReplyEdit, handleReplyDelete } = useRepliesAPI({
169179
annotationId,
@@ -198,10 +208,13 @@ const useAnnotationThread = ({
198208

199209
const handleAnnotationDelete = ({ id, permissions }: { id: string, permissions: AnnotationPermission }): void => {
200210
const annotationDeleteSuccessCallback = () => {
201-
// will emit event
211+
emitDeleteAnnotationEndEvent(id);
202212
};
203213

204214
setAnnotation(prevAnnotation => ({ ...prevAnnotation, isPending: true }));
215+
216+
emitDeleteAnnotationStartEvent(id);
217+
205218
handleDelete({
206219
id,
207220
permissions,
@@ -214,11 +227,18 @@ const useAnnotationThread = ({
214227
...updatedAnnotation,
215228
isPending: false,
216229
});
230+
231+
emitUpdateAnnotationEndEvent(updatedAnnotation);
217232
};
218233

219234
const handleAnnotationEdit = (id: string, text: string, permissions: AnnotationPermission): void => {
220235
setAnnotation(prevAnnotation => ({ ...prevAnnotation, isPending: true }));
221236

237+
emitUpdateAnnotationStartEvent({
238+
id,
239+
description: { message: text },
240+
});
241+
222242
handleEdit({
223243
id,
224244
text,

0 commit comments

Comments
 (0)