Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wrap Avatar in label in ImageInput instead of passing an as prop
Browse files Browse the repository at this point in the history
Robin Metral committed May 10, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent b5426d9 commit e69cfb4
Showing 3 changed files with 67 additions and 50 deletions.
22 changes: 14 additions & 8 deletions packages/circuit-ui/components/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
@@ -27,15 +27,17 @@ export default {
docs: { page: docs },
},
argTypes: {
imageUrl: { control: 'text' },
src: { control: 'text' },
alt: { control: 'text' },
variant: { control: { type: 'radio', options: ['business', 'person'] } },
size: { control: { type: 'radio', options: ['yotta', 'giga'] } },
},
};

export const base = (args: AvatarProps): JSX.Element => <Avatar {...args} />;
base.args = {
imageUrl: 'https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png',
src: 'https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png',
alt: '',
variant: 'person',
size: 'yotta',
};
@@ -45,12 +47,14 @@ export const sizes = (): JSX.Element => (
<Avatar
size="yotta"
variant="person"
imageUrl="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
src="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
alt=""
/>
<Avatar
size="giga"
variant="person"
imageUrl="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
src="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
alt=""
/>
</Stack>
);
@@ -59,18 +63,20 @@ export const identity = (): JSX.Element => (
<Stack>
<Avatar
variant="person"
imageUrl="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
src="https://upload.wikimedia.org/wikipedia/en/8/86/Avatar_Aang.png"
alt=""
/>
<Avatar variant="person" />
<Avatar variant="person" src="" alt="" />
</Stack>
);

export const object = (): JSX.Element => (
<Stack>
<Avatar
variant="business"
imageUrl="https://source.unsplash.com/EcWFOYOpkpY"
src="https://source.unsplash.com/EcWFOYOpkpY"
alt=""
/>
<Avatar variant="business" />
<Avatar variant="business" src="" alt="" />
</Stack>
);
68 changes: 36 additions & 32 deletions packages/circuit-ui/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -13,18 +13,21 @@
* limitations under the License.
*/

import { HTMLAttributes } from 'react';
import React, { HTMLAttributes } from 'react';
import { css } from '@emotion/core';
import isPropValid from '@emotion/is-prop-valid';

import styled, { StyleProps } from '../../styles/styled';

export interface AvatarProps
extends Omit<HTMLAttributes<HTMLDivElement>, 'size'> {
export interface AvatarProps extends HTMLAttributes<HTMLImageElement> {
/**
* The URL of the Avatar image
* The Avatar image source
*/
imageUrl?: string;
src?: string;
/**
* Alt text for the Avatar
*/
alt: string;
/**
* The variant of the Avatar, either representing a person or a business
*/
@@ -33,15 +36,6 @@ export interface AvatarProps
* The size of the Avatar
*/
size?: 'giga' | 'yotta';
/**
* Alternative DOM element to render
*/
as?: 'label';
/**
* htmlFor when the element is a label
* TODO the element should extend either a div or a label
*/
htmlFor?: string;
}

const avatarSizes = {
@@ -54,34 +48,44 @@ const placeholders = {
business: `<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M30 25C30 20.0294 34.0294 16 39 16C43.9706 16 48 20.0294 48 25C48 29.9706 43.9706 34 39 34C34.0294 34 30 29.9706 30 25Z" fill="white"/><path d="M41.1571 60.5691L30.6742 48.3905C29.0304 46.4808 26.0517 46.5483 24.496 48.5304L8 69.5483V81.9998C8 85.3135 10.6863 87.9998 14 87.9998H19.9592L41.1571 60.5691Z" fill="white"/><path d="M70.4856 32.878C72.0409 30.876 75.0425 30.8075 76.6876 32.7363L87.9996 45.9986V81.9986C87.9996 85.3123 85.3133 87.9986 81.9996 87.9986H27.6611L70.4856 32.878Z" fill="white"/></svg>`,
};

type StyledImageProps = Omit<AvatarProps, 'variant' | 'size'> & {
variant: 'person' | 'business';
size: 'giga' | 'yotta';
};

const baseStyles = ({
theme,
imageUrl,
variant = 'person',
size = 'yotta',
}: AvatarProps & StyleProps) => css`
variant,
size,
}: StyledImageProps & StyleProps) => css`
display: block;
width: ${avatarSizes[size]};
height: ${avatarSizes[size]};
box-shadow: inset 0 0 0 ${theme.borderWidth.kilo} rgba(0, 0, 0, 0.1);
box-shadow: 0 0 0 ${theme.borderWidth.kilo} rgba(0, 0, 0, 0.1);
background-color: ${theme.colors.n300};
border-radius: ${variant === 'person'
? theme.borderRadius.circle
: theme.borderRadius.tera};
border: none;
background-color: ${theme.colors.n300};
background-size: cover;
background-position: center;
background-image: url('data:image/svg+xml;utf8,${placeholders[variant]}');
${imageUrl &&
css`
background-image: url(${imageUrl});
`};
object-fit: cover;
object-position: center;
`;

const StyledImage = styled('img', {
shouldForwardProp: (prop) => isPropValid(prop),
})<StyledImageProps>(baseStyles);

/**
* The Avatar component.
*/
export const Avatar = styled('div', {
shouldForwardProp: (prop) => isPropValid(prop),
})<AvatarProps>(baseStyles);
export const Avatar = ({
src: initialSrc,
alt = '',
variant = 'person',
size = 'yotta',
...props
}: AvatarProps): JSX.Element => {
const src = initialSrc || `data:image/svg+xml;utf8,${placeholders[variant]}`;
return (
<StyledImage src={src} alt={alt} variant={variant} size={size} {...props} />
);
};
27 changes: 17 additions & 10 deletions packages/circuit-ui/components/ImageInput/ImageInput.tsx
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import { ChangeEvent, Fragment, InputHTMLAttributes, useState } from 'react';
import { css, jsx } from '@emotion/core';

import Avatar from '../Avatar';
import Label from '../Label';
import styled from '../../styles/styled';
import { uniqueId } from '../../util/id';
import { focusOutline, hideVisually } from '../../styles/style-mixins';
@@ -28,6 +29,10 @@ export interface ImageInputProps
* label
*/
label: string;
/**
* alt
*/
alt: string;
/**
* imageUrl
*/
@@ -40,12 +45,12 @@ const Input = styled.input(
&:focus + label {
${focusOutline({ theme })};
border-color: ${theme.colors.p500};
border-radius: ${theme.borderRadius.tera};
}
`,
);

const Image = styled(Avatar)`
const StyledAvatar = styled(Avatar)`
:hover {
filter: brightness(90%);
cursor: pointer;
@@ -58,6 +63,7 @@ const Image = styled(Avatar)`
export const ImageInput = ({
label,
imageUrl: initialImageUrl,
alt,
id: customId,
...props
}: ImageInputProps): JSX.Element => {
@@ -74,16 +80,17 @@ export const ImageInput = ({

return (
<Fragment>
<Input id={id} type="file" accept="image/*" onChange={handleChange} />
<Image
as="label"
imageUrl={imageUrl}
variant="business"
htmlFor={id}
<Input
id={id}
type="file"
accept="image/*"
onChange={handleChange}
{...props}
>
/>
<Label htmlFor={id}>
<span css={hideVisually()}>{label}</span>
</Image>
<StyledAvatar src={imageUrl} variant="business" alt={alt} />
</Label>
</Fragment>
);
};

0 comments on commit e69cfb4

Please sign in to comment.