-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Avatar): add avatar component (#1510)
- implement core component - add in variants, sizes, and shapes - add in new avatar icon to spritesheet - specify default aria label - add tests and stories
- Loading branch information
1 parent
e7ced34
commit bc21f85
Showing
9 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
@import '../../design-tokens/mixins.css'; | ||
|
||
/*------------------------------------*\ | ||
# AVATAR | ||
\*------------------------------------*/ | ||
|
||
/** | ||
* Avatar | ||
*/ | ||
.avatar { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
|
||
overflow: hidden; | ||
|
||
color: var(--eds-theme-color-text-neutral-strong); | ||
background-color: var(--eds-theme-color-background-neutral-medium); | ||
} | ||
|
||
.avatar:focus-visible { | ||
@mixin focus; | ||
} | ||
|
||
@supports not selector(:focus-visible) { | ||
.avatar:focus { | ||
@mixin focus; | ||
} | ||
} | ||
|
||
.avatar--sm { | ||
@mixin eds-theme-typography-label-md; | ||
|
||
height: var(--eds-size-4); | ||
width: var(--eds-size-4); | ||
} | ||
|
||
.avatar--md { | ||
@mixin eds-theme-typography-title-md; | ||
|
||
height: var(--eds-size-5); | ||
width: var(--eds-size-5); | ||
} | ||
|
||
.avatar--lg { | ||
@mixin eds-theme-typography-title-md; | ||
|
||
height: var(--eds-size-6); | ||
width: var(--eds-size-6); | ||
} | ||
|
||
.avatar--circle { | ||
border-radius: var(--eds-border-radius-round); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { BADGE } from '@geometricpanda/storybook-addon-badges'; | ||
import type { StoryObj, Meta } from '@storybook/react'; | ||
import type React from 'react'; | ||
|
||
import { Avatar } from './Avatar'; | ||
|
||
export default { | ||
title: 'Components/Avatar', | ||
component: Avatar, | ||
args: { | ||
size: 'md', | ||
shape: 'circle', | ||
variant: 'icon', | ||
}, | ||
parameters: { | ||
badges: [BADGE.BETA], | ||
layout: 'centered', | ||
}, | ||
} as Meta<Args>; | ||
|
||
type Args = React.ComponentProps<typeof Avatar>; | ||
|
||
export const Default: StoryObj<Args> = { | ||
args: {}, | ||
}; | ||
|
||
export const Small: StoryObj<Args> = { | ||
args: { | ||
size: 'sm', | ||
}, | ||
}; | ||
|
||
export const Medium: StoryObj<Args> = { | ||
args: { | ||
size: 'md', | ||
}, | ||
}; | ||
|
||
export const Large: StoryObj<Args> = { | ||
args: { | ||
size: 'lg', | ||
}, | ||
}; | ||
|
||
export const Square: StoryObj<Args> = { | ||
args: { | ||
shape: 'square', | ||
}, | ||
}; | ||
|
||
export const UsingImage: StoryObj<Args> = { | ||
args: { | ||
variant: 'image', | ||
src: `data:image/svg+xml,%3csvg width='38' height='37' viewBox='0 0 38 37' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M19 17.9417C16.4333 17.9417 14.3333 17.125 12.7 15.4917C11.0667 13.8583 10.25 11.7583 10.25 9.19168C10.25 6.62502 11.0667 4.52501 12.7 2.89168C14.3333 1.25835 16.4333 0.441681 19 0.441681C21.5667 0.441681 23.6667 1.25835 25.3 2.89168C26.9333 4.52501 27.75 6.62502 27.75 9.19168C27.75 11.7583 26.9333 13.8583 25.3 15.4917C23.6667 17.125 21.5667 17.9417 19 17.9417ZM0.333344 36.6667V31.1833C0.333344 29.7056 0.702788 28.4417 1.44168 27.3917C2.18057 26.3417 3.13334 25.5445 4.30001 25C6.90557 23.8333 9.40418 22.9583 11.7958 22.375C14.1875 21.7917 16.5889 21.5 19 21.5C21.4111 21.5 23.8028 21.8014 26.175 22.4042C28.5472 23.007 31.0361 23.8722 33.6417 25C34.8472 25.5445 35.8195 26.3417 36.5583 27.3917C37.2972 28.4417 37.6667 29.7056 37.6667 31.1833V36.6667H0.333344Z' fill='%235D6369'/%3e%3c/svg%3e`, | ||
}, | ||
}; | ||
|
||
export const UsingInitials: StoryObj<Args> = { | ||
args: { | ||
variant: 'initials', | ||
user: { | ||
fullName: 'John Smith', | ||
id: '12345', | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { generateSnapshots } from '@chanzuckerberg/story-utils'; | ||
import * as stories from './Avatar.stories'; | ||
|
||
describe('<Avatar />', () => { | ||
generateSnapshots(stories); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import clsx from 'clsx'; | ||
import React from 'react'; | ||
import Icon from '../Icon'; | ||
import styles from './Avatar.module.css'; | ||
|
||
export type UserData = { | ||
/** | ||
* The full name of the attached user (e.g., Jane Doe, David S. Pumpkins) | ||
*/ | ||
fullName: string; | ||
/** | ||
* User ID associated with the attached user | ||
*/ | ||
id?: string | number; | ||
/** | ||
* Additional data for an attached user (email, etc.) | ||
*/ | ||
[k: string]: string | number | undefined; | ||
}; | ||
|
||
export interface Props { | ||
/** | ||
* Label for the given avatar. Defaults to a string using user data. | ||
*/ | ||
ariaLabel: string; | ||
/** | ||
* CSS class names that can be appended to the component. | ||
*/ | ||
className?: string; | ||
/** | ||
* The shape of the avatar | ||
*/ | ||
shape?: 'circle' | 'square'; | ||
/** | ||
* The size of the component | ||
*/ | ||
size?: 'sm' | 'md' | 'lg'; | ||
/** | ||
* The URL to an image resource (loaded lazily) | ||
*/ | ||
src?: string; | ||
/** | ||
* The user associated with this avatar | ||
*/ | ||
user?: UserData; | ||
/** | ||
* Variants of how the avatar will be portrayed | ||
*/ | ||
variant?: 'icon' | 'initials' | 'image'; | ||
} | ||
|
||
function getInitials(fromName: string): string { | ||
/** | ||
* Scenarios: | ||
* - User's name is spelled as first and last name: John Smith | ||
* - User's name has a middle name or initial: John C. Smith | ||
* - User's Name has dashes in it | ||
*/ | ||
return fromName | ||
.split(' ') | ||
.map((part) => part[0]) | ||
.reduce( | ||
(prev, curr, idx, arr) => | ||
idx === 0 || idx === arr.length - 1 ? prev + curr : prev, | ||
'', | ||
) | ||
.toUpperCase(); | ||
} | ||
|
||
/** | ||
* BETA: This component is still a work in progress and is subject to change. | ||
* | ||
* `import {Avatar} from "@chanzuckerberg/eds";` | ||
* | ||
* Representation of a single, unique user, keyed by the user name | ||
*/ | ||
export const Avatar = ({ | ||
ariaLabel, | ||
className, | ||
shape = 'circle', | ||
size = 'md', | ||
user, | ||
variant = 'initials', | ||
src, | ||
...other | ||
}: Props) => { | ||
const componentClassName = clsx( | ||
styles['avatar'], | ||
shape && styles[`avatar--${shape}`], | ||
size && styles[`avatar--${size}`], | ||
variant && styles[`avatar--${variant}`], | ||
className, | ||
); | ||
|
||
const descriptiveLabel = | ||
ariaLabel ?? | ||
`Avatar for ${!user && 'unknown'} user ${user?.fullName || ''}`; | ||
|
||
return ( | ||
<div | ||
aria-label={descriptiveLabel} | ||
className={componentClassName} | ||
role="img" | ||
{...other} | ||
> | ||
{variant === 'initials' && (user ? getInitials(user.fullName) : '??')} | ||
{variant === 'icon' && <Icon name="avatar" purpose="decorative" />} | ||
{variant === 'image' && <img alt="user" src={src} />} | ||
</div> | ||
); | ||
}; |
124 changes: 124 additions & 0 deletions
124
src/components/Avatar/__snapshots__/Avatar.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`<Avatar /> Default story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for unknown user " | ||
class="avatar avatar--circle avatar--md avatar--icon" | ||
role="img" | ||
> | ||
<svg | ||
aria-hidden="true" | ||
class="icon" | ||
fill="currentColor" | ||
viewBox="0 0 24 24" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M11 10.9667C9.53337 10.9667 8.33337 10.5 7.40004 9.56667C6.46671 8.63334 6.00004 7.43334 6.00004 5.96668C6.00004 4.50001 6.46671 3.30001 7.40004 2.36668C8.33337 1.43334 9.53337 0.966675 11 0.966675C12.4667 0.966675 13.6667 1.43334 14.6 2.36668C15.5334 3.30001 16 4.50001 16 5.96668C16 7.43334 15.5334 8.63334 14.6 9.56667C13.6667 10.5 12.4667 10.9667 11 10.9667ZM0.333374 21.6667V18.5333C0.333374 17.6889 0.544485 16.9667 0.966707 16.3667C1.38893 15.7667 1.93337 15.3111 2.60004 15C4.08893 14.3333 5.51671 13.8333 6.88337 13.5C8.25004 13.1667 9.62226 13 11 13C12.3778 13 13.7445 13.1722 15.1 13.5167C16.4556 13.8611 17.8778 14.3556 19.3667 15C20.0556 15.3111 20.6112 15.7667 21.0334 16.3667C21.4556 16.9667 21.6667 17.6889 21.6667 18.5333V21.6667H0.333374Z" | ||
/> | ||
</svg> | ||
</div> | ||
`; | ||
|
||
exports[`<Avatar /> Large story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for unknown user " | ||
class="avatar avatar--circle avatar--lg avatar--icon" | ||
role="img" | ||
> | ||
<svg | ||
aria-hidden="true" | ||
class="icon" | ||
fill="currentColor" | ||
viewBox="0 0 24 24" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M11 10.9667C9.53337 10.9667 8.33337 10.5 7.40004 9.56667C6.46671 8.63334 6.00004 7.43334 6.00004 5.96668C6.00004 4.50001 6.46671 3.30001 7.40004 2.36668C8.33337 1.43334 9.53337 0.966675 11 0.966675C12.4667 0.966675 13.6667 1.43334 14.6 2.36668C15.5334 3.30001 16 4.50001 16 5.96668C16 7.43334 15.5334 8.63334 14.6 9.56667C13.6667 10.5 12.4667 10.9667 11 10.9667ZM0.333374 21.6667V18.5333C0.333374 17.6889 0.544485 16.9667 0.966707 16.3667C1.38893 15.7667 1.93337 15.3111 2.60004 15C4.08893 14.3333 5.51671 13.8333 6.88337 13.5C8.25004 13.1667 9.62226 13 11 13C12.3778 13 13.7445 13.1722 15.1 13.5167C16.4556 13.8611 17.8778 14.3556 19.3667 15C20.0556 15.3111 20.6112 15.7667 21.0334 16.3667C21.4556 16.9667 21.6667 17.6889 21.6667 18.5333V21.6667H0.333374Z" | ||
/> | ||
</svg> | ||
</div> | ||
`; | ||
|
||
exports[`<Avatar /> Medium story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for unknown user " | ||
class="avatar avatar--circle avatar--md avatar--icon" | ||
role="img" | ||
> | ||
<svg | ||
aria-hidden="true" | ||
class="icon" | ||
fill="currentColor" | ||
viewBox="0 0 24 24" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M11 10.9667C9.53337 10.9667 8.33337 10.5 7.40004 9.56667C6.46671 8.63334 6.00004 7.43334 6.00004 5.96668C6.00004 4.50001 6.46671 3.30001 7.40004 2.36668C8.33337 1.43334 9.53337 0.966675 11 0.966675C12.4667 0.966675 13.6667 1.43334 14.6 2.36668C15.5334 3.30001 16 4.50001 16 5.96668C16 7.43334 15.5334 8.63334 14.6 9.56667C13.6667 10.5 12.4667 10.9667 11 10.9667ZM0.333374 21.6667V18.5333C0.333374 17.6889 0.544485 16.9667 0.966707 16.3667C1.38893 15.7667 1.93337 15.3111 2.60004 15C4.08893 14.3333 5.51671 13.8333 6.88337 13.5C8.25004 13.1667 9.62226 13 11 13C12.3778 13 13.7445 13.1722 15.1 13.5167C16.4556 13.8611 17.8778 14.3556 19.3667 15C20.0556 15.3111 20.6112 15.7667 21.0334 16.3667C21.4556 16.9667 21.6667 17.6889 21.6667 18.5333V21.6667H0.333374Z" | ||
/> | ||
</svg> | ||
</div> | ||
`; | ||
|
||
exports[`<Avatar /> Small story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for unknown user " | ||
class="avatar avatar--circle avatar--sm avatar--icon" | ||
role="img" | ||
> | ||
<svg | ||
aria-hidden="true" | ||
class="icon" | ||
fill="currentColor" | ||
viewBox="0 0 24 24" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M11 10.9667C9.53337 10.9667 8.33337 10.5 7.40004 9.56667C6.46671 8.63334 6.00004 7.43334 6.00004 5.96668C6.00004 4.50001 6.46671 3.30001 7.40004 2.36668C8.33337 1.43334 9.53337 0.966675 11 0.966675C12.4667 0.966675 13.6667 1.43334 14.6 2.36668C15.5334 3.30001 16 4.50001 16 5.96668C16 7.43334 15.5334 8.63334 14.6 9.56667C13.6667 10.5 12.4667 10.9667 11 10.9667ZM0.333374 21.6667V18.5333C0.333374 17.6889 0.544485 16.9667 0.966707 16.3667C1.38893 15.7667 1.93337 15.3111 2.60004 15C4.08893 14.3333 5.51671 13.8333 6.88337 13.5C8.25004 13.1667 9.62226 13 11 13C12.3778 13 13.7445 13.1722 15.1 13.5167C16.4556 13.8611 17.8778 14.3556 19.3667 15C20.0556 15.3111 20.6112 15.7667 21.0334 16.3667C21.4556 16.9667 21.6667 17.6889 21.6667 18.5333V21.6667H0.333374Z" | ||
/> | ||
</svg> | ||
</div> | ||
`; | ||
|
||
exports[`<Avatar /> Square story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for unknown user " | ||
class="avatar avatar--square avatar--md avatar--icon" | ||
role="img" | ||
> | ||
<svg | ||
aria-hidden="true" | ||
class="icon" | ||
fill="currentColor" | ||
viewBox="0 0 24 24" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M11 10.9667C9.53337 10.9667 8.33337 10.5 7.40004 9.56667C6.46671 8.63334 6.00004 7.43334 6.00004 5.96668C6.00004 4.50001 6.46671 3.30001 7.40004 2.36668C8.33337 1.43334 9.53337 0.966675 11 0.966675C12.4667 0.966675 13.6667 1.43334 14.6 2.36668C15.5334 3.30001 16 4.50001 16 5.96668C16 7.43334 15.5334 8.63334 14.6 9.56667C13.6667 10.5 12.4667 10.9667 11 10.9667ZM0.333374 21.6667V18.5333C0.333374 17.6889 0.544485 16.9667 0.966707 16.3667C1.38893 15.7667 1.93337 15.3111 2.60004 15C4.08893 14.3333 5.51671 13.8333 6.88337 13.5C8.25004 13.1667 9.62226 13 11 13C12.3778 13 13.7445 13.1722 15.1 13.5167C16.4556 13.8611 17.8778 14.3556 19.3667 15C20.0556 15.3111 20.6112 15.7667 21.0334 16.3667C21.4556 16.9667 21.6667 17.6889 21.6667 18.5333V21.6667H0.333374Z" | ||
/> | ||
</svg> | ||
</div> | ||
`; | ||
|
||
exports[`<Avatar /> UsingImage story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for unknown user " | ||
class="avatar avatar--circle avatar--md avatar--image" | ||
role="img" | ||
> | ||
<img | ||
alt="user" | ||
src="data:image/svg+xml,%3csvg width='38' height='37' viewBox='0 0 38 37' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M19 17.9417C16.4333 17.9417 14.3333 17.125 12.7 15.4917C11.0667 13.8583 10.25 11.7583 10.25 9.19168C10.25 6.62502 11.0667 4.52501 12.7 2.89168C14.3333 1.25835 16.4333 0.441681 19 0.441681C21.5667 0.441681 23.6667 1.25835 25.3 2.89168C26.9333 4.52501 27.75 6.62502 27.75 9.19168C27.75 11.7583 26.9333 13.8583 25.3 15.4917C23.6667 17.125 21.5667 17.9417 19 17.9417ZM0.333344 36.6667V31.1833C0.333344 29.7056 0.702788 28.4417 1.44168 27.3917C2.18057 26.3417 3.13334 25.5445 4.30001 25C6.90557 23.8333 9.40418 22.9583 11.7958 22.375C14.1875 21.7917 16.5889 21.5 19 21.5C21.4111 21.5 23.8028 21.8014 26.175 22.4042C28.5472 23.007 31.0361 23.8722 33.6417 25C34.8472 25.5445 35.8195 26.3417 36.5583 27.3917C37.2972 28.4417 37.6667 29.7056 37.6667 31.1833V36.6667H0.333344Z' fill='%235D6369'/%3e%3c/svg%3e" | ||
/> | ||
</div> | ||
`; | ||
|
||
exports[`<Avatar /> UsingInitials story renders snapshot 1`] = ` | ||
<div | ||
aria-label="Avatar for false user John Smith" | ||
class="avatar avatar--circle avatar--md avatar--initials" | ||
role="img" | ||
> | ||
JS | ||
</div> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Avatar as default } from './Avatar'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.