Skip to content

Commit 70e0d1e

Browse files
sobermashika112tiffanynwyeung
authored
feat(storage-browser): Multi File Download (#6620)
* update workflow * feat(storage-browser): Multi File Download (#6553) * download multiple view * cleanup download resolvers * lint fix * add unit tests * add integ test * Create brave-queens-clean.md * update broken tests * chore(storage-browser): add multi-file download docs examples * fix: replace URL created from empty string to `null`, add wording changes * chore: revert workflows used for testing --------- Co-authored-by: ashika112 <[email protected]> Co-authored-by: ashika112 <[email protected]> Co-authored-by: Tiffany Yeung <[email protected]>
1 parent e231429 commit 70e0d1e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1396
-79
lines changed

.changeset/brave-queens-clean.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@aws-amplify/ui-react-storage": minor
3+
---
4+
5+
feat(storage-browser): Multi File Download

docs/src/pages/[platform]/connected-components/storage/storage-browser/examples/Composed.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { StorageBrowser, useView } from './StorageBrowser';
55
import { ComposedCopyView } from './ComposedCopyView';
66
import { ComposedCreateFolderView } from './ComposedCreateFolderView';
77
import { ComposedDeleteView } from './ComposedDeleteView';
8+
import { ComposedDownloadView } from './ComposedDownloadView';
89
import { ComposedUploadView } from './ComposedUploadView';
910

1011
function LocationsView() {
@@ -52,6 +53,8 @@ function MyLocationActionView() {
5253
return <ComposedCreateFolderView onExit={onExit} />;
5354
case 'delete':
5455
return <ComposedDeleteView onExit={onExit} />;
56+
case 'download':
57+
return <ComposedDownloadView onExit={onExit} />;
5558
case 'upload':
5659
return <ComposedUploadView onExit={onExit} />;
5760
default:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react';
2+
import { StorageBrowser, useView } from './StorageBrowser';
3+
4+
export function ComposedDownloadView({ onExit }: { onExit: () => void }) {
5+
const state = useView('Download');
6+
7+
return (
8+
<StorageBrowser.DownloadView.Provider {...state} onActionExit={onExit}>
9+
<StorageBrowser.DownloadView.Exit />
10+
<StorageBrowser.DownloadView.TasksTable />
11+
<StorageBrowser.DownloadView.Start />
12+
<StorageBrowser.DownloadView.Message />
13+
</StorageBrowser.DownloadView.Provider>
14+
);
15+
}

docs/src/pages/[platform]/connected-components/storage/storage-browser/examples/Custom.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as React from 'react';
22
import { StorageBrowser, useView } from './StorageBrowser';
3-
import { CustomDeleteView } from './CustomDeleteView';
43
import { CustomCopyView } from './CustomCopyView';
54
import { CustomCreateFolderView } from './CustomCreateFolderView';
6-
import { CustomUploadView } from './CustomUploadView';
5+
import { CustomDeleteView } from './CustomDeleteView';
6+
import { CustomDownloadView } from './CustomDownloadView';
77
import { CustomLocationsView } from './CustomLocationsView';
8+
import { CustomUploadView } from './CustomUploadView';
89

910
function MyLocationActionView() {
1011
const state = useView('LocationDetail');
@@ -19,6 +20,8 @@ function MyLocationActionView() {
1920
return <CustomCreateFolderView onExit={onExit} />;
2021
case 'delete':
2122
return <CustomDeleteView onExit={onExit} />;
23+
case 'download':
24+
return <CustomDownloadView onExit={onExit} />;
2225
case 'upload':
2326
return <CustomUploadView onExit={onExit} />;
2427
default:

docs/src/pages/[platform]/connected-components/storage/storage-browser/examples/CustomDeleteView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import { Button, Flex, Text } from '@aws-amplify/ui-react';
3-
import { StorageBrowser, useView } from './StorageBrowser';
3+
import { useView } from './StorageBrowser';
44

55
export function CustomDeleteView({ onExit }: { onExit: () => void }) {
66
const state = useView('Delete');
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as React from 'react';
2+
import { Button, Flex, Text, View } from '@aws-amplify/ui-react';
3+
import { useView } from './StorageBrowser';
4+
5+
export function CustomDownloadView({ onExit }: { onExit: () => void }) {
6+
const state = useView('Download');
7+
8+
return (
9+
<Flex direction="column">
10+
<Button variation="link" alignSelf="flex-start" onClick={onExit}>
11+
Exit
12+
</Button>
13+
{state.tasks.map((task) => (
14+
<Flex key={task.data.key} direction="row">
15+
<Text>{task.data.key}</Text>
16+
</Flex>
17+
))}
18+
<Button onClick={state.onActionStart}>
19+
Download {state.tasks.length} files
20+
</Button>
21+
</Flex>
22+
);
23+
}

docs/src/pages/[platform]/connected-components/storage/storage-browser/examples/defaultActions.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
CreateStorageBrowserInput,
33
DeleteHandlerOutput,
4+
DownloadHandlerOutput,
45
UploadHandlerOutput,
56
} from '@aws-amplify/ui-react-storage/browser';
67
import uniqueId from 'lodash/uniqueId';
@@ -59,13 +60,27 @@ export const defaultActions: CreateStorageBrowserInput['actions']['default'] = {
5960
},
6061
viewName: 'DeleteView',
6162
},
62-
download: () => {
63-
return {
64-
result: Promise.resolve({
65-
status: 'COMPLETE' as const,
66-
value: { url: new URL('') },
67-
}),
68-
};
63+
download: {
64+
actionListItem: {
65+
icon: 'download',
66+
label: 'Download',
67+
},
68+
handler: () => {
69+
const result: DownloadHandlerOutput['result'] = new Promise((resolve) => {
70+
setTimeout(() => {
71+
resolve({
72+
status: 'COMPLETE' as const,
73+
// creating URL with empty string will throw TypeError,
74+
// preventing promise from resolving
75+
value: { url: null },
76+
});
77+
}, 500);
78+
});
79+
return {
80+
result,
81+
};
82+
},
83+
viewName: 'DownloadView',
6984
},
7085
upload: {
7186
actionListItem: {

docs/src/pages/[platform]/connected-components/storage/storage-browser/react.mdx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ Below you wll find examples of how to compose each view. See [View reference](#v
362362
<Tabs.Item value="Locations">Locations</Tabs.Item>
363363
<Tabs.Item value="LocationDetail">Location Detail</Tabs.Item>
364364
<Tabs.Item value="Upload">Upload</Tabs.Item>
365+
<Tabs.Item value="Download">Download</Tabs.Item>
365366
<Tabs.Item value="Copy">Copy</Tabs.Item>
366367
<Tabs.Item value="CreateFolder">CreateFolder</Tabs.Item>
367368
<Tabs.Item value="Delete">Delete</Tabs.Item>
@@ -396,6 +397,12 @@ Below you wll find examples of how to compose each view. See [View reference](#v
396397
```
397398
</ExampleCode>
398399
</Tabs.Panel>
400+
<Tabs.Panel value="Download">
401+
<ExampleCode>
402+
```jsx file=./examples/ComposedDownloadView.tsx
403+
```
404+
</ExampleCode>
405+
</Tabs.Panel>
399406
<Tabs.Panel value="Copy">
400407
<ExampleCode>
401408
```jsx file=./examples/ComposedCopyView.tsx
@@ -415,7 +422,7 @@ Below you wll find examples of how to compose each view. See [View reference](#v
415422

416423
The `createStorageBrowser` function returns a `useView()` hook that lets you use the Storage Browser's internal state and event handlers to build your own UI on top the Storage Browser functionality.
417424

418-
The `useView()` hook takes a single argument which is the name of the view you want to get the UI state and event handlers for. The available views are `Locations`, `LocationDetails`, `Copy`, `Upload`, `Delete`, and `CreateFolder`. The return value of the hook will have all the necessary internal state and event handlers required to build that view. In fact, the Storage Browser component itself uses the `useView()` to manage its state, so you can build the UI exactly how we do!
425+
The `useView()` hook takes a single argument which is the name of the view you want to get the UI state and event handlers for. The available views are `Locations`, `LocationDetails`, `Copy`, `Upload`, `Download`, `Delete`, and `CreateFolder`. The return value of the hook will have all the necessary internal state and event handlers required to build that view. In fact, the Storage Browser component itself uses the `useView()` to manage its state, so you can build the UI exactly how we do!
419426

420427
<Example>
421428
<Custom />
@@ -424,6 +431,7 @@ The `useView()` hook takes a single argument which is the name of the view you w
424431
<Tabs.Item value="StorageBrowser">StorageBrowser</Tabs.Item>
425432
<Tabs.Item value="Locations">Locations</Tabs.Item>
426433
<Tabs.Item value="Upload">Upload</Tabs.Item>
434+
<Tabs.Item value="Download">Download</Tabs.Item>
427435
<Tabs.Item value="Copy">Copy</Tabs.Item>
428436
<Tabs.Item value="CreateFolder">CreateFolder</Tabs.Item>
429437
<Tabs.Item value="Delete">Delete</Tabs.Item>
@@ -457,6 +465,12 @@ The `useView()` hook takes a single argument which is the name of the view you w
457465
```jsx file=./examples/CustomUploadView.tsx
458466
```
459467
</ExampleCode>
468+
</Tabs.Panel>
469+
<Tabs.Panel value="Download">
470+
<ExampleCode>
471+
```jsx file=./examples/CustomDownloadView.tsx
472+
```
473+
</ExampleCode>
460474
</Tabs.Panel>
461475
<Tabs.Panel value="Delete">
462476
<ExampleCode>
@@ -479,7 +493,7 @@ The following `default` actions are invoked by the default UI views:
479493
* `copy`: Copy data to another location within a `bucket` or `prefix` in [Copy action view](#copy-view).
480494
* `createFolder`: Create a folder within a `bucket` or `prefix` in [CreateFolder action view](#createfolder-view).
481495
* `delete`: Delete a given object in [Delete action view](#delete-view).
482-
* `download`: Download a given object in [LocationDetails view](#locationdetails-view) using an [anchor link](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download).
496+
* `download`: Download a given object in [LocationDetails view](#locationdetails-view) using an [anchor link](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download) or in [Download action view](#download-view).
483497
* `upload`: Upload a file in [Upload action view](#upload-view).
484498

485499
`defaultActionConfigs` allows access to the default implementation of these actions. Below is an example to instrument the default upload action to count the files being uploaded.
@@ -824,6 +838,36 @@ interface UploadViewState {
824838
* `<StorageBrowser.UploadView.Title />`
825839

826840

841+
### Download view
842+
843+
#### Download view state
844+
845+
```ts
846+
interface DownloadViewState {
847+
isProcessing: boolean;
848+
isProcessingComplete: boolean;
849+
location: LocationState;
850+
onActionCancel: () => void;
851+
onActionExit: () => void;
852+
onActionStart: () => void;
853+
onTaskRemove?: (task: Task<DeleteHandlerData>) => void;
854+
statusCounts: StatusCounts;
855+
tasks: Task<DeleteHandlerData>[];
856+
}
857+
```
858+
859+
#### Download view components
860+
861+
* `<StorageBrowser.DownloadView.Provider />`
862+
* `<StorageBrowser.DownloadView.Cancel />`
863+
* `<StorageBrowser.DownloadView.Exit />`
864+
* `<StorageBrowser.DownloadView.Message />`
865+
* `<StorageBrowser.DownloadView.Start />`
866+
* `<StorageBrowser.DownloadView.Statuses />`
867+
* `<StorageBrowser.DownloadView.TasksTable />`
868+
* `<StorageBrowser.DownloadView.Title />`
869+
870+
827871
### Copy view
828872

829873
#### Copy view state

packages/e2e/features/ui/components/storage/storage-browser/action-menu.feature

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ Feature: Create folder with Storage Browser
7474
When I click the "Exit" button
7575
# list uploaded file
7676
Then I see "1" files with random names
77+
# download file
78+
Then I click checkbox for with "1" files with random names
79+
When I click the "Menu Toggle" button
80+
Then I click the "Download" menuitem
81+
Then I click the "Download" button
82+
Then I see "All files downloaded"
83+
When I click the "Exit" button
7784
# copy file
7885
Then I click checkbox for with "1" files with random names
7986
When I click the "Menu Toggle" button
@@ -174,3 +181,12 @@ Feature: Create folder with Storage Browser
174181
When A network failure occurs
175182
Then I click the "Delete" button
176183
Then I see "All files failed to delete"
184+
@react
185+
Scenario: Download file shows a Network error if offline
186+
When I click the first button containing "public"
187+
Then I click checkbox for file "001_dont_delete_file.txt"
188+
When I click the "Menu Toggle" button
189+
Then I click the "Download" menuitem
190+
When A network failure occurs
191+
Then I click the "Download" button
192+
Then I see "All files failed to download"

packages/react-storage/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"name": "createStorageBrowser",
6767
"path": "dist/esm/browser.mjs",
6868
"import": "{ createStorageBrowser }",
69-
"limit": "64.5 kB",
69+
"limit": "64.6 kB",
7070
"ignore": [
7171
"@aws-amplify/storage"
7272
]
@@ -75,7 +75,7 @@
7575
"name": "StorageBrowser",
7676
"path": "dist/esm/index.mjs",
7777
"import": "{ StorageBrowser }",
78-
"limit": "87 kB"
78+
"limit": "88 kB"
7979
},
8080
{
8181
"name": "FileUploader",

0 commit comments

Comments
 (0)