-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(avatarGroup): introduce (#5916)
* feat(avatarGroup): introduce * update * feat(avatarGroup): update base on feedback * update * re-add package * update * update * update * feat(avatarGroupe): update with feedBack * fix: indentation * remove `'use client';` * feat(utils): separate "avatar" * feat(avatarGroup): introduce * update * feat(avatarGroup): update base on feedback * update * re-add package * update * update * update * feat(avatarGroupe): update with feedBack * fix: indentation * remove `'use client';` * feat(utils): separate "avatar" * review: expedited code-review changes * test(avatarGroup): add * review: expedited code-review changes * remove: removed files with wrong casing * chore: renamed utils * chore: fix linting order * chore: fix tests --------- Co-authored-by: Claudio Wunder <[email protected]>
- Loading branch information
1 parent
5b0dc4b
commit 4dc0089
Showing
13 changed files
with
288 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,22 @@ | ||
.avatar { | ||
@apply flex | ||
h-8 | ||
w-8 | ||
items-center | ||
justify-center | ||
rounded-full | ||
border-2 | ||
border-white | ||
bg-neutral-100 | ||
object-cover | ||
text-xs | ||
text-neutral-800 | ||
dark:border-neutral-950 | ||
dark:bg-neutral-900 | ||
dark:text-neutral-300; | ||
} | ||
|
||
.avatarRoot { | ||
@apply -ml-2 | ||
first:ml-0; | ||
} |
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,31 @@ | ||
import type { Meta as MetaObj, StoryObj } from '@storybook/react'; | ||
|
||
import { githubProfileAvatarUrl } from '@/util/gitHubUtils'; | ||
|
||
import Avatar from './'; | ||
|
||
type Story = StoryObj<typeof Avatar>; | ||
type Meta = MetaObj<typeof Avatar>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
src: githubProfileAvatarUrl('ovflowd'), | ||
alt: 'ovflowd', | ||
}, | ||
}; | ||
|
||
export const NoSquare: Story = { | ||
args: { | ||
src: '/static/images/logos/stacked-dark.svg', | ||
alt: 'SD', | ||
}, | ||
}; | ||
|
||
export const FallBack: Story = { | ||
args: { | ||
src: 'https://avatars.githubusercontent.com/u/', | ||
alt: 'UA', | ||
}, | ||
}; | ||
|
||
export default { component: Avatar } as Meta; |
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,20 @@ | ||
import * as RadixAvatar from '@radix-ui/react-avatar'; | ||
import type { FC } from 'react'; | ||
|
||
import styles from './index.module.css'; | ||
|
||
type AvatarProps = { | ||
src: string; | ||
alt: string; | ||
}; | ||
|
||
const Avatar: FC<AvatarProps> = ({ src, alt }) => ( | ||
<RadixAvatar.Root className={styles.avatarRoot}> | ||
<RadixAvatar.Image src={src} alt={alt} className={styles.avatar} /> | ||
<RadixAvatar.Fallback delayMs={500} className={styles.avatar}> | ||
{alt} | ||
</RadixAvatar.Fallback> | ||
</RadixAvatar.Root> | ||
); | ||
|
||
export default 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { render, fireEvent } from '@testing-library/react'; | ||
|
||
import { githubProfileAvatarUrl } from '@/util/gitHubUtils'; | ||
|
||
import AvatarGroup from '../index'; | ||
|
||
const names = [ | ||
'ovflowd', | ||
'bmuenzenmeyer', | ||
'AugustinMauroy', | ||
'HinataKah0', | ||
'Harkunwar', | ||
'rodion-arr', | ||
'mikeesto', | ||
'bnb', | ||
'benhalverson', | ||
'aymen94', | ||
'shanpriyan', | ||
'Wai-Dung', | ||
'manishprivet', | ||
'araujogui', | ||
]; | ||
|
||
const avatars = names.map(name => ({ | ||
src: githubProfileAvatarUrl(name), | ||
alt: name, | ||
})); | ||
|
||
describe('AvatarGroup component', () => { | ||
it('renders the AvatarGroup component properly', () => { | ||
const { getByText } = render(<AvatarGroup avatars={avatars} limit={2} />); | ||
|
||
const showMoreButton = getByText('+12'); | ||
expect(showMoreButton).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays the rest of the avatars when "show more" button is clicked', () => { | ||
const { getByText } = render(<AvatarGroup avatars={avatars} limit={2} />); | ||
|
||
const showMoreButton = getByText('+12'); | ||
fireEvent.click(showMoreButton); | ||
|
||
const hideMoreButton = getByText('-12'); | ||
expect(hideMoreButton).toBeInTheDocument(); | ||
}); | ||
}); |
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,4 @@ | ||
.avatarGroup { | ||
@apply flex | ||
items-center; | ||
} |
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,61 @@ | ||
import type { Meta as MetaObj, StoryObj } from '@storybook/react'; | ||
|
||
import { githubProfileAvatarUrl } from '@/util/gitHubUtils'; | ||
|
||
import AvatarGroup from './'; | ||
|
||
type Story = StoryObj<typeof AvatarGroup>; | ||
type Meta = MetaObj<typeof AvatarGroup>; | ||
|
||
const names = [ | ||
'ovflowd', | ||
'bmuenzenmeyer', | ||
'AugustinMauroy', | ||
'HinataKah0', | ||
'Harkunwar', | ||
'rodion-arr', | ||
'mikeesto', | ||
'bnb', | ||
'benhalverson', | ||
'aymen94', | ||
'shanpriyan', | ||
'Wai-Dung', | ||
'manishprivet', | ||
'araujogui', | ||
]; | ||
|
||
const unknownAvatar = { | ||
src: 'https://avatars.githubusercontent.com/u/', | ||
alt: 'unknown-avatar', | ||
}; | ||
|
||
const defaultProps = { | ||
avatars: [ | ||
unknownAvatar, | ||
...names.map(name => ({ src: githubProfileAvatarUrl(name), alt: name })), | ||
], | ||
}; | ||
|
||
export const Default: Story = { | ||
args: { ...defaultProps }, | ||
}; | ||
|
||
export const WithCustomLimit: Story = { | ||
args: { | ||
...defaultProps, | ||
limit: 5, | ||
}, | ||
}; | ||
|
||
export const InSmallContainer: Story = { | ||
decorators: [ | ||
Story => ( | ||
<div className="w-[150px]"> | ||
<Story /> | ||
</div> | ||
), | ||
], | ||
args: { ...defaultProps, limit: 5 }, | ||
}; | ||
|
||
export default { component: AvatarGroup } as Meta; |
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,48 @@ | ||
import classNames from 'classnames'; | ||
import type { ComponentProps, FC } from 'react'; | ||
import { useState, useMemo } from 'react'; | ||
|
||
import { getAcronymFromString } from '@/util/stringUtils'; | ||
|
||
import Avatar from './Avatar'; | ||
import avatarstyles from './Avatar/index.module.css'; | ||
import styles from './index.module.css'; | ||
|
||
type AvatarGroupProps = { | ||
avatars: ComponentProps<typeof Avatar>[]; | ||
limit?: number; | ||
}; | ||
|
||
const AvatarGroup: FC<AvatarGroupProps> = ({ avatars, limit = 10 }) => { | ||
const [showMore, setShowMore] = useState(false); | ||
|
||
const renderAvatars = useMemo( | ||
() => avatars.slice(0, showMore ? avatars.length : limit), | ||
[showMore, avatars, limit] | ||
); | ||
|
||
return ( | ||
<div className={styles.avatarGroup}> | ||
{renderAvatars.map((avatar, index) => ( | ||
<Avatar | ||
src={avatar.src} | ||
alt={getAcronymFromString(avatar.alt)} | ||
key={index} | ||
/> | ||
))} | ||
|
||
{avatars.length > limit && ( | ||
<span | ||
onClick={() => setShowMore(!showMore)} | ||
className={classNames(avatarstyles.avatarRoot, 'cursor-pointer')} | ||
> | ||
<span className={avatarstyles.avatar}> | ||
{`${showMore ? '-' : '+'}${avatars.length - limit}`} | ||
</span> | ||
</span> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default AvatarGroup; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,9 @@ | ||
import { githubProfileAvatarUrl } from '@/util/gitHubUtils'; | ||
|
||
describe('Github utils', () => { | ||
it('githubProfileAvatarUrl returns the correct URL', () => { | ||
expect(githubProfileAvatarUrl('octocat')).toBe( | ||
'https://avatars.githubusercontent.com/octocat' | ||
); | ||
}); | ||
}); |
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,15 @@ | ||
import { getAcronymFromString } from '@/util/stringUtils'; | ||
|
||
describe('String utils', () => { | ||
it('getAcronymFromString returns the correct acronym', () => { | ||
expect(getAcronymFromString('John Doe')).toBe('JD'); | ||
}); | ||
|
||
it('getAcronymFromString returns the correct acronym for a single word', () => { | ||
expect(getAcronymFromString('John')).toBe('J'); | ||
}); | ||
|
||
it('getAcronymFromString if the string is empty, it returns NA', () => { | ||
expect(getAcronymFromString('')).toBe('NA'); | ||
}); | ||
}); |
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,2 @@ | ||
export const githubProfileAvatarUrl = (username: string): string => | ||
`https://avatars.githubusercontent.com/${username}`; |
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,2 @@ | ||
export const getAcronymFromString = (str: string) => | ||
[...(str.trim().match(/\b(\w)/g) || '')].join('').toUpperCase(); |