Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Avatar and ImageInput components #922

Merged
merged 36 commits into from
Jun 3, 2021
Merged
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
90c4ea6
Create draft Avatar and ImageInput components
May 6, 2021
6e6b5b3
Add SVG placeholders for object and identity variants
May 6, 2021
ac2b34d
Fix linting
May 6, 2021
31fc08b
Update Avatar prop names
May 6, 2021
2ec6862
Wrap Avatar in label in ImageInput instead of passing an as prop
May 10, 2021
42a0ca2
Add icons and logic for removing an image
May 11, 2021
4fcd7a0
Clear file input via ref when the clear button is clicked
May 20, 2021
7ad234b
Add @jsxRuntime pragma to enable usage with React 17
May 20, 2021
d074661
Fix focus state UI bug where the outline appeared behind the ActionBu…
May 20, 2021
2a7d67f
Add active state styles
May 20, 2021
1b96826
Update Avatar docs
May 20, 2021
b93b5ac
Switch Avatar default variant to object
May 21, 2021
bceaece
Add onChange and onClear callbacks with loading and error states
May 25, 2021
0b37bad
Add snapshot tests for Avatar
May 25, 2021
dbb4f8c
Fix types in ImageInput spec to pass CI
May 25, 2021
48fa172
Switch from brightness() to pseudo-elements and clean up styles
May 25, 2021
52f3f0f
Do not call onChange with a falsy file param
May 25, 2021
6817573
Update ImageInput docs
May 25, 2021
a9716ca
Add initial tests for ImageInput
May 25, 2021
e162fe9
Add business logic tests for ImageInput
May 26, 2021
4c0a39e
Rename ImageInput into AvatarInput
May 26, 2021
40f10de
Add test for the logic of clearing an uploaded avatar
May 26, 2021
a73ffc4
Properly export the components
May 27, 2021
b729518
Add pointer-events:none to spinner to fix FF loading UI
May 27, 2021
018f6bf
Address code review feedback
May 27, 2021
c2cf4cf
Optimize SVGs
May 27, 2021
7b825bb
Batched improvements (see commit description)
May 28, 2021
a70e608
Rename AvatarInput back to ImageInput
May 28, 2021
1a6fe89
Move invalid border to inset
May 31, 2021
10d54aa
Move add button out of label element
May 31, 2021
7327509
Add snapshot test for rendering with a custom component
May 31, 2021
0ae1158
Write docs
May 31, 2021
5950ee9
Add changeset
May 31, 2021
5727c31
Address PR review comments
Jun 2, 2021
1d4c229
Make the Avatar's alt prop required
Jun 3, 2021
eb3121e
Update Avatar spec with required alt prop
Jun 3, 2021
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
Prev Previous commit
Next Next commit
Address code review feedback
Robin Metral committed May 27, 2021
commit 018f6bf9d02df79f9df0f5f1e1749d07ddba922d
2 changes: 1 addition & 1 deletion packages/circuit-ui/components/Avatar/Avatar.docs.mdx
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import { Status, Props, Story } from '../../../../.storybook/components';

<Status.Stable />

The Avatar component is used to display an avatar or an object image. It is used internally by the AvatarInput component, to allow users to upload images.
The Avatar component is used to display an avatar or an object image. It can be replaced by an [AvatarInput](Components/AvatarInput) to allow users to upload avatars.

<Story id="components-avatar--base" />

16 changes: 5 additions & 11 deletions packages/circuit-ui/components/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
@@ -26,22 +26,16 @@ export default {
parameters: {
docs: { page: docs },
},
argTypes: {
src: { control: 'text' },
alt: { control: 'text' },
variant: { control: { type: 'radio', options: ['object', 'identity'] } },
size: { control: { type: 'radio', options: ['yotta', 'giga'] } },
},
};

export const base = (args: AvatarProps): JSX.Element => <Avatar {...args} />;
base.args = {
export const Base = (args: AvatarProps): JSX.Element => <Avatar {...args} />;
Base.args = {
src: 'https://source.unsplash.com/EcWFOYOpkpY/200x200',
variant: 'object',
size: 'yotta',
};

export const object = (): JSX.Element => (
export const ObjectVariant = (): JSX.Element => (
<Stack>
<Avatar
src="https://source.unsplash.com/EcWFOYOpkpY/200x200"
@@ -51,7 +45,7 @@ export const object = (): JSX.Element => (
</Stack>
);

export const identity = (): JSX.Element => (
export const IdentityVariant = (): JSX.Element => (
<Stack>
<Avatar
src="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
@@ -61,7 +55,7 @@ export const identity = (): JSX.Element => (
</Stack>
);

export const sizes = (): JSX.Element => (
export const Sizes = (): JSX.Element => (
<Stack>
<Stack>
<Avatar
8 changes: 4 additions & 4 deletions packages/circuit-ui/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -21,8 +21,8 @@ import styled, { StyleProps } from '../../styles/styled';

export interface AvatarProps extends HTMLAttributes<HTMLImageElement> {
/**
* The source of Avatar image.
* Defaults to a placeholder.
* The source URL of the Avatar image.
* Defaults to a placeholder illustration.
*/
src?: string;
/**
@@ -32,11 +32,11 @@ export interface AvatarProps extends HTMLAttributes<HTMLImageElement> {
alt?: string;
/**
* The variant of the Avatar, either identity or object. Refer to the docs for usage guidelines.
* The variant also changes which placeholder is rendered when an src prop is not provided.
* The variant also changes which placeholder is rendered when the `src` prop is not provided.
*/
variant?: 'object' | 'identity';
/**
* One of two available sizes for the Avatar.
* One of two available sizes for the Avatar, either giga or yotta.
*/
size?: 'giga' | 'yotta';
}
Original file line number Diff line number Diff line change
@@ -6,13 +6,13 @@ import { Status, Props, Story } from '../../../../.storybook/components';

The AvatarInput component allows users to upload avatar images. It can be used on its own or as part of a form.

<Story id="components-AvatarInput--base" />
<Story id="components-avatarinput--base" />

<Props />

## Usage guidelines

- **Do** use a short and concise alert message. Consider the maximum amount of characters in one line (50-60, including spaces) for an optimal readability.
- **Do not** use the AvatarInput component if the avatar is read-only for the user, use the Avatar component instead.
- **Do not** use the AvatarInput component if the avatar is read-only for the user, use the [Avatar](Components/Avatar) component instead.

## (TBD)
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ describe('AvatarInput', () => {
it('should render with an existing image', () => {
const { container } = renderAvatarInput({
...defaultProps,
imageUrl: 'https://source.unsplash.com/EcWFOYOpkpY/200x200',
src: 'https://source.unsplash.com/EcWFOYOpkpY/200x200',
});
expect(container).toMatchSnapshot();
});
@@ -95,7 +95,7 @@ describe('AvatarInput', () => {
<AvatarInput
label="Upload an image"
clearButtonLabel="Clear"
imageUrl={imageUrl}
src={imageUrl}
onChange={onChange}
onClear={onClear}
invalid={!!error}
Original file line number Diff line number Diff line change
@@ -30,34 +30,34 @@ export default {
},
};

export const base = (args: AvatarInputProps): JSX.Element => (
export const Base = (args: AvatarInputProps): JSX.Element => (
<AvatarInput {...args} />
);

base.args = {
Base.args = {
label: 'Upload an image',
clearButtonLabel: 'Clear',
onChange: () => Promise.resolve(),
onClear: () => {},
loadingLabel: 'Uploading',
};

export const withImage = (): JSX.Element => (
export const WithImage = (): JSX.Element => (
<AvatarInput
label="Upload an image"
clearButtonLabel="Clear"
imageUrl="https://source.unsplash.com/EcWFOYOpkpY/200x200"
src="https://source.unsplash.com/EcWFOYOpkpY/200x200"
onChange={() => Promise.resolve()}
onClear={() => {}}
loadingLabel="Uploading"
/>
);

export const invalid = (): JSX.Element => (
export const Invalid = (): JSX.Element => (
<AvatarInput
label="Upload an image"
clearButtonLabel="Clear"
imageUrl="https://source.unsplash.com/EcWFOYOpkpY/200x200"
src="https://source.unsplash.com/EcWFOYOpkpY/200x200"
onChange={() => Promise.resolve()}
onClear={() => {}}
invalid={true}
@@ -107,7 +107,7 @@ export const Stateful = (): JSX.Element => {
<AvatarInput
label="Upload an image"
clearButtonLabel="Clear"
imageUrl={imageUrl}
src={imageUrl}
onChange={onChange}
onClear={onClear}
invalid={!!error}
14 changes: 7 additions & 7 deletions packages/circuit-ui/components/AvatarInput/AvatarInput.tsx
Original file line number Diff line number Diff line change
@@ -37,17 +37,17 @@ import { focusOutline, hideVisually } from '../../styles/style-mixins';
export interface AvatarInputProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'onChange'> {
/**
* A clear and concise description of the image input's purpose.
* A clear and concise description of the AvatarInput's purpose.
*/
label: string;
/**
* A unique identifier for the input element. If not defined, a generated id is used.
*/
id?: string;
/**
* An existing image URL to be displayed in the image input.
* The source URL of an existing Avatar to be displayed in the AvatarInput.
*/
imageUrl?: string;
src?: string;
/**
* An accessible label for the "clear" icon button.
*/
@@ -212,7 +212,7 @@ const LoadingLabel = styled.span(hideVisually);
*/
export const AvatarInput = ({
label,
imageUrl,
src,
id: customId,
clearButtonLabel,
onChange,
@@ -266,8 +266,8 @@ export const AvatarInput = ({
/>
<StyledLabel isLoading={isLoading} invalid={invalid} htmlFor={id}>
<span css={hideVisually()}>{label}</span>
<Avatar src={imageUrl || previewImage} />
{!imageUrl && (
<Avatar src={src || previewImage} />
{!src && (
<AddButton
type="button"
size="kilo"
@@ -295,7 +295,7 @@ export const AvatarInput = ({
</AddButton>
)}
</StyledLabel>
{imageUrl && (
{src && (
<ActionButton
type="button"
size="kilo"