Skip to content

Commit

Permalink
chore(ui-react-storage): add docs and e2e tests for StorageImage (#4262)
Browse files Browse the repository at this point in the history
* test(react-storage): add e2e tests for StorageImage (#4253)

* docs: add docs for StorageImage (#4259)

* Create stale-dancers-flash.md

* test: update sitemap snapshot
  • Loading branch information
zchenwei committed Jul 19, 2023
1 parent e4bf312 commit aea82ff
Show file tree
Hide file tree
Showing 23 changed files with 373 additions and 39 deletions.
14 changes: 14 additions & 0 deletions .changeset/stale-dancers-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@aws-amplify/ui-react-storage": minor
"@aws-amplify/ui-react": patch
"@aws-amplify/ui": patch
---

feat(ui-react-storage): Add a new connected component `StorageImage`. It allows you load the images managed by Amplify Storage.

*Example:*
```jsx
import { StorageImage } from '@aws-amplify/ui-react-storage';

<StorageImage alt="StorageImage" imgKey="image.jpg" accessLevel="public" />
```
7 changes: 7 additions & 0 deletions docs/src/data/links.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ export const connectedComponents: ComponentNavItem[] = [
body: "Amplify UI Storage components allow you to store files in the cloud using Amplify's Storage category",
platforms: ['react'],
},
{
href: '/connected-components/storage/storageimage',
label: 'Storage Image',
body: 'StorageImage component allows users to load an image managed by Amplify Storage.',
platforms: ['react'],
tertiary: true,
},
{
href: '/connected-components/storage/storagemanager',
label: 'Storage Manager',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { StorageImage } from '@aws-amplify/ui-react-storage';

export const DefaultStorageImageExample = () => {
return <StorageImage alt="cat" imgKey="cat.jpg" accessLevel="public" />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { StorageImage } from '@aws-amplify/ui-react-storage';

export const StorageImageErrorHandlingExample = () => {
return (
<StorageImage
alt="fallback cat"
imgKey="cat.jpg"
accessLevel="public"
fallbackSrc="/fallback_cat.jpg"
onStorageGetError={(error) => console.error(error)}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { DefaultStorageImageExample } from './DefaultStorageImageExample';
export { StorageImageErrorHandlingExample } from './StorageImageErrorHandlingExample';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: Storage Image
description: The Storage Image lets you load images from Amplify Storage.
reactSource: packages/react-storage/src/components/StorageImage/StorageImage.tsx
supportedFrameworks: react
---

import { Fragment } from '@/components/Fragment';
import { getCustomStaticPath } from "@/utils/getCustomStaticPath";

export async function getStaticPaths() {
return getCustomStaticPath(frontmatter.supportedFrameworks);
}

{/* `getStaticProps` is required to prevent "Error: getStaticPaths was added without a getStaticProps. Without getStaticProps, getStaticPaths does nothing" */}

export async function getStaticProps() {
return { props: {} }
}

<Fragment>{({ platform }) => import(`./${platform}.mdx`)}</Fragment>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export const STORAGE_IMAGE = [
{
name: 'alt',
description: 'Alternative text description of the image',
type: 'string',
},
{
name: 'imgKey',
description: 'The key of an image.',
type: 'string',
},
{
name: 'accessLevel',
description:
'Access level for files in Storage. See https://docs.amplify.aws/lib/storage/configureaccess/q/platform/js/',
type: `'public' | 'private' | 'protected'`,
},
{
name: 'identityId?',
description:
'The unique Amazon Cognito Identity ID of the image owner. Required when loading a protected image.',
type: 'string',
},
{
name: 'fallbackSrc?',
description:
'A fallback image source to be loaded when the component fails to load the image from Storage',
type: 'string',
},
{
name: 'onStorageGetError?',
description: 'Triggered when an error happens calling Storage.get',
type: `(error: Error) => void;`,
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Alert, Image } from '@aws-amplify/ui-react';

import { ComponentStyleDisplay } from '@/components/ComponentStyleDisplay';
import { Example, ExampleCode } from '@/components/Example';
import { Fragment } from '@/components/Fragment';
import { AppDirectoryAlert } from '@/components/AppDirectoryAlert';
import { InstallScripts } from '@/components/InstallScripts';
import ReactPropsTable from '@/components/propsTable/ReactPropsTable';
import { STORAGE_IMAGE } from './props';
import {
DefaultStorageImageExample,
StorageImageErrorHandlingExample
} from './examples'

## Basic Usage

<Alert variation="warning" heading="Wait!">
Did you follow the [quick start instructions](/connected-components/storage#quick-start) to set up the storage and auth services?
</Alert>

<Fragment platforms={['react']}>
{({ platform }) => import('@/components/AppDirectoryAlert')}
</Fragment>

To use the StorageImage component, import it into your React application with the included styles.

<InstallScripts component="storage" />

<ExampleCode>
```js
import { StorageImage } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
```
</ExampleCode>

At a minimum you must include the `alt`, `imgKey` and `accessLevel` props. `accessLevel` refers to the [Amplify Storage access level](https://docs.amplify.aws/lib/storage/configureaccess/q/platform/js/), which is `'public' | 'private' | 'protected'`.

<Example>
<Image alt='cat' src='/cats/1.jpg' width="400px" height="400px" />
<ExampleCode>
```jsx file=./examples/DefaultStorageImageExample.tsx
```
</ExampleCode>
</Example>

## Props

<ReactPropsTable props={STORAGE_IMAGE} />

Note: A new `Storage.get` request is made only when the `imgKey` changes.

## Error Handling

To handle the error caused by `Storage.get`, you can pass a `onStorageGetError` handler and optionally provide a `fallbackSrc` for the component to load a fallback image.

<Example>
<Image alt='fallback cat' src='/cats/2.jpg' width="400px" height="400px" />
<ExampleCode>
```jsx file=./examples/StorageImageErrorHandlingExample.tsx
```
</ExampleCode>
</Example>

## Customization

### Target Classes
<ComponentStyleDisplay componentName="StorageImage" />
1 change: 1 addition & 0 deletions docs/tests/__snapshots__/sitemap.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ exports[`Sitemap Snapshot 1`] = `
/react/connected-components/liveness/troubleshooting,
/react/connected-components/storage,
/react/connected-components/storage/fileuploader,
/react/connected-components/storage/storageimage,
/react/connected-components/storage/storagemanager,
/react/getting-started/accessibility,
/react/getting-started/figma,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/storage/file-uploader/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';

import { Amplify } from 'aws-amplify';
import {
Button,
Text,
Loader,
useAuthenticator,
withAuthenticator,
} from '@aws-amplify/ui-react';
import { StorageImage } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
import awsExports from './aws-exports';

Amplify.configure(awsExports);

export function StorageImageExample() {
const [isLoaded, setIsLoaded] = React.useState(false);
const { signOut } = useAuthenticator((context) => [context.signOut]);

const onLoad = () => {
setIsLoaded(true);
};

return (
<>
<StorageImage
alt="private cat"
imgKey="private-e2e.jpeg"
accessLevel="private"
onLoad={onLoad}
/>
{isLoaded ? (
<Text>The private image is loaded.</Text>
) : (
<Loader testId="Loader" />
)}
<Button onClick={signOut}>Sign out</Button>
</>
);
}
export default withAuthenticator(StorageImageExample);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/storage/file-uploader/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';

import { Amplify } from 'aws-amplify';
import {
Button,
Text,
Loader,
useAuthenticator,
withAuthenticator,
} from '@aws-amplify/ui-react';
import { StorageImage } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
import awsExports from './aws-exports';

Amplify.configure(awsExports);

export function StorageImageExample() {
const [isLoaded, setIsLoaded] = React.useState(false);
const { signOut } = useAuthenticator((context) => [context.signOut]);

const onLoad = () => {
setIsLoaded(true);
};

return (
<>
<StorageImage
alt="protected cat"
imgKey="protected-e2e.jpeg"
accessLevel="protected"
onLoad={onLoad}
/>
{isLoaded ? (
<Text>The protected image is loaded.</Text>
) : (
<Loader testId="Loader" />
)}
<Button onClick={signOut}>Sign out</Button>
</>
);
}
export default withAuthenticator(StorageImageExample);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/storage/file-uploader/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';

import { Amplify } from 'aws-amplify';
import { Text, Loader } from '@aws-amplify/ui-react';
import { StorageImage } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
import awsExports from './aws-exports';

Amplify.configure(awsExports);

export function StorageImageExample() {
const [isLoaded, setIsLoaded] = React.useState(false);

const onLoad = () => {
setIsLoaded(true);
};

return (
<>
<StorageImage
alt="public cat"
imgKey="public-e2e.jpeg"
accessLevel="public"
onLoad={onLoad}
/>
{isLoaded ? (
<Text>The public image is loaded.</Text>
) : (
<Loader testId="Loader" />
)}
</>
);
}
export default StorageImageExample;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Load an image from S3 with private access level settings

Background:
Given I'm running the example "ui/components/storage/storage-image/private-access-level"

@react
Scenario: I successfully load a private image
When I type my "email" with status "CONFIRMED"
And I type my password
And I click the "Sign in" button
Then I see "Loader" element
Then I see the "private cat" image
Then I see "The private image is loaded."
Then I see "Sign out"
Then I click "Sign out"
Then I see "Sign in"
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Load an image from S3 with protected access level settings

Background:
Given I'm running the example "ui/components/storage/storage-image/protected-access-level"

@react
Scenario: I successfully load a protected image
When I type my "email" with status "CONFIRMED"
And I type my password
And I click the "Sign in" button
Then I see "Loader" element
Then I see the "protected cat" image
Then I see "The protected image is loaded."
Then I see "Sign out"
Then I click "Sign out"
Then I see "Sign in"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Load an image from S3 with public access level settings

Background:
Given I'm running the example "ui/components/storage/storage-image/public-access-level"

@react
Scenario: I successfully load a public image
Then I see "Loader" element
Then I see the "public cat" image
Then I see "The public image is loaded."
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,16 @@ export const StorageImage = ({
...rest
}: StorageImageProps): JSX.Element => {
const options = React.useMemo(
() => ({
accessLevel,
identityId,
}),
() => ({ level: accessLevel, identityId }),
[accessLevel, identityId]
);

const errorConfig = React.useMemo(
() => ({
fallbackURL: fallbackSrc,
onStorageGetError,
}),
[fallbackSrc, onStorageGetError]
);

const url = useStorageURL(imgKey, options, errorConfig);
const url = useStorageURL({
key: imgKey,
options,
fallbackURL: fallbackSrc,
onStorageGetError,
});

return (
<Image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ImageProps } from '@aws-amplify/ui-react';
export interface StorageImageProps extends Omit<ImageProps, 'src'> {
// Use imgKey instead of key because key is a reserved keyword
// and cannot be accessed via props in React components
// Note: a new Storage.get request is made only when the imgKey gets updated after the initial
imgKey: string;
accessLevel: StorageAccessLevel;
identityId?: string;
Expand Down
Loading

0 comments on commit aea82ff

Please sign in to comment.