Skip to content

Commit

Permalink
feat(avatar): add new variant and color props (#2681)
Browse files Browse the repository at this point in the history
  • Loading branch information
shleewhite committed Sep 21, 2022
1 parent 8138f6e commit 0ead4df
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 30 deletions.
6 changes: 6 additions & 0 deletions .changeset/ninety-islands-type.md
@@ -0,0 +1,6 @@
---
'@twilio-paste/avatar': minor
'@twilio-paste/core': minor
---

[Avatar] add new variant and color props
Expand Up @@ -4,12 +4,12 @@ exports[`Avatar image should render responsive css with an image 1`] = `
<DocumentFragment>
.emotion-0 {
box-sizing: border-box;
background-color: colorBackgroundUser;
border-radius: borderRadiusCircle;
overflow: hidden;
width: sizeIcon30;
height: sizeIcon30;
background-color: colorBackgroundUser;
color: colorText;
border-radius: borderRadiusCircle;
}
@media screen and (min-width: 40em) {
Expand Down Expand Up @@ -66,12 +66,12 @@ exports[`Avatar intials should render responsive css 1`] = `
<DocumentFragment>
.emotion-0 {
box-sizing: border-box;
background-color: colorBackgroundUser;
border-radius: borderRadiusCircle;
overflow: hidden;
width: sizeIcon10;
height: sizeIcon10;
background-color: colorBackgroundUser;
color: colorText;
border-radius: borderRadiusCircle;
}
@media screen and (min-width: 40em) {
Expand Down
53 changes: 48 additions & 5 deletions packages/paste-core/components/avatar/src/index.tsx
Expand Up @@ -3,9 +3,10 @@ import * as PropTypes from 'prop-types';
import {isValidElementType} from 'react-is';
import {Text} from '@twilio-paste/text';
import {Box, safelySpreadBoxProps} from '@twilio-paste/box';
import type {BoxStyleProps} from '@twilio-paste/box';
import {isIconSizeTokenProp} from '@twilio-paste/style-props';
import {getComputedTokenNames, getInitialsFromName} from './utils';
import type {AvatarProps, AvatarContentProps} from './types';
import type {AvatarProps, AvatarContentProps, ColorVariants, AvatarVariants} from './types';

const DEFAULT_SIZE = 'sizeIcon70';

Expand Down Expand Up @@ -41,8 +42,43 @@ const AvatarContents: React.FC<AvatarContentProps> = ({name, size = DEFAULT_SIZE
);
};

const colorVariants: Record<ColorVariants, BoxStyleProps> = {
default: {
backgroundColor: 'colorBackgroundUser',
color: 'colorText',
},
decorative10: {
backgroundColor: 'colorBackgroundDecorative10Weakest',
color: 'colorTextDecorative10',
},
decorative20: {
backgroundColor: 'colorBackgroundDecorative20Weakest',
color: 'colorTextDecorative20',
},
decorative30: {
backgroundColor: 'colorBackgroundDecorative30Weakest',
color: 'colorTextDecorative30',
},
decorative40: {
backgroundColor: 'colorBackgroundDecorative40Weakest',
color: 'colorTextDecorative40',
},
};

const variants: Record<AvatarVariants, BoxStyleProps> = {
user: {
borderRadius: 'borderRadiusCircle',
},
entity: {
borderRadius: 'borderRadius20',
},
};

const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
({name, children, size = DEFAULT_SIZE, element = 'AVATAR', src, icon, ...props}, ref) => {
(
{name, children, size = DEFAULT_SIZE, element = 'AVATAR', src, icon, color = 'default', variant = 'user', ...props},
ref
) => {
if (name === undefined) {
console.error('[Paste Avatar]: name prop is required');
}
Expand All @@ -52,12 +88,11 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
{...safelySpreadBoxProps(props)}
as="div"
element={element}
backgroundColor="colorBackgroundUser"
borderRadius="borderRadiusCircle"
overflow="hidden"
ref={ref}
size={size}
color="colorText"
{...colorVariants[color]}
{...variants[variant]}
>
<AvatarContents name={name} size={size} icon={icon} src={src} />
</Box>
Expand All @@ -72,6 +107,14 @@ Avatar.propTypes = {
name: PropTypes.string.isRequired,
element: PropTypes.string,
src: PropTypes.string,
color: PropTypes.oneOf([
'default',
'decorative10',
'decorative20',
'decorative30',
'decorative40',
] as ColorVariants[]),
variant: PropTypes.oneOf(['user', 'entity'] as AvatarVariants[]),
icon: (props) => {
if (typeof props.icon !== 'function') new Error('[Paste Avatar]: icon prop must be a Paste Icon');
return null;
Expand Down
5 changes: 5 additions & 0 deletions packages/paste-core/components/avatar/src/types.ts
Expand Up @@ -2,12 +2,17 @@ import type {IconSize} from '@twilio-paste/style-props';
import type {GenericIconProps} from '@twilio-paste/icons/esm/types';
import type {BoxProps} from '@twilio-paste/box';

export type ColorVariants = 'default' | 'decorative10' | 'decorative20' | 'decorative30' | 'decorative40';
export type AvatarVariants = 'user' | 'entity';

export type AvatarProps = React.HTMLAttributes<'div'> &
Pick<BoxProps, 'element'> & {
name: string;
size?: IconSize;
icon?: React.FC<GenericIconProps>;
src?: string;
color?: ColorVariants;
variant?: AvatarVariants;
};

export type AvatarContentProps = {
Expand Down
30 changes: 28 additions & 2 deletions packages/paste-core/components/avatar/stories/index.stories.tsx
Expand Up @@ -4,6 +4,7 @@ import {Stack} from '@twilio-paste/stack';
import {Box} from '@twilio-paste/box';
import {CustomizationProvider} from '@twilio-paste/customization';
import {UserIcon} from '@twilio-paste/icons/esm/UserIcon';
import {BusinessIcon} from '@twilio-paste/icons/esm/BusinessIcon';
import {useTheme} from '@twilio-paste/theme';
import {Avatar} from '../src';

Expand Down Expand Up @@ -66,6 +67,10 @@ export const Image = (): React.ReactNode => {
);
};

Image.story = {
parameters: {chromatic: {delay: 3000}},
};

export const Icon = (): React.ReactNode => {
return (
<Stack orientation="horizontal" spacing="space40">
Expand All @@ -84,8 +89,29 @@ export const Icon = (): React.ReactNode => {
);
};

Image.story = {
parameters: {chromatic: {delay: 3000}},
export const ColorVariants = (): React.ReactNode => {
return (
<Stack orientation="horizontal" spacing="space40">
<Avatar color="default" name="avatar example" icon={UserIcon} />
<Avatar color="decorative10" name="avatar example" icon={UserIcon} />
<Avatar color="decorative20" name="avatar example" icon={UserIcon} />
<Avatar color="decorative30" name="avatar example" icon={UserIcon} />
<Avatar color="decorative40" name="avatar example" icon={UserIcon} />
</Stack>
);
};

export const Variants = (): React.ReactNode => {
return (
<Stack orientation="horizontal" spacing="space40">
<Avatar variant="user" name="avatar example" />
<Avatar variant="user" name="avatar example" icon={UserIcon} />
<Avatar variant="user" name="avatar example" src="./avatars/avatar-sizeIcon70.png" />
<Avatar variant="entity" name="entity example" />
<Avatar variant="entity" name="entity example" icon={BusinessIcon} />
<Avatar variant="entity" name="entity example" src="./avatars/avatar-sizeIcon70.png" />
</Stack>
);
};

export const ResponsiveInitials = (): React.ReactNode => {
Expand Down
67 changes: 48 additions & 19 deletions packages/paste-website/src/pages/components/avatar/index.mdx
Expand Up @@ -15,6 +15,7 @@ import Changelog from '@twilio-paste/avatar/CHANGELOG.md';
import {Callout, CalloutHeading, CalloutText} from '@twilio-paste/callout';
import {SidebarCategoryRoutes} from '../../../constants';
import {UserIcon} from '@twilio-paste/icons/esm/UserIcon';
import {BusinessIcon} from '@twilio-paste/icons/esm/BusinessIcon';

export const pageQuery = graphql`
{
Expand Down Expand Up @@ -196,6 +197,44 @@ Provide the Avatar with an `icon` prop to display an icon. You must import the i
</Stack>`}
</LivePreview>

### Avatar variants

The Avatar `variant` prop can be used to change the shape. There are two variants, one for users and one for entities, like a business. The default value for `variant` is "user".

<LivePreview
scope={{
Avatar,
Stack,
UserIcon,
BusinessIcon,
}}
>
{`<Stack orientation="horizontal" spacing="space40">
<Avatar variant="user" name="user example" icon={UserIcon} />
<Avatar variant="entity" name="entity example" icon={BusinessIcon} />
</Stack>`}
</LivePreview>

### Changing the color of an Avatar

The Avatar `color` prop can be used to change the color. These colors are meant to differentiate between multiple Avatars in close proximity to each other. Do not use these colors to convey meaning, like warning, error, or success.

<LivePreview
scope={{
Avatar,
Stack,
UserIcon,
}}
>
{`<Stack orientation="horizontal" spacing="space40">
<Avatar color="default" name="avatar example" icon={UserIcon} />
<Avatar color="decorative10" name="avatar example" icon={UserIcon} />
<Avatar color="decorative20" name="avatar example" icon={UserIcon} />
<Avatar color="decorative30" name="avatar example" icon={UserIcon} />
<Avatar color="decorative40" name="avatar example" icon={UserIcon} />
</Stack>`}
</LivePreview>

### Responsive sizing

The Avatar `size` can be set as a responsive array of sizes.
Expand Down Expand Up @@ -277,25 +316,15 @@ const AvatarExample = () => {

#### Props

##### `name` string

Provide the name of the user or object.

##### `size` IconSize[]

Responsive size property that takes IconSize token names.

##### `src` string

Used to set the image `src` attribute when setting an image as the Avatar.

##### `icon` Paste Icon

Specify a Paste Icon to be displayed in the Avatar.

##### `element` string

Overrides the default element name ('AVATAR') to apply unique styles with the Customization Provider.
| Prop | Type | Description | Default |
| -------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------ |
| name | `string` | Provide the name of the user, entity or object. | |
| size? | `IconSize[]` | Responsive size property that takes IconSize token names. | `sizeIcon70` |
| src? | `string` | Used to set the image `src` attribute when setting an image as the Avatar. | |
| icon? | `React.FC<GenericIconProps>` | Specify a Paste Icon to be displayed in the Avatar. | |
| variant? | `'user', 'entity'` | Specify the variant of the Avatar based on what it represents. | `'user'` |
| color? | `'default', 'decorative10', 'decorative20', 'decorative30', 'decorative40'` | Used to set the color of the Avatar. | `'default'` |
| element? | `string` | Overrides the default element name to apply unique styles with the Customization Provider | `'AVATAR'` |

<ChangelogRevealer>
<Changelog />
Expand Down

0 comments on commit 0ead4df

Please sign in to comment.