Skip to content

Commit dc63ac6

Browse files
osama-rizkbobbor
andauthored
feat(storage-browser): file preview and file thumbnail (#6669)
* feat(storage-browser): Object preview * Add renderer resolver and image renderer * Add video preview component * Add text preview * Add un-supported preview view * Add main layout styling * Add file limit * Add preview placeholder loader * Add retry logic * Add typing for filePreview API * Add file meta data section * Update file metadata * update to handle composable case * Update file preview state logic * Update FilePreview naming to be consistent * Update state naming to be consistent * update renderer props type * Add file thumbnail * Add unit testing preview components and utils * Add tests for FilePreview component, controller and hook. Also utils and context * Update file preview styling and fix createProvider test * Update storage browser theming * Add new file grounds and update composable and default auth * Add display text for filePreview * Update composable * Update the preview layout. * update image styling * Add change set * Update storage browser playground * update file preview playground * Rename files utils directory * Update file type generic to avoid using AllFileTypes<any> * Update multiple function definition to be consistent * rename file preview callbacks (retryFilePreview,openFilePreview,closeFilePreview,) to be consistent * separate file icon and file preview logic * use unknown instead of any for file type generic resolver * Update content type sting processing * update file type logic * optimize useFilePreview hook callbacks using useCallback * Remove un-used code * Add video error handling * Update video return type * Add Image error handling * Update image, vide and metadata styling * Increase the storage size limit. * Update text preview component return value * Use PreviewFallback for Image, text and video error handling. * Update composable playground to use manged auth * Silence types temporarily * Update PreviewFallback component * update file size extraction * Update alt for text and video * Update display messages * Add exhaustive check for renderer * Update provide props * use existing file size formatter humanFileSize * removed null assertion from file size * Fix typo in url util name * fix DEFAULT_FILE_SIZE_LIMITS type * fix url import * Remove null assertion form file preview state * Update display text destruction * Update FileSizeResolver and * update file reducer * fix typescript issue * remove ts un-used code * Update PreviewFallback * Update text preview to use preview placeholder * Update file limit check logic * Make text content request cancellable * use snapshot in testing file preview display text * Update ariaLabel of file button * replace runtime exhaustiveCheck with build-time type safety * feat(object-preview) : update docs for the object preview feature (#6675) * Add new properties for LocationDetailViewState * Add file preview section in under Customization * Add file preview display text * Add file thumbnail icons * Update overview section to reference file preview capability * Update composition example to include File preview * Add file preview to CustomUI section * Add spacing for custom file preview comments * update ### File preview section description/overview * update overview description * feat(storage-browser): file preview e2e tests (#6672) * update file preview playground * Update video and un-supported tests * Update image, video and retry get by alt * update preview loader assertion message * remove redundant assertion. * use when instead of then for button click * update file row content * Storage browser/object preview bug bash issues (#6676) * close preview on search * Close preview on refresh and navigate * close preview on paginate. * Fix Clicking on a selected/active textfile reloads the content * Add unit tests for close file on actions and use file preview * preview do not fails if get properties fails * fix 404 * Add a test cases for opening a preview * Fix fileType infer issue * update get configuration logic * update preview error scenario. * Skip flaky e2e preview error * Fix flaky test in file preview error * Add mocking for file preview (#6679) * Add mocking for file preview * Fix file preview custom example * Update next config domains * Update mock comment * address feedback from stakeholders * fix file size limit test * remove unused code in text preview component * revert build command to use yarn --------- Co-authored-by: Philipp Andreas Paul <[email protected]>
1 parent 5bc366a commit dc63ac6

File tree

93 files changed

+4960
-108
lines changed

Some content is hidden

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

93 files changed

+4960
-108
lines changed

.changeset/dirty-cheetahs-fail.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@aws-amplify/ui-react-storage': minor
3+
'@aws-amplify/ui-react': minor
4+
'@aws-amplify/ui': minor
5+
---
6+
7+
feat(storage-browser): File preview and file thumbnail.

docs/next.config.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ function createRemarkMdxFrontmatterPlugin(options = {}) {
2525
}
2626

2727
module.exports = withNextPluginPreval({
28+
images: {
29+
domains: ['images.unsplash.com'],
30+
},
2831
env: {
2932
BRANCH,
3033
SITE_URL: process.env.SITE_URL,
@@ -205,6 +208,15 @@ module.exports = withNextPluginPreval({
205208
},
206209

207210
webpack(config) {
211+
// Add alias to mock storage-internal for storage browser examples
212+
config.resolve.alias = {
213+
...config.resolve.alias,
214+
'@aws-amplify/storage/internals': path.resolve(
215+
__dirname,
216+
'src/pages/[platform]/connected-components/storage/storage-browser/examples/mockStorageInternal.ts'
217+
),
218+
};
219+
208220
const defaultRehypePlugins = [
209221
// This is a custom plugin that removes lines that end in `// IGNORE`
210222
// This allows us to include code necessary for an example to run

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ComposedCreateFolderView } from './ComposedCreateFolderView';
77
import { ComposedDeleteView } from './ComposedDeleteView';
88
import { ComposedDownloadView } from './ComposedDownloadView';
99
import { ComposedUploadView } from './ComposedUploadView';
10+
import ComposedLocationDetailView from './ComposedLocationDetailView';
1011

1112
function LocationsView() {
1213
const state = useView('Locations');
@@ -80,7 +81,7 @@ function MyStorageBrowser() {
8081

8182
return (
8283
<>
83-
<StorageBrowser.LocationDetailView />
84+
<ComposedLocationDetailView />
8485
<dialog ref={ref}>
8586
<MyLocationActionView />
8687
</dialog>

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ function LocationDetailView() {
88
return (
99
<StorageBrowser.LocationDetailView.Provider {...state}>
1010
<Flex direction="row">
11+
<StorageBrowser.LocationDetailView.Pagination />
1112
<StorageBrowser.LocationDetailView.Refresh />
1213
<StorageBrowser.LocationDetailView.Search />
1314
</Flex>
14-
<StorageBrowser.LocationDetailView.LocationItemsTable />
15-
<StorageBrowser.LocationDetailView.Pagination />
15+
<Flex direction="column" position="relative">
16+
<StorageBrowser.LocationDetailView.LocationItemsTable />
17+
18+
<StorageBrowser.LocationDetailView.FilePreview />
19+
</Flex>
1620
</StorageBrowser.LocationDetailView.Provider>
1721
);
1822
}
1923

2024
export default function Example() {
21-
return (
22-
<StorageBrowser.Provider>
23-
<LocationDetailView />
24-
</StorageBrowser.Provider>
25-
);
25+
return <LocationDetailView />;
2626
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { CustomDeleteView } from './CustomDeleteView';
66
import { CustomDownloadView } from './CustomDownloadView';
77
import { CustomLocationsView } from './CustomLocationsView';
88
import { CustomUploadView } from './CustomUploadView';
9+
import CustomLocationDetailView from './CustomLocationDetailView';
910

1011
function MyLocationActionView() {
1112
const state = useView('LocationDetail');
@@ -40,7 +41,7 @@ function MyStorageBrowser() {
4041
return <MyLocationActionView />;
4142
}
4243

43-
return <StorageBrowser.LocationDetailView />;
44+
return <CustomLocationDetailView />;
4445
}
4546

4647
export default function Example() {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import Image from 'next/image';
3+
import { createStorageBrowser } from '@aws-amplify/ui-react-storage/browser';
4+
import { mockConfig } from './mockConfig'; // IGNORE
5+
import { defaultActions } from './defaultActions'; // IGNORE
6+
7+
const CustomImageRenderer = ({ fileData, url }: any) => (
8+
<div style={{ border: '3px solid gray' }}>
9+
<div>this is my custom image renderer</div>
10+
<Image src={url} alt={fileData?.key} width={500} height={300} />
11+
</div>
12+
);
13+
14+
const CustomAudioRenderer = ({ fileData, url }: any) => (
15+
<div
16+
style={{
17+
border: '3px solid #6f42c1',
18+
padding: '15px',
19+
borderRadius: '8px',
20+
}}
21+
>
22+
<h3>🎵 Audio Player</h3>
23+
<p>
24+
<strong>File:</strong> {fileData?.key}
25+
</p>
26+
<audio controls style={{ width: '100%' }}>
27+
<source src={url} />
28+
Your browser does not support the audio element.
29+
</audio>
30+
</div>
31+
);
32+
33+
const { StorageBrowser } = createStorageBrowser({
34+
config: mockConfig, // IGNORE
35+
actions: { default: defaultActions }, // IGNORE
36+
filePreview: {
37+
fileTypeResolver: (properties) => {
38+
if (properties.key.endsWith('txt')) return 'text';
39+
if (properties.key.endsWith('mp4')) return 'video';
40+
if (properties.key.endsWith('jpg')) return 'image';
41+
42+
// Audio files
43+
if (properties?.contentType?.startsWith('audio/')) return 'audio';
44+
45+
return undefined;
46+
},
47+
maxFileSize: (fileType) => {
48+
// 1GB preview limit for videos
49+
if (fileType == 'video') return 1000 * 1024 * 1024;
50+
},
51+
urlOptions: (fileType) => {
52+
// 3 hours expiration for the pre-signed url for videos
53+
if (fileType == 'video')
54+
return { expiresIn: 1000 * 60 * 60 * 3, validateObjectExistence: true };
55+
},
56+
rendererResolver: (fileType) => {
57+
// Add a new renderer for certain files types
58+
if (fileType === 'audio') return CustomAudioRenderer;
59+
60+
if (fileType == 'image') {
61+
// Override the default image renderer
62+
return CustomImageRenderer;
63+
}
64+
},
65+
},
66+
});
67+
68+
export default function Example() {
69+
return <StorageBrowser />;
70+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as React from 'react';
2+
import { Flex } from '@aws-amplify/ui-react';
3+
import { StorageBrowser, useView } from './StorageBrowser';
4+
import CustomFilePreview from './CustomUIFilePreview';
5+
6+
function LocationDetailView() {
7+
const state = useView('LocationDetail');
8+
const { filePreviewState, onSelectActiveFile } = state;
9+
10+
return (
11+
<StorageBrowser.LocationDetailView.Provider {...state}>
12+
<Flex direction="row">
13+
<StorageBrowser.LocationDetailView.Pagination />
14+
<StorageBrowser.LocationDetailView.Refresh />
15+
<StorageBrowser.LocationDetailView.Search />
16+
</Flex>
17+
<Flex direction="column" position="relative">
18+
<StorageBrowser.LocationDetailView.LocationItemsTable />
19+
20+
<CustomFilePreview
21+
filePreviewState={filePreviewState}
22+
onCloseFilePreview={() => onSelectActiveFile(undefined)}
23+
/>
24+
</Flex>
25+
</StorageBrowser.LocationDetailView.Provider>
26+
);
27+
}
28+
29+
export default function Example() {
30+
return <LocationDetailView />;
31+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { Button, Card, Flex, Text } from '@aws-amplify/ui-react';
3+
4+
export default function CustomFilePreview({
5+
filePreviewState,
6+
onCloseFilePreview,
7+
}) {
8+
const { previewedFile, url, isLoading, hasError } = filePreviewState;
9+
const { key, size } = previewedFile || {};
10+
11+
if (!previewedFile) return null;
12+
13+
if (isLoading) return <div>...loading</div>;
14+
if (hasError) return <div>...oops some thing bad happens</div>;
15+
16+
return (
17+
<Card padding="1rem" margin="1rem">
18+
<Flex direction="column" gap="0.5rem">
19+
<Flex justifyContent="space-between" alignItems="center">
20+
<Text fontSize="large" fontWeight="bold">
21+
Custom Preview: {key}
22+
</Text>
23+
<Button size="small" onClick={onCloseFilePreview}>
24+
Close
25+
</Button>
26+
</Flex>
27+
28+
<Text color="gray">Size: {size} bytes</Text>
29+
30+
{url ? (
31+
<Text color="green">✓ Preview URL ready</Text>
32+
) : (
33+
<Text color="orange">⏳ Loading preview...</Text>
34+
)}
35+
</Flex>
36+
</Card>
37+
);
38+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ export default function Example() {
1111
// Some are a function that return a string
1212
getPermissionName: (permissions) => permissions.join('/'),
1313
},
14+
LocationDetailView: {
15+
searchSubfoldersToggleLabel: 'Search subfolders',
16+
filePreview: {
17+
filePreviewTitle: 'File Preview Title',
18+
closeButtonLabel: 'Close Preview',
19+
},
20+
},
1421
}}
1522
/>
1623
);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
FcPrevious,
88
FcRefresh,
99
FcSearch,
10+
FcDocument,
11+
FcImageFile,
12+
FcFile,
1013
} from 'react-icons/fc';
1114
import { StorageBrowser } from './StorageBrowser'; // IGNORE
1215
import { IconsProvider } from '@aws-amplify/ui-react';
@@ -20,6 +23,9 @@ export default function Example() {
2023
'sort-indeterminate': <FcMinus />,
2124
'sort-ascending': <FcAlphabeticalSortingAz />,
2225
'sort-descending': <FcAlphabeticalSortingZa />,
26+
'file-pdf': <FcDocument />,
27+
'file-text': <FcFile />,
28+
'file-image': <FcImageFile />,
2329
},
2430
searchField: {
2531
search: <FcSearch />,

0 commit comments

Comments
 (0)