-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Vincent Smedinga <v.smedinga@amsterdam.nl> Co-authored-by: Aram Limpens <a.limpens@amsterdam.nl>
- Loading branch information
1 parent
b423dfa
commit 8dec2cf
Showing
11 changed files
with
415 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,15 @@ | ||
<!-- @license CC0-1.0 --> | ||
|
||
# Avatar | ||
|
||
A circular badge representing a person. | ||
|
||
## Design | ||
|
||
The avatar contains 1 or 2 initial letters from the person's full name, a picture, or a generic icon. | ||
The default background colour is dark blue. | ||
|
||
## Usage | ||
|
||
Display an avatar for the person currently using the application, | ||
or to associate a person with a content item. |
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,74 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
|
||
.ams-avatar { | ||
aspect-ratio: var(--ams-avatar-aspect-ratio); | ||
border-radius: 50%; | ||
display: inline-flex; | ||
font-family: var(--ams-avatar-font-family); | ||
font-size: var(--ams-avatar-font-size); | ||
line-height: var(--ams-avatar-line-height); | ||
padding-block: var(--ams-avatar-padding-block); | ||
padding-inline: var(--ams-avatar-padding-inline); | ||
place-content: center; | ||
width: calc(var(--ams-avatar-line-height) * var(--ams-avatar-font-size)); | ||
|
||
svg { | ||
fill: currentColor; | ||
} | ||
} | ||
|
||
.ams-avatar--has-image { | ||
overflow: hidden; | ||
padding-block: 0; | ||
padding-inline: 0; | ||
vertical-align: middle; /* Remove ‘phantom’ white space when image doesn’t load */ | ||
width: calc(var(--ams-avatar-line-height) * var(--ams-avatar-font-size) + 2 * var(--ams-avatar-padding-inline)); | ||
} | ||
|
||
.ams-avatar--blue { | ||
background-color: var(--ams-avatar-blue-background-color); | ||
color: var(--ams-avatar-blue-color); | ||
} | ||
|
||
.ams-avatar--dark-blue { | ||
background-color: var(--ams-avatar-dark-blue-background-color); | ||
color: var(--ams-avatar-dark-blue-color); | ||
} | ||
|
||
.ams-avatar--dark-green { | ||
background-color: var(--ams-avatar-dark-green-background-color); | ||
color: var(--ams-avatar-dark-green-color); | ||
} | ||
|
||
.ams-avatar--green { | ||
background-color: var(--ams-avatar-green-background-color); | ||
color: var(--ams-avatar-green-color); | ||
} | ||
|
||
.ams-avatar--magenta { | ||
background-color: var(--ams-avatar-magenta-background-color); | ||
color: var(--ams-avatar-magenta-color); | ||
} | ||
|
||
.ams-avatar--orange { | ||
background-color: var(--ams-avatar-orange-background-color); | ||
color: var(--ams-avatar-orange-color); | ||
} | ||
|
||
.ams-avatar--purple { | ||
background-color: var(--ams-avatar-purple-background-color); | ||
color: var(--ams-avatar-purple-color); | ||
} | ||
|
||
.ams-avatar--red { | ||
background-color: var(--ams-avatar-red-background-color); | ||
color: var(--ams-avatar-red-color); | ||
} | ||
|
||
.ams-avatar--yellow { | ||
background-color: var(--ams-avatar-yellow-background-color); | ||
color: var(--ams-avatar-yellow-color); | ||
} |
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,95 @@ | ||
import { render, screen } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { Avatar, avatarColors } from './Avatar' | ||
import '@testing-library/jest-dom' | ||
|
||
describe('Avatar', () => { | ||
it('renders', () => { | ||
render(<Avatar label="NR" />) | ||
|
||
const component = screen.getByText('Initialen gebruiker: NR') | ||
|
||
expect(component).toBeInTheDocument() | ||
expect(component).toBeVisible() | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
const { container } = render(<Avatar label="RS" />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveClass('ams-avatar') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
const { container } = render(<Avatar label="VS" className="extra" />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveClass('ams-avatar extra') | ||
}) | ||
|
||
it('renders with a label consisting of no more than two, uppercase letters', () => { | ||
const { container } = render(<Avatar label="Design System" />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveTextContent('DE') | ||
}) | ||
|
||
it('renders with default content and title', () => { | ||
const { container } = render(<Avatar label="" />) | ||
|
||
const component = screen.getByText('Gebruiker') | ||
const svg = container.querySelector('svg') | ||
|
||
expect(component).toBeVisible() | ||
expect(svg).toBeVisible() | ||
}) | ||
|
||
it('renders with a profile picture', () => { | ||
const { container } = render(<Avatar label="RS" imageSrc="image-source" />) | ||
|
||
const component = screen.getByText('Initialen gebruiker: RS') | ||
const image = container.querySelector('[src="image-source"]') | ||
|
||
expect(component).toBeVisible() | ||
expect(image).toBeVisible() | ||
}) | ||
|
||
it('shortens a label that is too long', () => { | ||
const { container } = render(<Avatar label="ABC" />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveTextContent('AB') | ||
}) | ||
|
||
it('renders with default color', () => { | ||
const { container } = render(<Avatar label="VS" />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveClass('ams-avatar--dark-blue') | ||
}) | ||
|
||
avatarColors.map((color) => | ||
it(`renders with ${color} color`, () => { | ||
const { container } = render(<Avatar label="AL" color={color} />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveClass(`ams-avatar--${color}`) | ||
}), | ||
) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLSpanElement>() | ||
|
||
const { container } = render(<Avatar label="AL" ref={ref} />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
}) |
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,73 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
|
||
import { PersonalLoginIcon } from '@amsterdam/design-system-react-icons' | ||
import clsx from 'clsx' | ||
import { forwardRef } from 'react' | ||
import type { ForwardedRef, HTMLAttributes } from 'react' | ||
import { Icon } from '../Icon' | ||
import { Image } from '../Image' | ||
import { VisuallyHidden } from '../VisuallyHidden' | ||
|
||
export const avatarColors = [ | ||
'blue', | ||
'dark-blue', | ||
'dark-green', | ||
'green', | ||
'magenta', | ||
'orange', | ||
'purple', | ||
'red', | ||
'yellow', | ||
] as const | ||
|
||
type AvatarColor = (typeof avatarColors)[number] | ||
|
||
type ContentProps = { | ||
imageSrc?: string | ||
initials: string | ||
} | ||
|
||
const Content = ({ imageSrc, initials }: ContentProps) => { | ||
if (imageSrc) { | ||
return <Image src={imageSrc} alt="" /> | ||
} | ||
|
||
if (initials.length) { | ||
return <span aria-hidden={true}>{initials}</span> | ||
} | ||
|
||
return <Icon svg={PersonalLoginIcon} size="level-6" /> | ||
} | ||
|
||
export type AvatarProps = { | ||
color?: AvatarColor | ||
imageSrc?: string | ||
label: string | ||
} & HTMLAttributes<HTMLSpanElement> | ||
|
||
export const Avatar = forwardRef( | ||
( | ||
{ label, imageSrc, className, color = 'dark-blue', ...restProps }: AvatarProps, | ||
ref: ForwardedRef<HTMLSpanElement>, | ||
) => { | ||
const initials = label.slice(0, 2).toUpperCase() | ||
|
||
const a11yLabel = initials.length === 0 ? 'Gebruiker' : `Initialen gebruiker: ${initials}` | ||
|
||
return ( | ||
<span | ||
{...restProps} | ||
ref={ref} | ||
className={clsx('ams-avatar', `ams-avatar--${color}`, imageSrc && 'ams-avatar--has-image', className)} | ||
> | ||
<VisuallyHidden>{a11yLabel}</VisuallyHidden> | ||
<Content imageSrc={imageSrc} initials={initials} /> | ||
</span> | ||
) | ||
}, | ||
) | ||
|
||
Avatar.displayName = '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,5 @@ | ||
<!-- @license CC0-1.0 --> | ||
|
||
# React Avatar component | ||
|
||
[Avatar documentation](../../../css/src/components/avatar/README.md) |
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 { Avatar } from './Avatar' | ||
export type { AvatarProps } 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
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,49 @@ | ||
{ | ||
"ams": { | ||
"avatar": { | ||
"aspect-ratio": { "value": "{ams.proportion.square}" }, | ||
"font-family": { "value": "{ams.text.font-family}" }, | ||
"font-size": { "value": "{ams.text.level.6.font-size}" }, | ||
"font-weight": { "value": "{ams.text.font-weight.normal}" }, | ||
"line-height": { "value": "{ams.text.level.6.line-height}" }, | ||
"padding-block": { "value": "0.25rem" }, | ||
"padding-inline": { "value": "0.25rem" }, | ||
"blue": { | ||
"background-color": { "value": "{ams.color.blue}" }, | ||
"color": { "value": "{ams.color.primary-black}" } | ||
}, | ||
"dark-blue": { | ||
"background-color": { "value": "{ams.color.primary-blue}" }, | ||
"color": { "value": "{ams.color.primary-white}" } | ||
}, | ||
"dark-green": { | ||
"background-color": { "value": "{ams.color.dark-green}" }, | ||
"color": { "value": "{ams.color.primary-white}" } | ||
}, | ||
"green": { | ||
"background-color": { "value": "{ams.color.green}" }, | ||
"color": { "value": "{ams.color.primary-black}" } | ||
}, | ||
"magenta": { | ||
"background-color": { "value": "{ams.color.magenta}" }, | ||
"color": { "value": "{ams.color.primary-white}" } | ||
}, | ||
"orange": { | ||
"background-color": { "value": "{ams.color.orange}" }, | ||
"color": { "value": "{ams.color.primary-black}" } | ||
}, | ||
"purple": { | ||
"background-color": { "value": "{ams.color.purple}" }, | ||
"color": { "value": "{ams.color.primary-white}" } | ||
}, | ||
"red": { | ||
"background-color": { "value": "{ams.color.primary-red}" }, | ||
"color": { "value": "{ams.color.primary-white}" } | ||
}, | ||
"yellow": { | ||
"background-color": { "value": "{ams.color.yellow}" }, | ||
"color": { "value": "{ams.color.primary-black}" } | ||
} | ||
} | ||
} | ||
} |
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,32 @@ | ||
import { Canvas, Controls, Markdown, Meta, Primary } from "@storybook/blocks"; | ||
import * as AvatarStories from "./Avatar.stories.tsx"; | ||
import README from "../../../../packages/css/src/components/avatar/README.md?raw"; | ||
|
||
<Meta of={AvatarStories} /> | ||
|
||
<Markdown>{README}</Markdown> | ||
|
||
## Stories | ||
|
||
### Default | ||
|
||
<Primary /> | ||
|
||
<Controls /> | ||
|
||
### With Picture | ||
|
||
The Avatar can also display a photo or other image for the person. | ||
Make sure to scale the image down to around 100 pixels to prevent unnecessary data transfers. | ||
|
||
<Canvas of={AvatarStories.WithPicture} /> | ||
|
||
### Fallback Icon | ||
|
||
A user icon displays if no label and image are provided. | ||
|
||
<Canvas of={AvatarStories.FallbackIcon} /> | ||
|
||
### In a Header | ||
|
||
<Canvas of={AvatarStories.InAHeader} /> |
Oops, something went wrong.