Skip to content

Commit 81b0071

Browse files
authored
fix(content-sharing): Add error handler (#4414)
* fix(content-sharing): Add error handler * fix: SharingModal Test * fix: improve code * fix: rollback change permission and access callback changes * fix: test and package * fix: update usm version * fix: upgrade sass version * fix: update snapshot due to blueprint upgrade * fix: state management and bump package
1 parent 5fd9164 commit 81b0071

File tree

9 files changed

+204
-121
lines changed

9 files changed

+204
-121
lines changed

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,14 @@
124124
"@babel/preset-typescript": "^7.24.7",
125125
"@babel/template": "^7.24.7",
126126
"@babel/types": "^7.24.7",
127-
"@box/blueprint-web": "^12.104.1",
128-
"@box/blueprint-web-assets": "4.88.2",
127+
"@box/blueprint-web": "^12.112.3",
128+
"@box/blueprint-web-assets": "4.91.3",
129129
"@box/box-ai-agent-selector": "^0.53.0",
130130
"@box/box-ai-content-answers": "^0.139.0",
131131
"@box/box-item-type-selector": "^0.73.1",
132132
"@box/cldr-data": "^34.2.0",
133133
"@box/combobox-with-api": "^1.18.0",
134-
"@box/copy-input": "^1.5.3",
134+
"@box/copy-input": "^1.22.3",
135135
"@box/frontend": "^11.0.1",
136136
"@box/item-icon": "^0.27.1",
137137
"@box/languages": "^1.0.0",
@@ -140,8 +140,8 @@
140140
"@box/metadata-view": "^1.10.0",
141141
"@box/react-virtualized": "^9.22.3-rc-box.10",
142142
"@box/types": "^0.2.1",
143-
"@box/unified-share-modal": "^1.7.1",
144-
"@box/user-selector": "^1.23.25",
143+
"@box/unified-share-modal": "^1.37.1",
144+
"@box/user-selector": "^1.55.3",
145145
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
146146
"@chromatic-com/storybook": "^4.0.1",
147147
"@commitlint/cli": "^19.8.0",
@@ -271,8 +271,8 @@
271271
"regenerator-runtime": "^0.14.1",
272272
"remarkable": "^2.0.1",
273273
"sanitize-html": "^2.14.0",
274-
"sass": "1.45.0",
275-
"sass-loader": "^8.0.2",
274+
"sass": "1.56.0",
275+
"sass-loader": "^16.0.6",
276276
"scroll-into-view-if-needed": "^2.2.20",
277277
"semantic-release": "^24.2.3",
278278
"sinon": "^2.3.7",
@@ -296,22 +296,22 @@
296296
"webpack-dev-server": "^5.2.1"
297297
},
298298
"peerDependencies": {
299-
"@box/blueprint-web": "^12.104.1",
300-
"@box/blueprint-web-assets": "4.88.2",
299+
"@box/blueprint-web": "^12.112.3",
300+
"@box/blueprint-web-assets": "4.91.3",
301301
"@box/box-ai-agent-selector": "^0.53.0",
302302
"@box/box-ai-content-answers": "^0.139.0",
303303
"@box/box-item-type-selector": "^0.73.1",
304304
"@box/cldr-data": ">=34.2.0",
305305
"@box/combobox-with-api": "^1.18.0",
306-
"@box/copy-input": "^1.5.3",
306+
"@box/copy-input": "^1.22.3",
307307
"@box/item-icon": "^0.27.1",
308308
"@box/metadata-editor": "^1.18.0",
309309
"@box/metadata-filter": "^1.41.3",
310310
"@box/metadata-view": "^1.10.0",
311311
"@box/react-virtualized": "^9.22.3-rc-box.10",
312312
"@box/types": "^0.2.1",
313-
"@box/unified-share-modal": "^1.7.1",
314-
"@box/user-selector": "^1.23.25",
313+
"@box/unified-share-modal": "^1.37.1",
314+
"@box/user-selector": "^1.55.3",
315315
"@hapi/address": "^2.1.4",
316316
"@tanstack/react-virtual": "^3.13.12",
317317
"axios": "^0.30.0",
@@ -352,7 +352,7 @@
352352
"regenerator-runtime": "^0.13.2",
353353
"remarkable": "^2.0.1",
354354
"sanitize-html": "^2.14.0",
355-
"sass": "1.45.0",
355+
"sass": "1.56.0",
356356
"scroll-into-view-if-needed": "^2.2.20",
357357
"tabbable": "^1.1.2",
358358
"uuid": "^8.3.2"

src/elements/content-sharing/SharingNotification.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ function SharingNotification({
234234
const sendInvitesFn = useInvites(api, itemID, itemType, {
235235
handleSuccess: response => {
236236
createNotification(TYPE_INFO, contentSharingMessages.sendInvitesSuccess);
237-
setIsLoading(false);
238237
setCollaboratorsList((prevList: collaboratorsListType | null) => {
239238
const newList = prevList ? { ...prevList } : { collaborators: [] };
240239
const newCollab = convertCollab({
@@ -251,7 +250,6 @@ function SharingNotification({
251250
},
252251
handleError: () => {
253252
createNotification(TYPE_ERROR, contentSharingMessages.sendInvitesError);
254-
setIsLoading(false);
255253
closeComponent();
256254
},
257255
setIsLoading,

src/elements/content-sharing/__tests__/SharingModal.test.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,8 +1021,13 @@ describe('elements/content-sharing/SharingModal', () => {
10211021
wrapper.update();
10221022

10231023
await act(async () => {
1024-
wrapper.find(UnifiedShareModal).invoke(`${usmFn}`)();
1024+
try {
1025+
await wrapper.find(UnifiedShareModal).invoke(`${usmFn}`)();
1026+
} catch (error) {
1027+
expect(error).toEqual(new Error({ status: '400' }));
1028+
}
10251029
});
1030+
10261031
wrapper.update();
10271032
expect(setIsVisibleMock).toHaveBeenCalledWith(false);
10281033
const notification = wrapper.find(Notification);

src/elements/content-sharing/__tests__/sharingService.test.ts

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,18 @@ describe('elements/content-sharing/sharingService', () => {
9797
});
9898

9999
test('should call share with correct parameters when createSharedLink is called', async () => {
100+
mockItemApiInstance.share.mockImplementation((_options, _access, successCallback) => {
101+
successCallback({ id: '123', shared_link: null });
102+
});
103+
100104
const service = createSharingServiceWrapper();
101105
await service.createSharedLink();
102106

103107
expect(mockItemApiInstance.share).toHaveBeenCalledWith(
104108
options,
105109
undefined,
106-
mockOnUpdateSharedLink,
107-
{},
110+
expect.any(Function),
111+
expect.any(Function),
108112
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
109113
);
110114
});
@@ -118,14 +122,18 @@ describe('elements/content-sharing/sharingService', () => {
118122
});
119123

120124
test('should call share with ACCESS_NONE and onRemoveSharedLink when deleteSharedLink is called', async () => {
125+
mockItemApiInstance.share.mockImplementation((_options, _access, successCallback) => {
126+
successCallback({ id: '123', shared_link: null });
127+
});
128+
121129
const service = createSharingServiceWrapper();
122130
await service.deleteSharedLink();
123131

124132
expect(mockItemApiInstance.share).toHaveBeenCalledWith(
125133
options,
126134
ACCESS_NONE,
127-
mockOnRemoveSharedLink,
128-
{},
135+
expect.any(Function),
136+
expect.any(Function),
129137
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
130138
);
131139
});
@@ -140,6 +148,10 @@ describe('elements/content-sharing/sharingService', () => {
140148
});
141149

142150
test('should call updateSharedLink with basic shared link settings', async () => {
151+
mockItemApiInstance.updateSharedLink.mockImplementation((_options, _access, successCallback) => {
152+
successCallback({ id: '123', shared_link: null });
153+
});
154+
143155
const service = createSharingServiceWrapper();
144156

145157
const sharedLinkSettings = {
@@ -167,13 +179,17 @@ describe('elements/content-sharing/sharingService', () => {
167179
expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith(
168180
options,
169181
expectedConvertedSettings,
170-
mockOnUpdateSharedLink,
171-
{},
182+
expect.any(Function),
183+
expect.any(Function),
172184
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
173185
);
174186
});
175187

176188
test('should call updateSharedLink with options including access, isDownloadAvailable, and serverUrl', async () => {
189+
mockItemApiInstance.updateSharedLink.mockImplementation((_options, _access, successCallback) => {
190+
successCallback({ id: '123', shared_link: null });
191+
});
192+
177193
const mockConvertedSharedLinkSettings = {
178194
password: 'test-password',
179195
permissions: { can_download: false, can_preview: true },
@@ -215,13 +231,17 @@ describe('elements/content-sharing/sharingService', () => {
215231
expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith(
216232
options,
217233
mockConvertedSharedLinkSettings,
218-
mockOnUpdateSharedLink,
219-
{},
234+
expect.any(Function),
235+
expect.any(Function),
220236
CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS,
221237
);
222238
});
223239

224240
test('should handle shared link settings correctly', async () => {
241+
mockItemApiInstance.updateSharedLink.mockImplementation((_options, _access, successCallback) => {
242+
successCallback({ id: '123', shared_link: null });
243+
});
244+
225245
const service = createSharingServiceWrapper();
226246

227247
const expirationDate = new Date('2024-12-31T23:59:59Z');
@@ -239,4 +259,47 @@ describe('elements/content-sharing/sharingService', () => {
239259
expect(convertSharedLinkSettings).toHaveBeenCalledWith(sharedLinkSettings, undefined, undefined, undefined);
240260
});
241261
});
262+
263+
describe('error handling', () => {
264+
test('should reject when createSharedLink fails', async () => {
265+
const mockError = new Error('Failed to create shared link');
266+
mockItemApiInstance.share.mockImplementation((_options, _access, _successCallback, errorCallback) => {
267+
errorCallback(mockError);
268+
});
269+
270+
const service = createSharingServiceWrapper();
271+
await expect(service.createSharedLink()).rejects.toEqual(mockError);
272+
});
273+
274+
test('should reject when deleteSharedLink fails', async () => {
275+
const mockError = new Error('Failed to delete shared link');
276+
mockItemApiInstance.share.mockImplementation((_options, _access, _successCallback, errorCallback) => {
277+
errorCallback(mockError);
278+
});
279+
280+
const service = createSharingServiceWrapper();
281+
await expect(service.deleteSharedLink()).rejects.toEqual(mockError);
282+
});
283+
284+
test('should reject when updateSharedLink fails', async () => {
285+
const mockError = new Error('Failed to update shared link');
286+
mockItemApiInstance.updateSharedLink.mockImplementation(
287+
(_options, _settings, _successCallback, errorCallback) => {
288+
errorCallback(mockError);
289+
},
290+
);
291+
292+
const service = createSharingServiceWrapper();
293+
const sharedLinkSettings = {
294+
expiration: null,
295+
isDownloadEnabled: true,
296+
isExpirationEnabled: false,
297+
isPasswordEnabled: false,
298+
password: '',
299+
vanityName: 'vanity-name',
300+
};
301+
302+
await expect(service.updateSharedLink(sharedLinkSettings)).rejects.toEqual(mockError);
303+
});
304+
});
242305
});

src/elements/content-sharing/__tests__/useInvites.test.js

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { renderHook, act } from '@testing-library/react';
1+
import { renderHook } from '@testing-library/react';
22
import useInvites from '../hooks/useInvites';
33
import API from '../../../api';
44

@@ -51,9 +51,7 @@ describe('useInvites hook', () => {
5151
}),
5252
);
5353

54-
act(() => {
55-
result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
56-
});
54+
await result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
5755

5856
expect(mockHandleSuccess).toHaveBeenCalledWith({ id: 'collab123', role: 'editor' });
5957
expect(mockTransformResponse).toHaveBeenCalledWith({ id: 'collab123', role: 'editor' });
@@ -69,10 +67,7 @@ describe('useInvites hook', () => {
6967
}),
7068
);
7169

72-
act(() => {
73-
result.current({ users: [{ email: 'fail@example.com', role: 'editor' }] });
74-
});
75-
70+
await result.current({ users: [{ email: 'fail@example.com', role: 'editor' }] }).catch(() => {});
7671
expect(mockHandleError).toHaveBeenCalled();
7772
});
7873

@@ -85,12 +80,8 @@ describe('useInvites hook', () => {
8580
}),
8681
);
8782

88-
let actionResult;
89-
act(() => {
90-
actionResult = result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
91-
});
92-
93-
expect(actionResult).toEqual(Promise.resolve());
83+
const actionResult = await result.current({ users: [{ email: 'user@example.com', role: 'editor' }] });
84+
expect(actionResult).toEqual(null);
9485
expect(mockHandleSuccess).not.toHaveBeenCalled();
9586
expect(mockHandleError).not.toHaveBeenCalled();
9687
});
@@ -108,11 +99,9 @@ describe('useInvites hook', () => {
10899
}),
109100
);
110101

111-
act(() => {
112-
result.current({
113-
users: [{ email: 'user@example.com', role: 'editor' }],
114-
groups: [{ id: 'group123', role: 'viewer' }],
115-
});
102+
await result.current({
103+
users: [{ email: 'user@example.com', role: 'editor' }],
104+
groups: [{ id: 'group123', role: 'viewer' }],
116105
});
117106

118107
expect(mockHandleSuccess).toHaveBeenCalledTimes(2);

src/elements/content-sharing/hooks/useInvites.js

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,38 @@ function useInvites(api: API, itemID: string, itemType: ItemType, options: UseIn
3434
id: itemID,
3535
type: itemType,
3636
};
37+
3738
const sendCollabRequest = collab => {
38-
setIsLoading(true);
39-
return api.getCollaborationsAPI(false).addCollaboration(
40-
itemData,
41-
collab,
42-
response => {
43-
handleSuccess(response);
44-
return transformResponse(response);
45-
},
46-
handleError,
47-
);
39+
return new Promise((resolve, reject) => {
40+
api.getCollaborationsAPI(false).addCollaboration(
41+
itemData,
42+
collab,
43+
response => {
44+
handleSuccess(response);
45+
resolve(transformResponse(response));
46+
},
47+
error => {
48+
handleError(error);
49+
reject(error);
50+
},
51+
);
52+
});
4853
};
4954

5055
const createPostCollaborationFn: SendInvitesFnType =
5156
() => async (collabRequest: InviteCollaboratorsRequest) => {
5257
if (!transformRequest) return Promise.resolve(null);
5358

5459
const { users, groups } = transformRequest(collabRequest);
55-
return Promise.all([
56-
...users.map(user => sendCollabRequest(user)),
57-
...groups.map(group => sendCollabRequest(group)),
58-
]);
60+
setIsLoading(true);
61+
try {
62+
return await Promise.all([
63+
...users.map(user => sendCollabRequest(user)),
64+
...groups.map(group => sendCollabRequest(group)),
65+
]);
66+
} finally {
67+
setIsLoading(false);
68+
}
5969
};
6070

6171
if (!sendInvites) {

0 commit comments

Comments
 (0)