Skip to content

Commit

Permalink
fix(Avatar): support display names using emoji and multi-byte (#1716)
Browse files Browse the repository at this point in the history
  • Loading branch information
booc0mtaco authored Jul 31, 2023
1 parent 8e4e462 commit 1294022
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/components/Avatar/Avatar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

color: var(--eds-theme-color-text-neutral-strong);
background-color: var(--eds-theme-color-background-neutral-medium);

white-space: nowrap;
}

.avatar:focus-visible {
Expand Down
66 changes: 58 additions & 8 deletions src/components/Avatar/Avatar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ import { Avatar } from './Avatar';
export default {
title: 'Components/Avatar',
component: Avatar,
args: {
size: 'md',
shape: 'circle',
variant: 'icon',
},
parameters: {
badges: ['1.3'],
layout: 'centered',
Expand All @@ -19,49 +14,62 @@ export default {

type Args = React.ComponentProps<typeof Avatar>;

export const Default: StoryObj<Args> = {
args: {},
};
export const Default: StoryObj<Args> = {};

export const Small: StoryObj<Args> = {
args: {
size: 'sm',
shape: 'circle',
variant: 'icon',
},
};

export const Medium: StoryObj<Args> = {
args: {
size: 'md',
shape: 'circle',
variant: 'icon',
},
};

export const Large: StoryObj<Args> = {
args: {
size: 'lg',
shape: 'circle',
variant: 'icon',
},
};

export const Square: StoryObj<Args> = {
args: {
shape: 'square',
size: 'md',
variant: 'icon',
},
};

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`,
size: 'md',
shape: 'circle',
},
};

export const WithCustomLabel: StoryObj<Args> = {
args: {
ariaLabel: 'Custom label for avatar',
size: 'md',
shape: 'circle',
variant: 'icon',
},
};

export const UsingInitials: StoryObj<Args> = {
args: {
size: 'md',
shape: 'circle',
variant: 'initials',
user: {
fullName: 'John Smith',
Expand All @@ -70,3 +78,45 @@ export const UsingInitials: StoryObj<Args> = {
},
},
};

export const UsingEmoji: StoryObj<Args> = {
args: {
shape: 'circle',
variant: 'initials',
user: {
fullName: 'Young Yarn Lad',
displayName: '🧶👦🏽',
id: '12345',
hasArbitraryMetadata: true,
},
size: 'lg',
},
};

export const WhenImageVariantMissingSource: StoryObj<Args> = {
args: {
shape: 'circle',
variant: 'image',
user: {
fullName: 'Young Yarn Lad',
id: '12345',
hasArbitraryMetadata: true,
moreMetadata: 123,
},
size: 'lg',
},
};

export const UsingMultibyteUnicode: StoryObj<Args> = {
args: {
shape: 'circle',
variant: 'image',
user: {
fullName: '你好世界',
id: '12345',
hasArbitraryMetadata: true,
moreMetadata: 123,
},
size: 'lg',
},
};
19 changes: 16 additions & 3 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export type UserData = {
* User ID associated with the attached user
*/
id?: string | number;
/**
* The display shortcut for the user name. Can be initials, emoji, or other text symbols (recommended max: 2)
*/
displayName?: string;
/**
* Additional data for an attached user (email, etc.)
*/
Expand Down Expand Up @@ -56,7 +60,7 @@ function getInitials(fromName: string): string {
* - User's name has a middle name or initial: John C. Smith
* - User's Name has dashes in it
*/
return fromName
const initials = fromName
.split(' ')
.map((part) => part[0])
.reduce(
Expand All @@ -65,6 +69,7 @@ function getInitials(fromName: string): string {
'',
)
.toUpperCase();
return initials;
}

/**
Expand Down Expand Up @@ -94,18 +99,26 @@ export const Avatar = ({
ariaLabel ??
`Avatar for ${user ? '' : 'unknown '}user ${user?.fullName || ''}`;

// use the display name if prop is provided. Otherwise, try to calculate initials
let avatarDisplayName = user ? getInitials(user.fullName) : '??';

if (user?.displayName) {
avatarDisplayName = user.displayName;
}

return (
<div
aria-label={descriptiveLabel}
className={componentClassName}
role="img"
{...other}
>
{variant === 'initials' && (user ? getInitials(user.fullName) : '??')}
{variant === 'initials' && avatarDisplayName}
{variant === 'icon' && <Icon name="person" purpose="decorative" />}
{variant === 'image' && (
{variant === 'image' && src && (
<img alt="user" className={styles['avatar__image']} src={src} />
)}
{variant === 'image' && !src && avatarDisplayName}
</div>
);
};
44 changes: 32 additions & 12 deletions src/components/Avatar/__snapshots__/Avatar.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,10 @@
exports[`<Avatar /> Default story renders snapshot 1`] = `
<div
aria-label="Avatar for unknown user "
class="avatar avatar--circle avatar--md avatar--icon"
class="avatar avatar--circle avatar--md avatar--initials"
role="img"
>
<svg
aria-hidden="true"
class="icon"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 12C10.9 12 9.95833 11.6083 9.175 10.825C8.39167 10.0417 8 9.1 8 8C8 6.9 8.39167 5.95833 9.175 5.175C9.95833 4.39167 10.9 4 12 4C13.1 4 14.0417 4.39167 14.825 5.175C15.6083 5.95833 16 6.9 16 8C16 9.1 15.6083 10.0417 14.825 10.825C14.0417 11.6083 13.1 12 12 12ZM18 20H6C5.45 20 4.97917 19.8042 4.5875 19.4125C4.19583 19.0208 4 18.55 4 18V17.2C4 16.6333 4.14583 16.1125 4.4375 15.6375C4.72917 15.1625 5.11667 14.8 5.6 14.55C6.63333 14.0333 7.68333 13.6458 8.75 13.3875C9.81667 13.1292 10.9 13 12 13C13.1 13 14.1833 13.1292 15.25 13.3875C16.3167 13.6458 17.3667 14.0333 18.4 14.55C18.8833 14.8 19.2708 15.1625 19.5625 15.6375C19.8542 16.1125 20 16.6333 20 17.2V18C20 18.55 19.8042 19.0208 19.4125 19.4125C19.0208 19.8042 18.55 20 18 20ZM6 18H18V17.2C18 17.0167 17.9542 16.85 17.8625 16.7C17.7708 16.55 17.65 16.4333 17.5 16.35C16.6 15.9 15.6917 15.5625 14.775 15.3375C13.8583 15.1125 12.9333 15 12 15C11.0667 15 10.1417 15.1125 9.225 15.3375C8.30833 15.5625 7.4 15.9 6.5 16.35C6.35 16.4333 6.22917 16.55 6.1375 16.7C6.04583 16.85 6 17.0167 6 17.2V18ZM12 10C12.55 10 13.0208 9.80417 13.4125 9.4125C13.8042 9.02083 14 8.55 14 8C14 7.45 13.8042 6.97917 13.4125 6.5875C13.0208 6.19583 12.55 6 12 6C11.45 6 10.9792 6.19583 10.5875 6.5875C10.1958 6.97917 10 7.45 10 8C10 8.55 10.1958 9.02083 10.5875 9.4125C10.9792 9.80417 11.45 10 12 10Z"
/>
</svg>
??
</div>
`;

Expand Down Expand Up @@ -100,6 +90,16 @@ exports[`<Avatar /> Square story renders snapshot 1`] = `
</div>
`;

exports[`<Avatar /> UsingEmoji story renders snapshot 1`] = `
<div
aria-label="Avatar for user Young Yarn Lad"
class="avatar avatar--circle avatar--lg avatar--initials"
role="img"
>
🧶👦🏽
</div>
`;

exports[`<Avatar /> UsingImage story renders snapshot 1`] = `
<div
aria-label="Avatar for unknown user "
Expand All @@ -124,6 +124,26 @@ exports[`<Avatar /> UsingInitials story renders snapshot 1`] = `
</div>
`;

exports[`<Avatar /> UsingMultibyteUnicode story renders snapshot 1`] = `
<div
aria-label="Avatar for user 你好世界"
class="avatar avatar--circle avatar--lg avatar--image"
role="img"
>
</div>
`;

exports[`<Avatar /> WhenImageVariantMissingSource story renders snapshot 1`] = `
<div
aria-label="Avatar for user Young Yarn Lad"
class="avatar avatar--circle avatar--lg avatar--image"
role="img"
>
YL
</div>
`;

exports[`<Avatar /> WithCustomLabel story renders snapshot 1`] = `
<div
aria-label="Custom label for avatar"
Expand Down

0 comments on commit 1294022

Please sign in to comment.