Skip to content

Commit 0168f1a

Browse files
feat(annotation-thread): annotation thread related hooks refactoring (#3192)
* feat(annotation-thread): annotation thread related hooks refactoring * feat(annotation-thread): use of event emitter and hooks refactoring
1 parent 941e990 commit 0168f1a

File tree

11 files changed

+260
-229
lines changed

11 files changed

+260
-229
lines changed

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

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,27 @@ const AnnotationThread = ({
7070
token,
7171
});
7272

73-
const { id: fileId, permissions = {} } = file;
74-
7573
const {
7674
annotation,
7775
replies,
7876
isLoading,
7977
error,
80-
annotationActions: { handleAnnotationDelete, handleAnnotationEdit, handleAnnotationStatusChange },
81-
annotationEvents: { handleAnnotationCreateEnd: handleAnnotationCreateEndEvent, handleAnnotationCreateStart },
78+
annotationActions: {
79+
handleAnnotationCreate,
80+
handleAnnotationDelete,
81+
handleAnnotationEdit,
82+
handleAnnotationStatusChange,
83+
},
8284
repliesActions: { handleReplyEdit, handleReplyCreate, handleReplyDelete },
8385
} = useAnnotationThread({
8486
api,
8587
annotationId,
8688
currentUser,
87-
fileId,
88-
filePermissions: permissions,
8989
errorCallback: onError,
9090
eventEmitter,
91+
file,
92+
onAnnotationCreate,
93+
target,
9194
});
9295

9396
const getAvatarUrl = async (userId: string): Promise<?string> =>
@@ -106,27 +109,18 @@ const AnnotationThread = ({
106109
);
107110
}, DEFAULT_COLLAB_DEBOUNCE);
108111

109-
const handleAnnotationCreateEnd = (newAnnotation: Object, requestId: string) => {
110-
handleAnnotationCreateEndEvent(newAnnotation, requestId);
111-
onAnnotationCreate(newAnnotation);
112-
};
113-
114112
return (
115113
<div className={classNames('AnnotationThread', className)} data-testid="annotation-thread">
116114
<IntlProvider locale={language} messages={messages}>
117115
{!annotationId ? (
118116
<AnnotationThreadCreate
119-
api={api}
120117
currentUser={currentUser}
121-
file={file}
122118
getAvatarUrl={getAvatarUrl}
123119
getMentionWithQuery={getMentionWithQuery}
124-
handleCancel={handleCancel}
120+
isPending={isLoading}
125121
mentionSelectorContacts={mentionSelectorContacts}
126-
onAnnotationCreateEnd={handleAnnotationCreateEnd}
127-
onAnnotationCreateStart={handleAnnotationCreateStart}
128-
onError={onError}
129-
target={target}
122+
onFormCancel={handleCancel}
123+
onFormSubmit={handleAnnotationCreate}
130124
/>
131125
) : (
132126
<AnnotationThreadContent

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// @flow
22
import React from 'react';
3-
import type { MessageDescriptor } from 'react-intl';
43
import ActivityError from '../common/activity-error';
54
import ActivityThread from '../activity-feed/ActivityThread';
65
import AnnotationActivity from '../annotations';
@@ -15,16 +14,14 @@ import type {
1514
} from '../../../../common/types/feed';
1615
import type { SelectorItems, User } from '../../../../common/types/core';
1716
import type { GetProfileUrlCallback } from '../../../common/flowTypes';
17+
import type { AnnotationThreadError } from './types';
1818

1919
import './AnnotationThreadContent.scss';
2020

2121
type Props = {
2222
annotation?: Annotation,
2323
currentUser: User,
24-
error?: {
25-
message: MessageDescriptor,
26-
title: MessageDescriptor,
27-
},
24+
error?: AnnotationThreadError,
2825
getAvatarUrl: string => Promise<?string>,
2926
getMentionWithQuery: (searchStr: string) => void,
3027
getUserProfileUrl?: GetProfileUrlCallback,

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

Lines changed: 16 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,48 @@
11
// @flow
22
import React from 'react';
33
import classNames from 'classnames';
4-
import uniqueId from 'lodash/uniqueId';
5-
import API from '../../../../api/APIFactory';
64
import CommentForm from '../comment-form';
7-
import { getBadUserError } from '../../../../utils/error';
8-
9-
import type { Annotation, Target } from '../../../../common/types/annotations';
10-
import type { BoxItem, SelectorItems, User } from '../../../../common/types/core';
11-
import type { ErrorContextProps } from '../../../../common/types/api';
5+
import type { SelectorItems, User } from '../../../../common/types/core';
126

137
import './AnnotationThreadCreate.scss';
148

159
type Props = {
16-
api: API,
1710
currentUser: User,
18-
file: BoxItem,
1911
getAvatarUrl: string => Promise<?string>,
2012
getMentionWithQuery: (searchStr: string) => void,
21-
handleCancel: () => void,
13+
isPending: boolean,
2214
mentionSelectorContacts: SelectorItems<>,
23-
onAnnotationCreateEnd: (annotation: Object, requestId: string) => void,
24-
onAnnotationCreateStart: (annotation: Object, requestId: string) => void,
25-
target: Target,
26-
} & ErrorContextProps;
15+
onFormCancel: () => void,
16+
onFormSubmit: (text: string) => void,
17+
};
2718

2819
const AnnotationThreadCreate = ({
29-
api,
3020
currentUser,
31-
file,
3221
getAvatarUrl,
3322
getMentionWithQuery,
34-
handleCancel,
23+
isPending,
3524
mentionSelectorContacts,
36-
onAnnotationCreateEnd,
37-
onAnnotationCreateStart,
38-
onError,
39-
target,
25+
onFormCancel,
26+
onFormSubmit,
4027
}: Props) => {
41-
const [isPending, setIsPending] = React.useState(false);
42-
43-
const handleCreate = (text: string): void => {
44-
const { id, permissions = {}, file_version = {} } = file;
45-
setIsPending(true);
46-
if (!currentUser) {
47-
throw getBadUserError();
48-
}
49-
50-
const requestId = uniqueId('annotation_');
51-
52-
const successCallback = (annotation: Annotation) => {
53-
onAnnotationCreateEnd(annotation, requestId);
54-
};
55-
56-
const payload = {
57-
description: { message: text },
58-
target,
59-
};
60-
61-
onAnnotationCreateStart(payload, requestId);
62-
63-
api.getAnnotationsAPI(false).createAnnotation(
64-
id,
65-
file_version.id,
66-
payload,
67-
permissions,
68-
successCallback,
69-
onError,
70-
);
28+
const handleSubmit = ({ text }) => {
29+
onFormSubmit(text);
7130
};
7231

7332
return (
7433
<div
75-
data-testid="annotation-create"
7634
className={classNames('AnnotationThreadCreate', { 'is-pending': isPending })}
35+
data-testid="annotation-create"
7736
>
7837
<CommentForm
7938
className="AnnotationThreadCreate-editor"
80-
user={currentUser}
81-
entityId={file.id}
82-
onSubmit={handleCreate}
83-
getMentionWithQuery={getMentionWithQuery}
84-
mentionSelectorContacts={mentionSelectorContacts}
39+
createComment={handleSubmit}
8540
getAvatarUrl={getAvatarUrl}
41+
getMentionWithQuery={getMentionWithQuery}
8642
isOpen
87-
onCancel={handleCancel}
43+
mentionSelectorContacts={mentionSelectorContacts}
44+
onCancel={onFormCancel}
45+
user={currentUser}
8846
/>
8947
</div>
9048
);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { annotation as mockAnnotation } from '../../../../../__mocks__/annotations';
2+
3+
export default () => ({
4+
annotation: mockAnnotation,
5+
annotationActions: {
6+
handleDelete: jest.fn(),
7+
handleEdit: jest.fn(),
8+
handleResolve: jest.fn(),
9+
},
10+
annotationEvents: {
11+
handleAnnotationCreateStart: jest.fn(),
12+
handleAnnotationCreateEnd: jest.fn(),
13+
},
14+
error: {},
15+
isLoading: false,
16+
replies: [],
17+
repliesActions: {
18+
handleCreateReply: jest.fn(),
19+
handleEditReply: jest.fn(),
20+
handleDeleteReply: jest.fn(),
21+
},
22+
});

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

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,17 @@ import { render } from '@testing-library/react';
33
import React from 'react';
44
import { IntlProvider } from 'react-intl';
55
import AnnotationThread from '../AnnotationThread';
6-
import { annotation as mockAnnotation } from '../../../../../__mocks__/annotations';
76

87
jest.mock('react-intl', () => jest.requireActual('react-intl'));
98
jest.mock('../AnnotationThreadContent', () => () => <div data-testid="annotation-content" />);
109
jest.mock('../AnnotationThreadCreate', () => () => <div data-testid="annotation-create" />);
1110

12-
jest.mock('../useAnnotationThread', () => {
13-
return jest.fn(() => ({
14-
annotation: mockAnnotation,
15-
annotationActions: {
16-
handleDelete: jest.fn(),
17-
handleEdit: jest.fn(),
18-
handleResolve: jest.fn(),
19-
},
20-
annotationEvents: {
21-
handleAnnotationCreateStart: jest.fn(),
22-
handleAnnotationCreateEnd: jest.fn(),
23-
},
24-
error: {},
25-
isLoading: false,
26-
replies: [],
27-
repliesActions: {
28-
handleCreateReply: jest.fn(),
29-
handleEditReply: jest.fn(),
30-
handleDeleteReply: jest.fn(),
31-
},
32-
}));
33-
});
11+
jest.mock('../useAnnotationThread');
3412

3513
describe('elements/content-sidebar/activity-feed/annotation-thread/AnnotationThread', () => {
3614
const file = {
3715
id: 'fileId',
16+
file_version: { id: '123' },
3817
permissions: {
3918
can_view_annotations: true,
4019
can_annotate: true,

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

Lines changed: 17 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import React from 'react';
33
import { render, fireEvent } from '@testing-library/react';
44
import { IntlProvider } from 'react-intl';
55
import AnnotationThreadCreate from '../AnnotationThreadCreate';
6-
import { annotation as mockAnnotation } from '../../../../../__mocks__/annotations';
76

8-
jest.mock('lodash/uniqueId', () => () => 'uniqueId');
97
jest.mock('react-intl', () => jest.requireActual('react-intl'));
108
jest.mock('../../comment-form', () => props => {
119
return (
@@ -15,7 +13,7 @@ jest.mock('../../comment-form', () => props => {
1513
<button type="button" onClick={props.onCancel}>
1614
Cancel
1715
</button>
18-
<button type="button" onClick={() => props.onSubmit('example message')}>
16+
<button type="button" onClick={() => props.createComment({ text: 'example message' })}>
1917
Post
2018
</button>
2119
</div>
@@ -25,33 +23,17 @@ jest.mock('../../comment-form', () => props => {
2523
});
2624

2725
describe('elements/content-sidebar/activity-feed/annotation-thread/AnnotationThreadCreate', () => {
28-
const getApiProp = annotationsAPIProps => ({
29-
getAnnotationsAPI: () => ({
30-
createAnnotation: jest.fn(),
31-
...annotationsAPIProps,
32-
}),
33-
});
34-
3526
const getDefaultProps = () => ({
3627
currentUser: { id: 'user_id' },
37-
file: {
38-
id: 'file_id',
39-
file_version: { id: 'file_version' },
40-
permissions: { can_annotate: true },
41-
},
42-
target: {
43-
location: { type: 'page', value: 1 },
44-
type: 'point',
45-
x: 12,
46-
y: 10,
47-
},
28+
onFormCancel: jest.fn(),
29+
onFormSubmit: jest.fn(),
4830
});
4931

5032
const IntlWrapper = ({ children }: { children?: React.ReactNode }) => {
5133
return <IntlProvider locale="en">{children}</IntlProvider>;
5234
};
53-
const getWrapper = (props, annotationsAPIProps = {}) =>
54-
render(<AnnotationThreadCreate api={getApiProp(annotationsAPIProps)} {...getDefaultProps()} {...props} />, {
35+
const getWrapper = props =>
36+
render(<AnnotationThreadCreate {...getDefaultProps()} {...props} />, {
5537
wrapper: IntlWrapper,
5638
});
5739

@@ -62,51 +44,29 @@ describe('elements/content-sidebar/activity-feed/annotation-thread/AnnotationThr
6244
expect(container.getElementsByClassName('AnnotationThreadCreate-is-pending')).toHaveLength(0);
6345
});
6446

47+
test('Should render correctly with pending state', () => {
48+
const { getByTestId } = getWrapper({ isPending: true });
49+
50+
expect(getByTestId('annotation-create')).toHaveClass('is-pending');
51+
});
52+
6553
test('Should handle create', () => {
66-
const createAnnotation = jest.fn((fileId, fileVersionId, payload, filePermissions, successCallback) => {
67-
successCallback(mockAnnotation);
68-
});
69-
const onAnnotationCreateStart = jest.fn();
70-
const onAnnotationCreateEnd = jest.fn();
71-
const onError = jest.fn();
72-
const target = { x: 1, y: 1 };
54+
const onFormSubmit = jest.fn();
7355

74-
const { getByText, getByTestId } = getWrapper(
75-
{ onAnnotationCreateEnd, onAnnotationCreateStart, onError, target },
76-
{ createAnnotation },
77-
);
56+
const { getByText } = getWrapper({ onFormSubmit });
7857

7958
fireEvent.click(getByText('Post'));
8059

81-
expect(onAnnotationCreateStart).toBeCalledWith(
82-
{
83-
description: { message: 'example message' },
84-
target,
85-
},
86-
'uniqueId',
87-
);
88-
expect(createAnnotation).toBeCalledWith(
89-
'file_id',
90-
'file_version',
91-
{
92-
description: { message: 'example message' },
93-
target,
94-
},
95-
{ can_annotate: true },
96-
expect.any(Function),
97-
onError,
98-
);
99-
expect(onAnnotationCreateEnd).toBeCalledWith(mockAnnotation, 'uniqueId');
100-
expect(getByTestId('annotation-create')).toHaveClass('is-pending');
60+
expect(onFormSubmit).toBeCalledWith('example message');
10161
});
10262

10363
test('Should call handleCancel on cancel', () => {
104-
const handleCancel = jest.fn();
64+
const onFormCancel = jest.fn();
10565

106-
const { getByText } = getWrapper({ handleCancel });
66+
const { getByText } = getWrapper({ onFormCancel });
10767

10868
fireEvent.click(getByText('Cancel'));
10969

110-
expect(handleCancel).toBeCalled();
70+
expect(onFormCancel).toBeCalled();
11171
});
11272
});

0 commit comments

Comments
 (0)