Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
"@box/types": "^2.1.8",
"@box/unified-share-modal": "^2.15.16",
"@box/user-selector": "^1.76.19",
"@box/uploads-manager": "^1.14.0",
"@box/uploads-manager": "^1.15.0",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@chromatic-com/storybook": "^4.0.1",
"@commitlint/cli": "^19.8.0",
Expand Down Expand Up @@ -314,7 +314,7 @@
"@box/types": "^2.1.8",
"@box/unified-share-modal": "^2.15.16",
"@box/user-selector": "^1.76.19",
"@box/uploads-manager": "^1.14.0",
"@box/uploads-manager": "^1.15.0",
"@hapi/address": "^2.1.4",
"@tanstack/react-virtual": "^3.13.12",
"axios": "^0.31.1",
Expand Down
5 changes: 5 additions & 0 deletions src/api/uploads/FolderUploadNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ class FolderUploadNode {
const folderObject: FolderUploadItem = {
extension: '',
name: this.name,
options: {
...this.fileAPIOptions,
folderId: this.folderId,
uploadInitTimestamp: Date.now(),
},
status: STATUS_COMPLETE,
isFolder: true,
size: 1,
Expand Down
2 changes: 2 additions & 0 deletions src/api/uploads/__tests__/FolderUploadNode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ describe('api/uploads/FolderUploadNode', () => {
expect(folderUploadNodeInstance.addFolderToUploadQueue).toHaveBeenCalledWith({
extension: '',
name,
options: expect.objectContaining({ folderId, uploadInitTimestamp: expect.any(Number) }),
status: STATUS_COMPLETE,
isFolder: true,
size: 1,
Expand All @@ -167,6 +168,7 @@ describe('api/uploads/FolderUploadNode', () => {
expect(folderUploadNodeInstance.addFolderToUploadQueue).toHaveBeenCalledWith({
extension: '',
name,
options: expect.objectContaining({ folderId, uploadInitTimestamp: expect.any(Number) }),
status: STATUS_COMPLETE,
isFolder: true,
size: 1,
Expand Down
58 changes: 47 additions & 11 deletions src/elements/content-uploader/ContentUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AxiosRequestConfig, AxiosResponse } from 'axios';
import DroppableContent from './DroppableContent';
import Footer from './Footer';
import UploadsManager from './UploadsManager';
import { getUploadItemKey, mapToModernizedUploadItems } from './utils/mapToModernizedUploadItem';
import API from '../../api';
import Browser from '../../utils/Browser';
import Internationalize from '../common/Internationalize';
Expand Down Expand Up @@ -495,15 +496,16 @@ class ContentUploader extends Component<ContentUploaderProps, State> {

const fileAPIOptions = getDataTransferItemAPIOptions(newItems[0]);
const { folderId = rootFolderId } = fileAPIOptions;
const folderUploads = newItems.map(item => {
const dropTimestamp = Date.now();
const folderUploads = newItems.map((item, index) => {
const folderUpload = this.getFolderUploadAPI(folderId);
folderUpload.buildFolderTreeFromDataTransferItem(item);
return {
api: folderUpload,
extension: '',
isFolder: true,
name: folderUpload.folder.name,
options: fileAPIOptions,
options: { ...fileAPIOptions, uploadInitTimestamp: dropTimestamp + index },
progress: 0,
size: 1,
status: STATUS_PENDING,
Expand Down Expand Up @@ -568,7 +570,7 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
extension: '',
isFolder: true,
name: folderUpload.folder.name,
options: apiOptions,
options: { uploadInitTimestamp: Date.now(), ...apiOptions },
progress: 0,
size: 1,
status: STATUS_PENDING,
Expand Down Expand Up @@ -1219,6 +1221,34 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
}
};

/**
* Find legacy UploadItem by the client-side key used by the modernized uploads manager.
*/
findItemByUploadKey = (key: string): UploadItem | undefined => {
const { rootFolderId } = this.props;
return this.state.items.find(item => getUploadItemKey(item, rootFolderId) === key);
};

handleModernizedItemAction = (key: string) => {
const item = this.findItemByUploadKey(key);
if (item) {
Comment thread
jpan-box marked this conversation as resolved.
this.onClick(item);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} else {
// eslint-disable-next-line no-console
console.warn(`ContentUploader: no upload item found for key "${key}" on action.`);
}
};

handleModernizedItemRemove = (key: string) => {
const item = this.findItemByUploadKey(key);
if (item) {
this.removeFileFromUploadQueue(item);
} else {
// eslint-disable-next-line no-console
console.warn(`ContentUploader: no upload item found for key "${key}" on remove.`);
}
};

/**
* Empties the items queue
*
Expand Down Expand Up @@ -1282,6 +1312,7 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
messages,
onClose,
onUpgradeCTAClick,
rootFolderId,
theme,
useUploadsManager,
}: ContentUploaderProps = this.props;
Expand All @@ -1303,7 +1334,14 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
return (
<div ref={measureRef} className={styleClassName} id={this.id}>
<ThemingStyles selector={`#${this.id}`} theme={theme} />
<UploadsManagerBP items={[]} />
<UploadsManagerBP
items={mapToModernizedUploadItems(items, rootFolderId)}
Comment thread
jpan-box marked this conversation as resolved.
isExpanded={isUploadsManagerExpanded}
onToggle={this.toggleUploadsManager}
onItemCancel={this.handleModernizedItemAction}
onItemRetry={this.handleModernizedItemAction}
Comment thread
dealwith marked this conversation as resolved.
onItemRemove={this.handleModernizedItemRemove}
/>
</div>
);
}
Expand All @@ -1326,9 +1364,9 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
view={view}
/>
</div>
)
);
}

return (
<div ref={measureRef} className={styleClassName} id={this.id}>
<ThemingStyles selector={`#${this.id}`} theme={theme} />
Expand All @@ -1353,14 +1391,12 @@ class ContentUploader extends Component<ContentUploaderProps, State> {
isDone={isDone}
/>
</div>
)
}
);
};

return (
<Internationalize language={language} messages={messages}>
<TooltipProvider>
{renderUploader()}
</TooltipProvider>
<TooltipProvider>{renderUploader()}</TooltipProvider>
</Internationalize>
);
}
Expand Down
119 changes: 116 additions & 3 deletions src/elements/content-uploader/__tests__/ContentUploader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,18 +775,131 @@ describe('elements/content-uploader/ContentUploader', () => {
expect(wrapper.find(UploadsManagerBP)).toHaveLength(0);
});

test('should render modernized UploadsManager when enableModernizedUploads is true', () => {
test('should render modernized UploadsManagerBP when enableModernizedUploads is true', () => {
const wrapper = getWrapper({ enableModernizedUploads: true });
expect(wrapper.find(UploadsManagerBP)).toHaveLength(1);
expect(wrapper.find(UploadsManager)).toHaveLength(0);
expect(wrapper.find(DroppableContent)).toHaveLength(0);
});

test('should render modernized UploadsManager when enableModernizedUploads is true and useUploadsManager is true', () => {
test('should render modernized UploadsManagerBP even when useUploadsManager is true', () => {
const wrapper = getWrapper({ enableModernizedUploads: true, useUploadsManager: true });
expect(wrapper.find(UploadsManagerBP)).toHaveLength(1);
expect(wrapper.find(UploadsManager)).toHaveLength(0);
expect(wrapper.find(DroppableContent)).toHaveLength(0);
});

test('should map state.items to modernized item shape', () => {
const wrapper = getWrapper({ enableModernizedUploads: true });
wrapper.setState({
items: [
{
name: 'foo.pdf',
extension: 'pdf',
progress: 42,
status: STATUS_IN_PROGRESS,
file: { name: 'foo.pdf' },
},
],
});
const items = wrapper.find(UploadsManagerBP).prop('items');
expect(items).toHaveLength(1);
expect(items[0]).toMatchObject({
name: 'foo.pdf',
extension: 'pdf',
progress: 42,
status: 'uploading',
});
});

test('should pass isExpanded from state', () => {
const wrapper = getWrapper({ enableModernizedUploads: true });
wrapper.setState({ isUploadsManagerExpanded: true });
expect(wrapper.find(UploadsManagerBP).prop('isExpanded')).toBe(true);
});

test('should call onClick when onItemCancel is invoked', () => {
const wrapper = getWrapper({ enableModernizedUploads: true });
const item = {
name: 'foo.pdf',
extension: 'pdf',
progress: 0,
status: STATUS_PENDING,
file: { name: 'foo.pdf' },
};
wrapper.setState({ items: [item] });
const instance = wrapper.instance();
const onClickSpy = jest.spyOn(instance, 'onClick').mockImplementation(() => {});

wrapper.find(UploadsManagerBP).prop('onItemCancel')('foo.pdf');

expect(onClickSpy).toHaveBeenCalledWith(item);
});

test('should call removeFileFromUploadQueue when onItemRemove is invoked', () => {
const wrapper = getWrapper({ enableModernizedUploads: true });
const item = {
name: 'foo.pdf',
extension: 'pdf',
progress: 0,
status: STATUS_COMPLETE,
file: { name: 'foo.pdf' },
};
wrapper.setState({ items: [item] });
const instance = wrapper.instance();
const removeSpy = jest.spyOn(instance, 'removeFileFromUploadQueue').mockImplementation(() => {});

wrapper.find(UploadsManagerBP).prop('onItemRemove')('foo.pdf');

expect(removeSpy).toHaveBeenCalledWith(item);
});

test('should no-op when modernized id does not match any item', () => {
const wrapper = getWrapper({ enableModernizedUploads: true });
wrapper.setState({ items: [] });
const instance = wrapper.instance();
const onClickSpy = jest.spyOn(instance, 'onClick').mockImplementation(() => {});

wrapper.find(UploadsManagerBP).prop('onItemCancel')('missing-id');

expect(onClickSpy).not.toHaveBeenCalled();
});

test('should not crash when state contains a folder item without a file', () => {
const wrapper = getWrapper({ enableModernizedUploads: true, rootFolderId: '0' });
const folderItem = {
name: 'my-folder',
extension: '',
progress: 0,
status: STATUS_PENDING,
isFolder: true,
api: {},
};
wrapper.setState({ items: [folderItem] });

expect(() => wrapper.find(UploadsManagerBP).prop('items')).not.toThrow();
const items = wrapper.find(UploadsManagerBP).prop('items');
expect(items).toHaveLength(1);
expect(items[0]).toMatchObject({ name: 'my-folder', isFolder: true });
});

test('should resolve folder item handler via modernized id', () => {
const wrapper = getWrapper({ enableModernizedUploads: true, rootFolderId: '0' });
const folderItem = {
name: 'my-folder',
extension: '',
progress: 0,
status: STATUS_PENDING,
isFolder: true,
api: {},
};
wrapper.setState({ items: [folderItem] });
const instance = wrapper.instance();
const onClickSpy = jest.spyOn(instance, 'onClick').mockImplementation(() => {});

const folderId = wrapper.find(UploadsManagerBP).prop('items')[0].id;
wrapper.find(UploadsManagerBP).prop('onItemCancel')(folderId);

expect(onClickSpy).toHaveBeenCalledWith(folderItem);
});
});
});
Expand Down
Loading
Loading