Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/elements/content-sharing/ContentSharing.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import 'regenerator-runtime/runtime';
import * as React from 'react';

import type { Configuration, VariantType } from '@box/unified-share-modal';
import type { Configuration, SharingService, VariantType } from '@box/unified-share-modal';
import API from '../../api';

import { isFeatureEnabled } from '../common/feature-checking';
Expand Down Expand Up @@ -67,6 +67,8 @@ type ContentSharingProps = {
onClose?: () => void,
/** onError - Callback when item data fails to load, preventing USM from opening */
onError?: (error: ElementsXhrError) => void,
/** onSendSharedLink - Callback to email the shared link to the selected contacts */
onSendSharedLink?: $PropertyType<SharingService, 'sendSharedLink'>,
/** token - Valid access token */
token: string,
/** uuid - Unique identifier, used for refreshing element visibility when called from the ES6 wrapper */
Expand Down Expand Up @@ -98,6 +100,7 @@ function ContentSharing({
messages,
onClose,
onError,
onSendSharedLink,
token,
uuid,
variant,
Expand Down Expand Up @@ -142,6 +145,7 @@ function ContentSharing({
itemType={itemType}
onClose={onClose}
onError={onError}
onSendSharedLink={onSendSharedLink}
variant={variant}
>
{children}
Expand Down
21 changes: 16 additions & 5 deletions src/elements/content-sharing/ContentSharingV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
Configuration,
Item,
SharedLink,
SharingService,
User,
VariantType,
} from '@box/unified-share-modal';
Expand Down Expand Up @@ -42,6 +43,8 @@ export interface ContentSharingV2Props {
onClose?: () => void;
/** onError - Callback when item data fails to load, preventing USM from opening */
onError?: (error: ElementsXhrError) => void;
/** onSendSharedLink - Callback to email the shared link to the selected contacts */
onSendSharedLink?: SharingService['sendSharedLink'];
/** variant - "desktop" or "modal" variant of the Unified Share Modal */
variant?: VariantType;
}
Expand All @@ -54,8 +57,12 @@ function ContentSharingV2({
itemType,
onClose,
onError,
onSendSharedLink,
variant,
}: ContentSharingV2Props) {
const { formatMessage } = useIntl();
const { addNotification } = useNotification();

const [avatarUrlMap, setAvatarUrlMap] = React.useState<AvatarURLMap | null>(null);
const [item, setItem] = React.useState<Item | null>(null);
const [hasError, setHasError] = React.useState<boolean>(false);
Expand All @@ -67,16 +74,22 @@ function ContentSharingV2({
const [collaboratorsData, setCollaboratorsData] = React.useState<Collaborations | null>(null);
const [owner, setOwner] = React.useState({ email: '', id: '', name: '' });

const { formatMessage } = useIntl();
const { addNotification } = useNotification();
const { sharingService } = useSharingService({
const config = React.useMemo(() => {
return {
sharedLinkEmail: !!onSendSharedLink,
...usmConfig,
};
}, [onSendSharedLink, usmConfig]);

const sharingService = useSharingService({
api,
avatarUrlMap,
collaborators,
currentUserId: currentUser?.id,
item,
itemId,
itemType,
onSendSharedLink,
sharedLink,
sharingServiceProps,
setCollaborators,
Expand Down Expand Up @@ -234,8 +247,6 @@ function ContentSharingV2({
}
}, [avatarUrlMap, collaboratorsData, currentUser, owner]);

const config = React.useMemo(() => ({ sharedLinkEmail: false, ...usmConfig }), [usmConfig]);

const handleOpenChange = (open: boolean) => {
if (!open) {
onClose?.();
Expand Down
22 changes: 22 additions & 0 deletions src/elements/content-sharing/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,25 @@ export const PEOPLE_IN_ITEM = 'peopleInThisItem';

export const COLLAB_USER_TYPE = 'user';
export const COLLAB_GROUP_TYPE = 'group';

export const API_TO_USM_COLLAB_ROLE_MAP = {
'co-owner': 'co_owner',
editor: 'editor',
owner: 'owner',
previewer: 'previewer',
'previewer uploader': 'previewer_uploader',
uploader: 'uploader',
viewer: 'viewer',
'viewer uploader': 'viewer_uploader',
};

export const USM_TO_API_COLLAB_ROLE_MAP = {
co_owner: 'co-owner',
editor: 'editor',
owner: 'owner',
previewer: 'previewer',
previewer_uploader: 'previewer uploader',
uploader: 'uploader',
viewer: 'viewer',
viewer_uploader: 'viewer uploader',
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { renderHook } from '@testing-library/react';

import type { CollaborationFormData, SharingResponse } from '@box/unified-share-modal';

import { TYPE_FILE, TYPE_FOLDER } from '../../../../constants';
import { createSharingService } from '../../sharingService';
import { convertCollab, convertCollabsRequest, convertItemResponse } from '../../utils';
Expand Down Expand Up @@ -57,6 +59,7 @@ const mockConvertedData = {
sharedLink: mockSharedLink,
};

const mockOnSendSharedLink = jest.fn();
const mockSetItem = jest.fn();
const mockSetSharedLink = jest.fn();
const mockSetCollaborators = jest.fn();
Expand All @@ -71,6 +74,7 @@ const renderHookWithProps = (props = {}) => {
item: mockItem,
itemId: mockItemId,
itemType: TYPE_FILE,
onSendSharedLink: mockOnSendSharedLink,
sharedLink: mockSharedLink,
sharingServiceProps: mockSharingServiceProps,
setCollaborators: mockSetCollaborators,
Expand Down Expand Up @@ -100,7 +104,7 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
test('should return null itemApiInstance and sharingService when item is null', () => {
const { result } = renderHookWithProps({ item: null });

expect(result.current.sharingService).toEqual({ sendInvitations: expect.any(Function) });
expect(result.current).toEqual({ sendInvitations: expect.any(Function), sendSharedLink: mockOnSendSharedLink });
expect(mockApi.getFileAPI).not.toHaveBeenCalled();
expect(mockApi.getFolderAPI).not.toHaveBeenCalled();
expect(createSharingService).not.toHaveBeenCalled();
Expand All @@ -110,9 +114,10 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
mockApi.getFileAPI.mockReturnValue({});
const { result } = renderHookWithProps({ sharedLink: null });

expect(result.current.sharingService).toEqual({
expect(result.current).toEqual({
...mockSharingService,
sendInvitations: expect.any(Function),
sendSharedLink: mockOnSendSharedLink,
});
expect(mockApi.getFileAPI).toHaveBeenCalled();
expect(createSharingService).toHaveBeenCalledWith(
Expand All @@ -129,7 +134,7 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
test('should return null itemApiInstance and sharingService when itemType is neither TYPE_FILE nor TYPE_FOLDER', () => {
const { result } = renderHookWithProps({ itemType: 'hubs' });

expect(result.current.sharingService).toEqual({ sendInvitations: expect.any(Function) });
expect(result.current).toEqual({ sendInvitations: expect.any(Function), sendSharedLink: mockOnSendSharedLink });
expect(mockApi.getFileAPI).not.toHaveBeenCalled();
expect(mockApi.getFolderAPI).not.toHaveBeenCalled();
expect(createSharingService).not.toHaveBeenCalled();
Expand Down Expand Up @@ -161,9 +166,10 @@ describe('elements/content-sharing/hooks/useSharingService', () => {

expect(mockApi.getFileAPI).toHaveBeenCalled();
expect(mockApi.getFolderAPI).not.toHaveBeenCalled();
expect(result.current.sharingService).toEqual({
expect(result.current).toEqual({
...mockSharingService,
sendInvitations: expect.any(Function),
sendSharedLink: mockOnSendSharedLink,
});
expect(createSharingService).toHaveBeenCalledWith({
hasSharedLink: false,
Expand Down Expand Up @@ -218,9 +224,10 @@ describe('elements/content-sharing/hooks/useSharingService', () => {

expect(mockApi.getFolderAPI).toHaveBeenCalled();
expect(mockApi.getFileAPI).not.toHaveBeenCalled();
expect(result.current.sharingService).toEqual({
expect(result.current).toEqual({
...mockSharingService,
sendInvitations: expect.any(Function),
sendSharedLink: mockOnSendSharedLink,
});
expect(createSharingService).toHaveBeenCalledWith({
hasSharedLink: false,
Expand Down Expand Up @@ -345,10 +352,10 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
mockSendInvitations.mockResolvedValue(mockResult);
const { result } = renderHookWithProps();

const sendInvitationsResult = await result.current.sharingService.sendInvitations({
const sendInvitationsResult = (await result.current.sendInvitations({
contacts: mockContacts,
role: 'editor',
});
} as unknown as CollaborationFormData)) as SharingResponse;

expect(mockFormatMessage).toHaveBeenCalledWith(
expect.objectContaining({ id: 'be.contentSharing.sendInvitationsSuccess' }),
Expand All @@ -365,34 +372,35 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
mockSendInvitations.mockResolvedValue(mockResult);
const { result } = renderHookWithProps();

const sendInvitationsResult = await result.current.sharingService.sendInvitations({
const sendInvitationsResult = (await result.current.sendInvitations({
contacts: mockContacts,
role: 'editor',
});
} as unknown as CollaborationFormData)) as SharingResponse;

expect(mockFormatMessage).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ id: 'be.contentSharing.sendInvitationsError' }),
{ count: 1 }, // Counts of invitations not sent
expect.objectContaining({ id: 'be.contentSharing.sendInvitationsSuccess' }),
{ count: 2 }, // Counts of successfully invited collaborators
);
expect(mockFormatMessage).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ id: 'be.contentSharing.sendInvitationsSuccess' }),
{ count: 2 }, // Counts of successfully invited collaborators
expect.objectContaining({ id: 'be.contentSharing.sendInvitationsError' }),
{ count: 1 }, // Counts of invitations not sent
);
expect(sendInvitationsResult.messages[0].type).toEqual('error');
expect(sendInvitationsResult.messages[1].type).toEqual('success');

expect(sendInvitationsResult.messages[0].type).toEqual('success');
expect(sendInvitationsResult.messages[1].type).toEqual('error');
});

test('should return error notification when no contacts are successfully invited', async () => {
const mockResult = [];
mockSendInvitations.mockResolvedValue(mockResult);
const { result } = renderHookWithProps();

const sendInvitationsResult = await result.current.sharingService.sendInvitations({
const sendInvitationsResult = (await result.current.sendInvitations({
contacts: mockContacts,
role: 'editor',
});
} as unknown as CollaborationFormData)) as SharingResponse;

expect(mockFormatMessage).toHaveBeenCalledWith(
expect.objectContaining({ id: 'be.contentSharing.sendInvitationsError' }),
Expand All @@ -405,10 +413,10 @@ describe('elements/content-sharing/hooks/useSharingService', () => {
mockSendInvitations.mockResolvedValue(null);
const { result } = renderHookWithProps();

const sendInvitationsResult = await result.current.sharingService.sendInvitations({
const sendInvitationsResult = await result.current.sendInvitations({
contacts: mockContacts,
role: 'editor',
});
} as unknown as CollaborationFormData);

expect(sendInvitationsResult).toBeNull();
});
Expand Down
2 changes: 2 additions & 0 deletions src/elements/content-sharing/hooks/useInvites.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ function useInvites(api: API, itemID: string, itemType: ItemType, options: UseIn
if (!transformRequest) return Promise.resolve(null);

const { users, groups } = transformRequest(collabRequest);

setIsLoading(true);

try {
return await Promise.all([
...users.map(user => sendCollabRequest(user)),
Expand Down
Loading
Loading