From c8a13e3f9e5c400b2ad136453c2b217595ca7d17 Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Wed, 24 Sep 2025 16:16:33 -0700 Subject: [PATCH 1/6] feat: Convert Content Sharing V2 API Response --- package.json | 2 +- .../content-sharing/ContentSharing.js | 1 + .../content-sharing/ContentSharingV2.tsx | 123 ++++++++++++++-- .../__tests__/ContentSharingV2.test.tsx | 134 ++++++++++++++++++ .../stories/ContentSharingV2.stories.tsx | 9 ++ .../tests/ContentSharingV2-visual.stories.tsx | 10 ++ src/elements/content-sharing/types.js | 123 ++++++++++++++++ .../utils/__mocks__/ContentSharingV2Mocks.js | 97 +++++++++++++ .../__tests__/convertItemResponse.test.ts | 119 ++++++++++++++++ .../__tests__/getAllowedAccessLevels.test.ts | 24 ++++ .../getAllowedPermissionLevels.test.ts | 28 ++++ .../content-sharing/utils/constants.ts | 31 ++++ .../utils/convertItemResponse.ts | 99 +++++++++++++ .../utils/getAllowedAccessLevels.ts | 12 ++ .../utils/getAllowedPermissionLevels.ts | 21 +++ src/elements/content-sharing/utils/index.ts | 3 + yarn.lock | 8 +- 17 files changed, 831 insertions(+), 13 deletions(-) create mode 100644 src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx create mode 100644 src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js create mode 100644 src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts create mode 100644 src/elements/content-sharing/utils/__tests__/getAllowedAccessLevels.test.ts create mode 100644 src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts create mode 100644 src/elements/content-sharing/utils/constants.ts create mode 100644 src/elements/content-sharing/utils/convertItemResponse.ts create mode 100644 src/elements/content-sharing/utils/getAllowedAccessLevels.ts create mode 100644 src/elements/content-sharing/utils/getAllowedPermissionLevels.ts create mode 100644 src/elements/content-sharing/utils/index.ts diff --git a/package.json b/package.json index 789bbdb50b..ede53a187c 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "@box/metadata-view": "^0.54.0", "@box/react-virtualized": "^9.22.3-rc-box.10", "@box/types": "^0.2.1", - "@box/unified-share-modal": "^0.48.8", + "@box/unified-share-modal": "^0.50.0", "@box/user-selector": "^1.23.25", "@cfaester/enzyme-adapter-react-18": "^0.8.0", "@chromatic-com/storybook": "^4.0.1", diff --git a/src/elements/content-sharing/ContentSharing.js b/src/elements/content-sharing/ContentSharing.js index 50663c283b..fdef1cc69f 100644 --- a/src/elements/content-sharing/ContentSharing.js +++ b/src/elements/content-sharing/ContentSharing.js @@ -117,6 +117,7 @@ function ContentSharing({ if (isFeatureEnabled(features, 'contentSharingV2')) { return ( (null); + const [sharedLink, setSharedLink] = React.useState(null); + const [currentUser, setCurrentUser] = React.useState(null); + const [collaborationRoles, setCollaborationRoles] = React.useState(null); + + // Handle successful GET requests to /files or /folders + const handleGetItemSuccess = React.useCallback(itemData => { + const { + collaborationRoles: collaborationRolesFromAPI, + item: itemFromAPI, + sharedLink: sharedLinkFromAPI, + } = convertItemResponse(itemData); + setItem(itemFromAPI); + setSharedLink(sharedLinkFromAPI); + setCollaborationRoles(collaborationRolesFromAPI); + }, []); + + // Reset state if the API has changed + React.useEffect(() => { + setItem(null); + setSharedLink(null); + setCurrentUser(null); + setCollaborationRoles(null); + }, [api]); + + // Get initial data for the item + React.useEffect(() => { + const getItem = () => { + if (itemType === TYPE_FILE) { + api.getFileAPI().getFile( + itemID, + handleGetItemSuccess, + {}, + { + fields: CONTENT_SHARING_ITEM_FIELDS, + }, + ); + } else if (itemType === TYPE_FOLDER) { + api.getFolderAPI().getFolderFields( + itemID, + handleGetItemSuccess, + {}, + { + fields: CONTENT_SHARING_ITEM_FIELDS, + }, + ); + } + }; + + if (api && !isEmpty(api) && !item && !sharedLink) { + getItem(); + } + }, [api, item, itemID, itemType, sharedLink, handleGetItemSuccess]); + + // Get initial data for the user + React.useEffect(() => { + const getUserSuccess = userData => { + const { enterprise, id } = userData; + setCurrentUser({ + id, + enterprise: { + name: enterprise ? enterprise.name : '', + }, + }); + }; + + const getUserData = () => { + api.getUsersAPI(false).getUser( + itemID, + getUserSuccess, + {}, + { + params: { + fields: [FIELD_ENTERPRISE, FIELD_HOSTNAME].toString(), + }, + }, + ); + }; + + if (api && !isEmpty(api) && item && sharedLink && !currentUser) { + getUserData(); + } + }, [api, currentUser, item, itemID, itemType, sharedLink]); + + const config = { + sharedLinkEmail: false, }; return ( - {children} + {item && ( + + {children} + + )} ); diff --git a/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx b/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx new file mode 100644 index 0000000000..7fce978ddc --- /dev/null +++ b/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx @@ -0,0 +1,134 @@ +import React from 'react'; +import { render, RenderResult, screen, waitFor } from '@testing-library/react'; + +import { + DEFAULT_ITEM_API_RESPONSE, + DEFAULT_USER_API_RESPONSE, + MOCK_ITEM, + MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, + MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION, +} from '../utils/__mocks__/ContentSharingV2Mocks'; +import { CONTENT_SHARING_ITEM_FIELDS } from '../constants'; + +import ContentSharingV2 from '../ContentSharingV2'; + +const createAPIMock = (fileAPI, folderAPI, usersAPI) => ({ + getFileAPI: jest.fn().mockReturnValue(fileAPI), + getFolderAPI: jest.fn().mockReturnValue(folderAPI), + getUsersAPI: jest.fn().mockReturnValue(usersAPI), +}); + +const createSuccessMock = responseFromAPI => (id, successFn) => { + return Promise.resolve(responseFromAPI).then(response => { + successFn(response); + }); +}; + +const getDefaultUserMock = jest.fn().mockImplementation(createSuccessMock(DEFAULT_USER_API_RESPONSE)); +const getDefaultFileMock = jest.fn().mockImplementation(createSuccessMock(DEFAULT_ITEM_API_RESPONSE)); +const getFileMockWithSharedLink = jest + .fn() + .mockImplementation(createSuccessMock(MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK)); +const getFileMockWithClassification = jest + .fn() + .mockImplementation(createSuccessMock(MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION)); +const getDefaultFolderMock = jest.fn().mockImplementation(createSuccessMock(DEFAULT_ITEM_API_RESPONSE)); +const defaultAPIMock = createAPIMock( + { getFile: getDefaultFileMock }, + { getFolderFields: getDefaultFolderMock }, + { getUser: getDefaultUserMock }, +); + +const getWrapper = (props): RenderResult => + render( + , + ); + +describe('elements/content-sharing/ContentSharingV2', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should see the correct elements for files', async () => { + getWrapper({}); + await waitFor(() => { + expect(getDefaultFileMock).toHaveBeenCalledWith( + MOCK_ITEM.id, + expect.any(Function), + {}, + { + fields: CONTENT_SHARING_ITEM_FIELDS, + }, + ); + }); + + expect(screen.getByRole('heading', { name: 'Share ‘Box Development Guide.pdf’' })).toBeVisible(); + expect(screen.getByRole('combobox', { name: 'Invite People' })).toBeVisible(); + expect(screen.getByRole('switch', { name: 'Shared link' })).toBeVisible(); + }); + + test('should see the correct elements for folders', async () => { + getWrapper({ itemType: 'folder' }); + await waitFor(() => { + expect(getDefaultFolderMock).toHaveBeenCalledWith( + MOCK_ITEM.id, + expect.any(Function), + {}, + { + fields: CONTENT_SHARING_ITEM_FIELDS, + }, + ); + }); + + expect(screen.getByRole('heading', { name: 'Share ‘Box Development Guide.pdf’' })).toBeVisible(); + expect(screen.getByRole('combobox', { name: 'Invite People' })).toBeVisible(); + expect(screen.getByRole('switch', { name: 'Shared link' })).toBeVisible(); + }); + + test('should see the shared link elements if shared link is present', async () => { + getWrapper({ + api: createAPIMock({ getFile: getFileMockWithSharedLink }, null, { getUser: getDefaultUserMock }), + }); + await waitFor(() => { + expect(getFileMockWithSharedLink).toHaveBeenCalledWith( + MOCK_ITEM.id, + expect.any(Function), + {}, + { + fields: CONTENT_SHARING_ITEM_FIELDS, + }, + ); + }); + + expect(await screen.findByLabelText('Shared link URL')).toBeVisible(); + expect(await screen.findByRole('button', { name: 'People with the link' })).toBeVisible(); + expect(await screen.findByRole('button', { name: 'Can view and download' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Link Settings' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Copy' })).toBeVisible(); + }); + + test('should see the classification elements if classification is present', async () => { + getWrapper({ + api: createAPIMock({ getFile: getFileMockWithClassification }, null, { getUser: getDefaultUserMock }), + }); + await waitFor(() => { + expect(getFileMockWithClassification).toHaveBeenCalledWith( + MOCK_ITEM.id, + expect.any(Function), + {}, + { + fields: CONTENT_SHARING_ITEM_FIELDS, + }, + ); + }); + + // TODO: Figure out why classification is not being rendered in the DOM + expect(await screen.findByText('BLUE')).toBeVisible(); + }); +}); diff --git a/src/elements/content-sharing/stories/ContentSharingV2.stories.tsx b/src/elements/content-sharing/stories/ContentSharingV2.stories.tsx index 166ca26ac5..e653601016 100644 --- a/src/elements/content-sharing/stories/ContentSharingV2.stories.tsx +++ b/src/elements/content-sharing/stories/ContentSharingV2.stories.tsx @@ -1,13 +1,22 @@ import * as React from 'react'; + import { TYPE_FILE, TYPE_FOLDER } from '../../../constants'; +import { mockAPIWithSharedLink, mockAPIWithoutSharedLink } from '../utils/__mocks__/ContentSharingV2Mocks'; import ContentSharingV2 from '../ContentSharingV2'; export const basic = {}; +export const withSharedLink = { + args: { + api: mockAPIWithSharedLink, + }, +}; + export default { title: 'Elements/ContentSharingV2', component: ContentSharingV2, args: { + api: mockAPIWithoutSharedLink, children: , itemType: TYPE_FILE, itemID: global.FILE_ID, diff --git a/src/elements/content-sharing/stories/tests/ContentSharingV2-visual.stories.tsx b/src/elements/content-sharing/stories/tests/ContentSharingV2-visual.stories.tsx index f49a24b1dd..3558c60975 100644 --- a/src/elements/content-sharing/stories/tests/ContentSharingV2-visual.stories.tsx +++ b/src/elements/content-sharing/stories/tests/ContentSharingV2-visual.stories.tsx @@ -1,16 +1,26 @@ +import * as React from 'react'; import { TYPE_FILE } from '../../../../constants'; +import { mockAPIWithSharedLink, mockAPIWithoutSharedLink } from '../../utils/__mocks__/ContentSharingV2Mocks'; import ContentSharingV2 from '../../ContentSharingV2'; export const withModernization = { args: { + api: mockAPIWithoutSharedLink, enableModernizedComponents: true, }, }; +export const withSharedLink = { + args: { + api: mockAPIWithSharedLink, + }, +}; + export default { title: 'Elements/ContentSharingV2/tests/visual-regression-tests', component: ContentSharingV2, args: { + children: , itemType: TYPE_FILE, itemID: global.FILE_ID, }, diff --git a/src/elements/content-sharing/types.js b/src/elements/content-sharing/types.js index 8181e28ddf..48e5fb16a2 100644 --- a/src/elements/content-sharing/types.js +++ b/src/elements/content-sharing/types.js @@ -152,3 +152,126 @@ export type ConvertCollabOptions = { isCurrentUserOwner: boolean, ownerEmail: ?string, }; + +// ContentSharingV2 types +interface Enterprise { + name?: string; +} + +export interface User { + id: string; + enterprise: Enterprise; +} + +export interface SharedLink { + /** + * The access level of the shared link. + */ + access?: AccessLevelType; + /** + * The available access levels for the shared link. The allowed levels are dependent on the Enterprise settings. + */ + accessLevels?: (AccessLevel | AccessLevelType)[]; + /** + * The expiration timestamp of the shared link to indicate when the item will be unshared. + */ + expiresAt?: number; + /** + * The permission level of the shared link. + */ + permission?: PermissionLevelType; + /** + * The available permission levels for the shared link. The allowed levels are dependent on the Item and Enterprise settings. + */ + permissionLevels?: (PermissionLevel | PermissionLevelType)[]; + /** + * The configuration options and permissions for managing the shared link settings. + */ + settings?: SharedLinkSettings; + /** + * The URL that can be used to access the shared item. + */ + url: string; + /** + * The static domain portion of the shared link. Used with `vanityName` to preview the custom URL. + */ + vanityDomain?: string; + /** + * The custom name of the shared link. Used with `vanityDomain` to preview the custom URL. + */ + vanityName?: string; +} + +export interface CollaborationRole { + /** + * The description for the role. Supported roles have default descriptions. + */ + description?: string; + /** + * The ID of the role. The value must be one of the supported roles within the Enterprise. + * + * If the value does not match a supported role, the role is treated as a custom collaboration role. + */ + id: InvitationRole | string; + /** + * When `true`, the role will be the default selected collaboration role. + */ + isDefault?: boolean; + /** + * When `true`, the role will be disabled when selecting a collaboration role. + */ + isDisabled?: boolean; + /** + * The label for the role. Supported roles have default labels. + */ + label?: string; +} + +export interface Classification { + colorId: number; + definition: string; + name: string; + restrictions?: string; +} + +export interface Item { + /** + * The classification of the item. + */ + classification?: Classification; + /** + * The ID of the item. + */ + id: string; + /** + * The name of the item. + */ + name: string; + /** + * The permissions that the current user has for the item. + */ + permissions?: { + /** + * When `true`, the user can invite collaborators on the item. + */ + canInviteCollaborator?: boolean, + /** + * When `true`, the user can change the access level of the shared link. + */ + canSetShareAccess?: boolean, + /** + * When `true`, the user can create a shared link for the item. + */ + canShare?: boolean, + }; + /** + * The type of the item. + */ + type: ItemType; +} + +export interface ItemData { + collaborationRoles: CollaborationRole[]; + item: Item; + sharedLink: SharedLink; +} diff --git a/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js b/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js new file mode 100644 index 0000000000..fd0eeb6717 --- /dev/null +++ b/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js @@ -0,0 +1,97 @@ +export const MOCK_PERMISSIONS = { + can_download: true, + can_invite_collaborator: true, + can_set_share_access: true, + can_share: true, +}; + +export const MOCK_CLASSIFICATION = { + color: '#91c2fd', + definition: 'Blue classification', + name: 'Blue', +}; + +export const MOCK_ITEM = { + id: '123456789', + name: 'Box Development Guide.pdf', + type: 'file', +}; + +export const MOCK_SHARED_LINK = { + access: 'open', + effective_permission: 'can_download', + is_password_enabled: true, + unshared_at: 1704067200000, + url: 'https://example.com/shared-link', + vanity_name: 'vanity-name', + vanity_url: 'https://example.com/vanity-url', +}; + +export const DEFAULT_USER_API_RESPONSE = { + id: '123', + enterprise: { + name: 'Parrot Enterprise', + }, +}; + +export const DEFAULT_ITEM_API_RESPONSE = { + allowed_invitee_roles: ['editor', 'viewer'], + allowed_shared_link_access_levels: ['open', 'company', 'collaborators'], + classification: null, + id: MOCK_ITEM.id, + name: MOCK_ITEM.name, + permissions: MOCK_PERMISSIONS, + shared_link: null, + shared_link_features: { password: true }, + type: MOCK_ITEM.type, +}; + +export const MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK = { + ...DEFAULT_ITEM_API_RESPONSE, + shared_link: MOCK_SHARED_LINK, +}; + +export const MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION = { + ...DEFAULT_ITEM_API_RESPONSE, + classification: MOCK_CLASSIFICATION, +}; + +// Mock API class for ContentSharingV2 storybook +export const createMockAPI = (itemResponse = DEFAULT_ITEM_API_RESPONSE, userResponse = DEFAULT_USER_API_RESPONSE) => { + const mockFileAPI = { + getFile: (itemID, successCallback) => { + // Simulate async behavior + setTimeout(() => { + successCallback(itemResponse); + }, 100); + }, + }; + + const mockFolderAPI = { + getFolderFields: (itemID, successCallback) => { + // Simulate async behavior + setTimeout(() => { + successCallback(itemResponse); + }, 100); + }, + }; + + const mockUsersAPI = { + getUser: (itemID, successCallback) => { + // Simulate async behavior + setTimeout(() => { + successCallback(userResponse); + }, 100); + }, + }; + + return { + getFileAPI: () => mockFileAPI, + getFolderAPI: () => mockFolderAPI, + getUsersAPI: () => mockUsersAPI, + }; +}; + +// Pre-configured mock APIs for different scenarios +export const mockAPIWithSharedLink = createMockAPI(MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK); +export const mockAPIWithoutSharedLink = createMockAPI(DEFAULT_ITEM_API_RESPONSE); diff --git a/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts b/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts new file mode 100644 index 0000000000..2bbf85b5e5 --- /dev/null +++ b/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts @@ -0,0 +1,119 @@ +import { + DEFAULT_ITEM_API_RESPONSE, + MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, + MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION, +} from '../__mocks__/ContentSharingV2Mocks'; +import { convertItemResponse } from '../convertItemResponse'; + +jest.mock('../getAllowedAccessLevels', () => ({ + getAllowedAccessLevels: jest.fn().mockReturnValue(['open', 'company', 'collaborators']), +})); + +jest.mock('../getAllowedPermissionLevels', () => ({ + getAllowedPermissionLevels: jest.fn().mockReturnValue(['canDownload', 'canPreview']), +})); + +describe('convertItemResponse', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('should convert basic item without shared link', () => { + const result = convertItemResponse(DEFAULT_ITEM_API_RESPONSE); + expect(result).toEqual({ + collaborationRoles: [{ id: 'editor' }, { id: 'viewer' }], + item: { + id: '123456789', + classification: undefined, + name: 'Box Development Guide.pdf', + permissions: { + canInviteCollaborator: true, + canSetShareAccess: true, + canShare: true, + }, + type: 'file', + }, + sharedLink: { + canInvite: true, + }, + }); + }); + + test('should handle folder type', () => { + const MOCK_ITEM_API_RESPONSE_WITH_FOLDER_TYPE = { + ...DEFAULT_ITEM_API_RESPONSE, + type: 'folder', + }; + const result = convertItemResponse(MOCK_ITEM_API_RESPONSE_WITH_FOLDER_TYPE); + expect(result.item.type).toBe('folder'); + }); + + test('should handle item with classification', () => { + const result = convertItemResponse(MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION); + expect(result.item.classification).toEqual({ + colorId: 4, + definition: 'Blue classification', + name: 'Blue', + }); + }); + + describe('shared link settings', () => { + test('should convert item with shared link', () => { + const result = convertItemResponse(MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK); + expect(result.sharedLink).toEqual({ + access: 'open', + accessLevels: ['open', 'company', 'collaborators'], + expiresAt: 1704067200000, + permission: 'can_download', + permissionLevels: ['canDownload', 'canPreview'], + settings: { + canChangeDownload: true, + canChangeExpiration: true, + canChangePassword: true, + canChangeVanityName: false, + isDownloadAvailable: true, + isDownloadEnabled: true, + isPasswordAvailable: true, + isPasswordEnabled: true, + }, + canInvite: true, + url: 'https://example.com/shared-link', + vanityDomain: 'https://example.com/vanity-url', + vanityName: 'vanity-name', + }); + }); + + test('should convert shared link settings correctly if user cannot change access level', () => { + const MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK_WITH_PERMISSIONS = { + ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, + permissions: { + ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK.permissions, + can_set_share_access: false, + }, + }; + const result = convertItemResponse(MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK_WITH_PERMISSIONS); + expect(result.sharedLink.settings.canChangeDownload).toEqual(false); + expect(result.sharedLink.settings.canChangePassword).toEqual(false); + expect(result.sharedLink.settings.canChangeExpiration).toEqual(false); + }); + + test('should convert shared link settings correctly if user does not have permissions', () => { + const MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK_WITH_PERMISSIONS = { + ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, + allowed_invitee_roles: ['viewer'], + shared_link: { + ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK.shared_link, + access: 'collaborators', + }, + shared_link_features: { password: false }, + permissions: { + ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK.permissions, + }, + }; + const result = convertItemResponse(MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK_WITH_PERMISSIONS); + expect(result.sharedLink.settings.canChangeDownload).toEqual(false); + expect(result.sharedLink.settings.canChangePassword).toEqual(false); + expect(result.sharedLink.settings.canChangeExpiration).toEqual(false); + }); + }); +}); diff --git a/src/elements/content-sharing/utils/__tests__/getAllowedAccessLevels.test.ts b/src/elements/content-sharing/utils/__tests__/getAllowedAccessLevels.test.ts new file mode 100644 index 0000000000..169594487d --- /dev/null +++ b/src/elements/content-sharing/utils/__tests__/getAllowedAccessLevels.test.ts @@ -0,0 +1,24 @@ +import { ACCESS_COLLAB, ACCESS_COMPANY, ACCESS_OPEN } from '../../../../constants'; +import { getAllowedAccessLevels } from '../getAllowedAccessLevels'; + +describe('getAllowedAccessLevels', () => { + test('should return default access levels when no levels parameter is provided', () => { + const result = getAllowedAccessLevels(); + expect(result).toEqual([ACCESS_OPEN, ACCESS_COMPANY, ACCESS_COLLAB]); + }); + + test('should return empty array when levels parameter is empty array', () => { + const result = getAllowedAccessLevels([]); + expect(result).toEqual([]); + }); + + test.each([ + [[ACCESS_OPEN, ACCESS_COMPANY, ACCESS_COLLAB]], + [[ACCESS_OPEN, ACCESS_COMPANY]], + [[ACCESS_OPEN]], + [[ACCESS_COMPANY]], + ])('should return the same levels as provided', levels => { + const result = getAllowedAccessLevels(levels); + expect(result).toEqual(levels); + }); +}); diff --git a/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts b/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts new file mode 100644 index 0000000000..65963a3615 --- /dev/null +++ b/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts @@ -0,0 +1,28 @@ +import { PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW } from '../../../../constants'; +import { getAllowedPermissionLevels } from '../getAllowedPermissionLevels'; + +describe('getAllowedPermissionLevels', () => { + test('should return both permission levels when all conditions are met', () => { + const result = getAllowedPermissionLevels(true, true, PERMISSION_CAN_DOWNLOAD); + expect(result).toEqual([PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW]); + }); + + test.each([PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW])( + 'should return only current permission when cannot change access level', + permission => { + const result = getAllowedPermissionLevels(false, true, permission); + expect(result).toEqual([permission]); + }, + ); + + test('should exclude download permission when download setting is not available', () => { + const result = getAllowedPermissionLevels(true, false, PERMISSION_CAN_DOWNLOAD); + expect(result).toEqual([PERMISSION_CAN_PREVIEW]); + }); + + test('should return empty array for unknown permission values when cannot change access level', () => { + const unknownPermission = 'unknown_permission'; + const result = getAllowedPermissionLevels(false, true, unknownPermission); + expect(result).toEqual([]); + }); +}); diff --git a/src/elements/content-sharing/utils/constants.ts b/src/elements/content-sharing/utils/constants.ts new file mode 100644 index 0000000000..71ee401eab --- /dev/null +++ b/src/elements/content-sharing/utils/constants.ts @@ -0,0 +1,31 @@ +import { + CLASSIFICATION_COLOR_ID_0, + CLASSIFICATION_COLOR_ID_1, + CLASSIFICATION_COLOR_ID_2, + CLASSIFICATION_COLOR_ID_3, + CLASSIFICATION_COLOR_ID_4, + CLASSIFICATION_COLOR_ID_5, + CLASSIFICATION_COLOR_ID_6, + CLASSIFICATION_COLOR_ID_7, +} from '../../../features/classification/constants'; +import { + bdlDarkBlue50, + bdlGray20, + bdlGreenLight50, + bdlLightBlue50, + bdlOrange50, + bdlPurpleRain50, + bdlWatermelonRed50, + bdlYellow50, +} from '../../../styles/variables'; + +export const API_TO_USM_CLASSIFICATION_COLORS_MAP = { + [bdlYellow50]: CLASSIFICATION_COLOR_ID_0, + [bdlOrange50]: CLASSIFICATION_COLOR_ID_1, + [bdlWatermelonRed50]: CLASSIFICATION_COLOR_ID_2, + [bdlPurpleRain50]: CLASSIFICATION_COLOR_ID_3, + [bdlLightBlue50]: CLASSIFICATION_COLOR_ID_4, + [bdlDarkBlue50]: CLASSIFICATION_COLOR_ID_5, + [bdlGreenLight50]: CLASSIFICATION_COLOR_ID_6, + [bdlGray20]: CLASSIFICATION_COLOR_ID_7, +}; diff --git a/src/elements/content-sharing/utils/convertItemResponse.ts b/src/elements/content-sharing/utils/convertItemResponse.ts new file mode 100644 index 0000000000..239d6299bb --- /dev/null +++ b/src/elements/content-sharing/utils/convertItemResponse.ts @@ -0,0 +1,99 @@ +import { ACCESS_COLLAB, INVITEE_ROLE_EDITOR, PERMISSION_CAN_DOWNLOAD } from '../../../constants'; +import { getAllowedAccessLevels, getAllowedPermissionLevels } from '../utils'; +import { API_TO_USM_CLASSIFICATION_COLORS_MAP } from '../utils/constants'; + +import type { ContentSharingItemAPIResponse, ItemData, SharedLink } from '../types'; + +export const convertItemResponse = (itemAPIData: ContentSharingItemAPIResponse): ItemData => { + const { + allowed_invitee_roles, + allowed_shared_link_access_levels, + classification, + id, + name, + permissions, + shared_link, + shared_link_features: { password: isPasswordAvailable }, + type, + } = itemAPIData; + + const { + can_download: isDownloadSettingAvailable, + can_invite_collaborator: canInvite, + can_set_share_access: canChangeAccessLevel, + can_share: canShare, + } = permissions; + + // Convert classification data for the item if available + let classificationData; + if (classification) { + const { color, definition, name: classificationName } = classification; + classificationData = { + colorId: API_TO_USM_CLASSIFICATION_COLORS_MAP[color], + definition, + name: classificationName, + }; + } + + const isEditAllowed = allowed_invitee_roles.indexOf(INVITEE_ROLE_EDITOR) !== -1; + + // The "canInvite" property is necessary even if the item does not have a shared link, + // because it allows users to invite individual collaborators. + let sharedLink: SharedLink = { canInvite: !!canInvite }; + if (shared_link) { + const { + access, + effective_permission: permission, + is_password_enabled: isPasswordEnabled, + unshared_at: expirationTimestamp, + url, + vanity_name: vanityName, + vanity_url: vanityUrl, + } = shared_link; + + const isDownloadAllowed = permission === PERMISSION_CAN_DOWNLOAD; + const canChangeDownload = canChangeAccessLevel && isDownloadSettingAvailable && access !== ACCESS_COLLAB; // access must be "company" or "open" + const canChangePassword = canChangeAccessLevel && isPasswordAvailable; + const canChangeExpiration = canChangeAccessLevel && isEditAllowed; + + sharedLink = { + access, + accessLevels: getAllowedAccessLevels(allowed_shared_link_access_levels), + expiresAt: expirationTimestamp, + permission, + permissionLevels: getAllowedPermissionLevels(canChangeAccessLevel, isDownloadSettingAvailable, permission), + settings: { + canChangeDownload, + canChangeExpiration, + canChangePassword, + canChangeVanityName: false, // vanity URLs cannot be set via the API, + isDownloadAvailable: isDownloadSettingAvailable, + isDownloadEnabled: isDownloadAllowed, + isPasswordAvailable, + isPasswordEnabled, + }, + canInvite: !!canInvite, + url, + vanityDomain: vanityUrl, + vanityName: vanityName || '', + }; + } + + const collaborationRoles = allowed_invitee_roles.map(role => ({ id: role })); + + return { + collaborationRoles, + item: { + id, + classification: classificationData, + name, + permissions: { + canInviteCollaborator: !!canInvite, + canSetShareAccess: canChangeAccessLevel, + canShare: !!canShare, + }, + type, + }, + sharedLink, + }; +}; diff --git a/src/elements/content-sharing/utils/getAllowedAccessLevels.ts b/src/elements/content-sharing/utils/getAllowedAccessLevels.ts new file mode 100644 index 0000000000..3cf2a545ee --- /dev/null +++ b/src/elements/content-sharing/utils/getAllowedAccessLevels.ts @@ -0,0 +1,12 @@ +import { ACCESS_COLLAB, ACCESS_COMPANY, ACCESS_OPEN } from '../../../constants'; + +export const getAllowedAccessLevels = (levels?: Array): Array | null => { + if (!levels) return [ACCESS_OPEN, ACCESS_COMPANY, ACCESS_COLLAB]; + + const allowedAccessLevels = []; + levels.forEach(level => { + allowedAccessLevels.push(level); + }); + + return allowedAccessLevels; +}; diff --git a/src/elements/content-sharing/utils/getAllowedPermissionLevels.ts b/src/elements/content-sharing/utils/getAllowedPermissionLevels.ts new file mode 100644 index 0000000000..2922386832 --- /dev/null +++ b/src/elements/content-sharing/utils/getAllowedPermissionLevels.ts @@ -0,0 +1,21 @@ +import { PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW } from '../../../constants'; + +export const getAllowedPermissionLevels = ( + canChangeAccessLevel, + isDownloadSettingAvailable, + permission, +): Array => { + let allowedPermissionLevels = [PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW]; + + if (!canChangeAccessLevel) { + // remove all but current level + allowedPermissionLevels = allowedPermissionLevels.filter(level => level === permission); + } + + // if we cannot set the download value, we remove this option from the dropdown + if (!isDownloadSettingAvailable) { + allowedPermissionLevels = allowedPermissionLevels.filter(level => level !== PERMISSION_CAN_DOWNLOAD); + } + + return allowedPermissionLevels; +}; diff --git a/src/elements/content-sharing/utils/index.ts b/src/elements/content-sharing/utils/index.ts new file mode 100644 index 0000000000..38b674e241 --- /dev/null +++ b/src/elements/content-sharing/utils/index.ts @@ -0,0 +1,3 @@ +export { convertItemResponse } from './convertItemResponse'; +export { getAllowedAccessLevels } from './getAllowedAccessLevels'; +export { getAllowedPermissionLevels } from './getAllowedPermissionLevels'; diff --git a/yarn.lock b/yarn.lock index b8c82fd7b0..3186ae4964 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1549,10 +1549,10 @@ resolved "https://registry.yarnpkg.com/@box/types/-/types-0.2.1.tgz#cd0a3915b2306e4cf581f6091b95f5d2db75ea60" integrity sha512-wd6nRR9QxBl7lYKJ/Hix0AKg1PNC3leZWOJ9Nt+d4j45WxCYBiCemZAtY2ekL5BITpVw8vlLmquzSpPhDTeO5A== -"@box/unified-share-modal@^0.48.8": - version "0.48.8" - resolved "https://registry.yarnpkg.com/@box/unified-share-modal/-/unified-share-modal-0.48.8.tgz#d166ec081788e142fd90332f1c96bc17890d79a8" - integrity sha512-zF1kAc9inyQnKkMPyghiRkpqeA5w4NO3fyuRRq0QIXwP0Xt8edZsD/sLj2sXmRzNw/9W2Qz/7wih3p+xzrEUxg== +"@box/unified-share-modal@^0.50.0": + version "0.50.1" + resolved "https://registry.yarnpkg.com/@box/unified-share-modal/-/unified-share-modal-0.50.1.tgz#b234c0c9985e52351fa227fb4bbbc6a519f0f8a5" + integrity sha512-m4NQMJnQWUeYVg62dwtZenyatjBdIye8UjLdBIAc0e7OKz2K6mpwBGe6/GzES1T/VKCw7Y4VWTfqqk9jPlBoxA== "@box/user-selector@^1.23.25": version "1.23.25" From ae6bdb7b3fd44783753aa36be679f14153ff60cb Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Wed, 24 Sep 2025 16:48:26 -0700 Subject: [PATCH 2/6] fix: import type directly from shared feature --- .../content-sharing/ContentSharingV2.tsx | 2 +- src/elements/content-sharing/types.js | 119 +----------------- .../__tests__/convertItemResponse.test.ts | 4 - .../utils/convertItemResponse.ts | 7 +- 4 files changed, 5 insertions(+), 127 deletions(-) diff --git a/src/elements/content-sharing/ContentSharingV2.tsx b/src/elements/content-sharing/ContentSharingV2.tsx index 705fe13c29..925352c544 100644 --- a/src/elements/content-sharing/ContentSharingV2.tsx +++ b/src/elements/content-sharing/ContentSharingV2.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import isEmpty from 'lodash/isEmpty'; import { UnifiedShareModal } from '@box/unified-share-modal'; +import type { CollaborationRole, Item, SharedLink, User } from '@box/unified-share-modal'; import API from '../../api'; import { FIELD_ENTERPRISE, FIELD_HOSTNAME, TYPE_FILE, TYPE_FOLDER } from '../../constants'; @@ -11,7 +12,6 @@ import { CONTENT_SHARING_ITEM_FIELDS } from './constants'; import { convertItemResponse } from './utils'; import type { ItemType, StringMap } from '../../common/types/core'; -import type { CollaborationRole, Item, SharedLink, User } from './types'; export interface ContentSharingV2Props { /** api - API instance */ diff --git a/src/elements/content-sharing/types.js b/src/elements/content-sharing/types.js index 48e5fb16a2..130328a442 100644 --- a/src/elements/content-sharing/types.js +++ b/src/elements/content-sharing/types.js @@ -1,4 +1,6 @@ // @flow +import type { CollaborationRole, Item, SharedLink } from '@box/unified-share-modal'; + import type { Access, BoxItemClassification, @@ -153,123 +155,6 @@ export type ConvertCollabOptions = { ownerEmail: ?string, }; -// ContentSharingV2 types -interface Enterprise { - name?: string; -} - -export interface User { - id: string; - enterprise: Enterprise; -} - -export interface SharedLink { - /** - * The access level of the shared link. - */ - access?: AccessLevelType; - /** - * The available access levels for the shared link. The allowed levels are dependent on the Enterprise settings. - */ - accessLevels?: (AccessLevel | AccessLevelType)[]; - /** - * The expiration timestamp of the shared link to indicate when the item will be unshared. - */ - expiresAt?: number; - /** - * The permission level of the shared link. - */ - permission?: PermissionLevelType; - /** - * The available permission levels for the shared link. The allowed levels are dependent on the Item and Enterprise settings. - */ - permissionLevels?: (PermissionLevel | PermissionLevelType)[]; - /** - * The configuration options and permissions for managing the shared link settings. - */ - settings?: SharedLinkSettings; - /** - * The URL that can be used to access the shared item. - */ - url: string; - /** - * The static domain portion of the shared link. Used with `vanityName` to preview the custom URL. - */ - vanityDomain?: string; - /** - * The custom name of the shared link. Used with `vanityDomain` to preview the custom URL. - */ - vanityName?: string; -} - -export interface CollaborationRole { - /** - * The description for the role. Supported roles have default descriptions. - */ - description?: string; - /** - * The ID of the role. The value must be one of the supported roles within the Enterprise. - * - * If the value does not match a supported role, the role is treated as a custom collaboration role. - */ - id: InvitationRole | string; - /** - * When `true`, the role will be the default selected collaboration role. - */ - isDefault?: boolean; - /** - * When `true`, the role will be disabled when selecting a collaboration role. - */ - isDisabled?: boolean; - /** - * The label for the role. Supported roles have default labels. - */ - label?: string; -} - -export interface Classification { - colorId: number; - definition: string; - name: string; - restrictions?: string; -} - -export interface Item { - /** - * The classification of the item. - */ - classification?: Classification; - /** - * The ID of the item. - */ - id: string; - /** - * The name of the item. - */ - name: string; - /** - * The permissions that the current user has for the item. - */ - permissions?: { - /** - * When `true`, the user can invite collaborators on the item. - */ - canInviteCollaborator?: boolean, - /** - * When `true`, the user can change the access level of the shared link. - */ - canSetShareAccess?: boolean, - /** - * When `true`, the user can create a shared link for the item. - */ - canShare?: boolean, - }; - /** - * The type of the item. - */ - type: ItemType; -} - export interface ItemData { collaborationRoles: CollaborationRole[]; item: Item; diff --git a/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts b/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts index 2bbf85b5e5..077ab26a19 100644 --- a/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts +++ b/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts @@ -33,9 +33,6 @@ describe('convertItemResponse', () => { }, type: 'file', }, - sharedLink: { - canInvite: true, - }, }); }); @@ -76,7 +73,6 @@ describe('convertItemResponse', () => { isPasswordAvailable: true, isPasswordEnabled: true, }, - canInvite: true, url: 'https://example.com/shared-link', vanityDomain: 'https://example.com/vanity-url', vanityName: 'vanity-name', diff --git a/src/elements/content-sharing/utils/convertItemResponse.ts b/src/elements/content-sharing/utils/convertItemResponse.ts index 239d6299bb..85fb3b4135 100644 --- a/src/elements/content-sharing/utils/convertItemResponse.ts +++ b/src/elements/content-sharing/utils/convertItemResponse.ts @@ -2,7 +2,7 @@ import { ACCESS_COLLAB, INVITEE_ROLE_EDITOR, PERMISSION_CAN_DOWNLOAD } from '../ import { getAllowedAccessLevels, getAllowedPermissionLevels } from '../utils'; import { API_TO_USM_CLASSIFICATION_COLORS_MAP } from '../utils/constants'; -import type { ContentSharingItemAPIResponse, ItemData, SharedLink } from '../types'; +import type { ContentSharingItemAPIResponse, ItemData } from '../types'; export const convertItemResponse = (itemAPIData: ContentSharingItemAPIResponse): ItemData => { const { @@ -37,9 +37,7 @@ export const convertItemResponse = (itemAPIData: ContentSharingItemAPIResponse): const isEditAllowed = allowed_invitee_roles.indexOf(INVITEE_ROLE_EDITOR) !== -1; - // The "canInvite" property is necessary even if the item does not have a shared link, - // because it allows users to invite individual collaborators. - let sharedLink: SharedLink = { canInvite: !!canInvite }; + let sharedLink; if (shared_link) { const { access, @@ -72,7 +70,6 @@ export const convertItemResponse = (itemAPIData: ContentSharingItemAPIResponse): isPasswordAvailable, isPasswordEnabled, }, - canInvite: !!canInvite, url, vanityDomain: vanityUrl, vanityName: vanityName || '', From a7b10114f93e5d473ee298f03738c0e04d67a67c Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Wed, 24 Sep 2025 16:49:54 -0700 Subject: [PATCH 3/6] fix: remove TODO --- .../content-sharing/__tests__/ContentSharingV2.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx b/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx index 7fce978ddc..93e0b8d839 100644 --- a/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx +++ b/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx @@ -127,8 +127,6 @@ describe('elements/content-sharing/ContentSharingV2', () => { }, ); }); - - // TODO: Figure out why classification is not being rendered in the DOM expect(await screen.findByText('BLUE')).toBeVisible(); }); }); From 6c67b0697ee7f4e923fb088334783b95c5e8723b Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Wed, 24 Sep 2025 16:53:00 -0700 Subject: [PATCH 4/6] fix: update usm package version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ede53a187c..db0845a494 100644 --- a/package.json +++ b/package.json @@ -310,7 +310,7 @@ "@box/metadata-view": "^0.54.0", "@box/react-virtualized": "^9.22.3-rc-box.10", "@box/types": "^0.2.1", - "@box/unified-share-modal": "^0.48.8", + "@box/unified-share-modal": "^0.50.0", "@box/user-selector": "^1.23.25", "@hapi/address": "^2.1.4", "@tanstack/react-virtual": "^3.13.12", From 83656085ca682b69ff037dd3c2c9076171256463 Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Thu, 25 Sep 2025 14:33:32 -0700 Subject: [PATCH 5/6] fix: nits and remove not needed token --- .../content-sharing/ContentSharing.js | 22 ++++++++++--------- .../__tests__/ContentSharingV2.test.tsx | 4 ++-- .../utils/convertItemResponse.ts | 6 +++-- .../utils/getAllowedAccessLevels.ts | 8 +------ 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/elements/content-sharing/ContentSharing.js b/src/elements/content-sharing/ContentSharing.js index fdef1cc69f..94b27ffe45 100644 --- a/src/elements/content-sharing/ContentSharing.js +++ b/src/elements/content-sharing/ContentSharing.js @@ -116,16 +116,18 @@ function ContentSharing({ if (isFeatureEnabled(features, 'contentSharingV2')) { return ( - - {children} - + api && ( + + {children} + + ) ); } diff --git a/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx b/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx index 93e0b8d839..f8165d9064 100644 --- a/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx +++ b/src/elements/content-sharing/__tests__/ContentSharingV2.test.tsx @@ -47,7 +47,7 @@ const getWrapper = (props): RenderResult => itemType={MOCK_ITEM.type} hasProviders={true} {...props} - >, + />, ); describe('elements/content-sharing/ContentSharingV2', () => { @@ -127,6 +127,6 @@ describe('elements/content-sharing/ContentSharingV2', () => { }, ); }); - expect(await screen.findByText('BLUE')).toBeVisible(); + expect(screen.getByText('BLUE')).toBeVisible(); }); }); diff --git a/src/elements/content-sharing/utils/convertItemResponse.ts b/src/elements/content-sharing/utils/convertItemResponse.ts index 85fb3b4135..aa184f1ec6 100644 --- a/src/elements/content-sharing/utils/convertItemResponse.ts +++ b/src/elements/content-sharing/utils/convertItemResponse.ts @@ -13,10 +13,12 @@ export const convertItemResponse = (itemAPIData: ContentSharingItemAPIResponse): name, permissions, shared_link, - shared_link_features: { password: isPasswordAvailable }, + shared_link_features, type, } = itemAPIData; + const { password: isPasswordAvailable } = shared_link_features; + const { can_download: isDownloadSettingAvailable, can_invite_collaborator: canInvite, @@ -67,7 +69,7 @@ export const convertItemResponse = (itemAPIData: ContentSharingItemAPIResponse): canChangeVanityName: false, // vanity URLs cannot be set via the API, isDownloadAvailable: isDownloadSettingAvailable, isDownloadEnabled: isDownloadAllowed, - isPasswordAvailable, + isPasswordAvailable: isPasswordAvailable ?? false, isPasswordEnabled, }, url, diff --git a/src/elements/content-sharing/utils/getAllowedAccessLevels.ts b/src/elements/content-sharing/utils/getAllowedAccessLevels.ts index 3cf2a545ee..8e2602371f 100644 --- a/src/elements/content-sharing/utils/getAllowedAccessLevels.ts +++ b/src/elements/content-sharing/utils/getAllowedAccessLevels.ts @@ -2,11 +2,5 @@ import { ACCESS_COLLAB, ACCESS_COMPANY, ACCESS_OPEN } from '../../../constants'; export const getAllowedAccessLevels = (levels?: Array): Array | null => { if (!levels) return [ACCESS_OPEN, ACCESS_COMPANY, ACCESS_COLLAB]; - - const allowedAccessLevels = []; - levels.forEach(level => { - allowedAccessLevels.push(level); - }); - - return allowedAccessLevels; + return [...levels]; }; From c843e0d72892c5fe6e3215cd20cacf7dd0447f7d Mon Sep 17 00:00:00 2001 From: reneshen0328 Date: Fri, 26 Sep 2025 16:26:40 -0700 Subject: [PATCH 6/6] fix: import types --- package.json | 4 ++-- .../content-sharing/utils/convertItemResponse.ts | 3 ++- yarn.lock | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index db0845a494..4c6dba7099 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "@box/metadata-view": "^0.54.0", "@box/react-virtualized": "^9.22.3-rc-box.10", "@box/types": "^0.2.1", - "@box/unified-share-modal": "^0.50.0", + "@box/unified-share-modal": "^0.52.0", "@box/user-selector": "^1.23.25", "@cfaester/enzyme-adapter-react-18": "^0.8.0", "@chromatic-com/storybook": "^4.0.1", @@ -310,7 +310,7 @@ "@box/metadata-view": "^0.54.0", "@box/react-virtualized": "^9.22.3-rc-box.10", "@box/types": "^0.2.1", - "@box/unified-share-modal": "^0.50.0", + "@box/unified-share-modal": "^0.52.0", "@box/user-selector": "^1.23.25", "@hapi/address": "^2.1.4", "@tanstack/react-virtual": "^3.13.12", diff --git a/src/elements/content-sharing/utils/convertItemResponse.ts b/src/elements/content-sharing/utils/convertItemResponse.ts index aa184f1ec6..060ee982b4 100644 --- a/src/elements/content-sharing/utils/convertItemResponse.ts +++ b/src/elements/content-sharing/utils/convertItemResponse.ts @@ -1,5 +1,6 @@ import { ACCESS_COLLAB, INVITEE_ROLE_EDITOR, PERMISSION_CAN_DOWNLOAD } from '../../../constants'; -import { getAllowedAccessLevels, getAllowedPermissionLevels } from '../utils'; +import { getAllowedAccessLevels } from './getAllowedAccessLevels'; +import { getAllowedPermissionLevels } from './getAllowedPermissionLevels'; import { API_TO_USM_CLASSIFICATION_COLORS_MAP } from '../utils/constants'; import type { ContentSharingItemAPIResponse, ItemData } from '../types'; diff --git a/yarn.lock b/yarn.lock index 3186ae4964..c20a76eefc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1419,7 +1419,7 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@box/blueprint-web-assets@^4.68.6": +"@box/blueprint-web-assets@^4.68.0", "@box/blueprint-web-assets@^4.68.6": version "4.68.6" resolved "https://registry.yarnpkg.com/@box/blueprint-web-assets/-/blueprint-web-assets-4.68.6.tgz#81c27616687794032e9dc7ece6857797188e5130" integrity sha512-2UrvvlCzE/PkgQ3yQldqlZxCF6pUXp+UKOuvFGAmAm2B1hWw0v3BfiPDTTJSRfGAnukNnpnItjdMkaq/qXKOpA== @@ -1549,10 +1549,10 @@ resolved "https://registry.yarnpkg.com/@box/types/-/types-0.2.1.tgz#cd0a3915b2306e4cf581f6091b95f5d2db75ea60" integrity sha512-wd6nRR9QxBl7lYKJ/Hix0AKg1PNC3leZWOJ9Nt+d4j45WxCYBiCemZAtY2ekL5BITpVw8vlLmquzSpPhDTeO5A== -"@box/unified-share-modal@^0.50.0": - version "0.50.1" - resolved "https://registry.yarnpkg.com/@box/unified-share-modal/-/unified-share-modal-0.50.1.tgz#b234c0c9985e52351fa227fb4bbbc6a519f0f8a5" - integrity sha512-m4NQMJnQWUeYVg62dwtZenyatjBdIye8UjLdBIAc0e7OKz2K6mpwBGe6/GzES1T/VKCw7Y4VWTfqqk9jPlBoxA== +"@box/unified-share-modal@^0.52.0": + version "0.52.0" + resolved "https://registry.yarnpkg.com/@box/unified-share-modal/-/unified-share-modal-0.52.0.tgz#5ebfb1c9246789ce4650efc9b19283de0c492f71" + integrity sha512-85/xr47n9uCNwJ3nMq5AEGHJ6DOUrClh9ARbwaoJeR39x0sTjv0JGIBAmK4yM2rhPMGZO2jnnBcZ8wbZ+yVoHw== "@box/user-selector@^1.23.25": version "1.23.25"