diff --git a/core/components/atoms/avatar/Avatar.tsx b/core/components/atoms/avatar/Avatar.tsx index 489cb2d98..a3b8dfd78 100644 --- a/core/components/atoms/avatar/Avatar.tsx +++ b/core/components/atoms/avatar/Avatar.tsx @@ -63,6 +63,10 @@ export interface AvatarProps extends BaseProps { * Show presence indicator for the `Avatar` */ presence?: TPresence; + /** + * Show status indicator for the `Avatar` + */ + status?: React.ReactNode; /** * Stroke color of `Presence indicator` & `Status indicator` in `Avatar` */ @@ -88,6 +92,7 @@ export const Avatar = (props: AvatarProps) => { tooltipSuffix, tabIndex, presence, + status, strokeColor, role = 'presentation', } = props; @@ -112,6 +117,7 @@ export const Avatar = (props: AvatarProps) => { const darkAppearance = ['secondary', 'success', 'warning', 'accent1', 'accent4']; const showPresence = presence && !disabled && shape === 'round'; + const showStatus = status && size === 'regular' && shape === 'round'; const AvatarClassNames = classNames( { @@ -215,6 +221,11 @@ export const Avatar = (props: AvatarProps) => { {showPresence && ( )} + {showStatus && ( + + {status} + + )} ); diff --git a/core/components/atoms/avatar/__stories__/assets/status-image.png b/core/components/atoms/avatar/__stories__/assets/status-image.png new file mode 100644 index 000000000..a303c6ebe Binary files /dev/null and b/core/components/atoms/avatar/__stories__/assets/status-image.png differ diff --git a/core/components/atoms/avatar/__stories__/statusWithIcon.story.jsx b/core/components/atoms/avatar/__stories__/statusWithIcon.story.jsx new file mode 100644 index 000000000..178872189 --- /dev/null +++ b/core/components/atoms/avatar/__stories__/statusWithIcon.story.jsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { Avatar, Icon, Tooltip } from '@/index'; + +// CSF format story +export const statusWithIcon = () => { + return ( + + + + } + /> + ); +}; + +export default { + title: 'Components/Avatar/Avatar/Status/Status With Icon', + component: Avatar, + parameters: { + docs: { + docPage: { + title: 'Avatar', + }, + }, + }, +}; diff --git a/core/components/atoms/avatar/__stories__/statusWithImage.story.jsx b/core/components/atoms/avatar/__stories__/statusWithImage.story.jsx new file mode 100644 index 000000000..abded76a1 --- /dev/null +++ b/core/components/atoms/avatar/__stories__/statusWithImage.story.jsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { Avatar, Tooltip } from '@/index'; +import StatusImage from './assets/status-image.png'; + +// CSF format story +export const statusWithImage = () => { + return ( + + DND + + } + /> + ); +}; + +export default { + title: 'Components/Avatar/Avatar/Status/Status With Image', + component: Avatar, + parameters: { + docs: { + docPage: { + title: 'Avatar', + }, + }, + }, +}; diff --git a/core/components/atoms/avatar/__tests__/Avatar.test.tsx b/core/components/atoms/avatar/__tests__/Avatar.test.tsx index 51d743e69..3c160c82f 100644 --- a/core/components/atoms/avatar/__tests__/Avatar.test.tsx +++ b/core/components/atoms/avatar/__tests__/Avatar.test.tsx @@ -20,6 +20,8 @@ const sizes: AvatarSize[] = ['regular', 'tiny']; const shapes: AvatarShape[] = ['round', 'square']; const booleanValues = [true, false]; +const statusComponent =
status
; + describe('Avatar component', () => { const mapper = { appearance: valueHelper(appearances, { required: true, iterate: true }), @@ -270,3 +272,47 @@ describe('Avatar component with prop:presence', () => { expect(presenceEle).toHaveStyle('box-shadow: 0 0 0 var(--spacing-s) red'); }); }); + +describe('Avatar component with prop:status', () => { + it('should have the Avatar-status class when size is regular', () => { + const { getByTestId } = render(Design); + const statusElement = getByTestId('DesignSystem-Avatar--Status'); + expect(statusElement).toBeInTheDocument(); + expect(statusElement).toHaveClass('Avatar-status'); + }); + + it('should not have the Avatar-status class when size is tiny', () => { + render( + + Design + + ); + const statusElement = screen.queryByText('DesignSystem-Avatar--Status'); + expect(statusElement).not.toBeInTheDocument(); + }); + + it('should have the Avatar-status class when shape is round', () => { + const { getByTestId } = render(Design); + const statusElement = getByTestId('DesignSystem-Avatar--Status'); + expect(statusElement).toBeInTheDocument(); + expect(statusElement).toHaveClass('Avatar-status'); + }); + + it('should not have the Avatar-status class when shape is square', () => { + render( + + Design + + ); + const statusElement = screen.queryByText('DesignSystem-Avatar--Status'); + expect(statusElement).not.toBeInTheDocument(); + }); + + it('status should have custom stroke color', () => { + const { getByTestId } = render( + + ); + const statusElement = getByTestId('DesignSystem-Avatar--Status'); + expect(statusElement).toHaveStyle('box-shadow: 0 0 0 var(--spacing-s) red'); + }); +}); diff --git a/css/src/components/avatar.css b/css/src/components/avatar.css index 967464e25..017457548 100644 --- a/css/src/components/avatar.css +++ b/css/src/components/avatar.css @@ -123,3 +123,17 @@ .Avatar-presence--away { background: var(--secondary-dark); } + +.Avatar-status { + top: calc(-1 * var(--spacing-s)); + right: calc(-1 * var(--spacing-s)); + width: var(--spacing-l); + height: var(--spacing-l); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + position: absolute; + cursor: pointer; + overflow: hidden; +} diff --git a/figma/Avatar.figma.tsx b/figma/Avatar.figma.tsx index f7a10b192..44ccf896d 100644 --- a/figma/Avatar.figma.tsx +++ b/figma/Avatar.figma.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Avatar } from '@/index'; +import { Avatar, Icon, Tooltip } from '@/index'; import figma from '@figma/code-connect'; figma.connect(Avatar, 'https://www.figma.com/design/w8sqBtJpvq86D06UE7gN0T/MDS---Web?node-id=37-592', { @@ -39,6 +39,13 @@ figma.connect(Avatar, 'https://www.figma.com/design/w8sqBtJpvq86D06UE7gN0T/MDS-- icon: figma.enum('Type', { Icon: , }), + status: figma.enum('Status', { + true: ( + + + + ), + }), }, example: ({ icon, image, ...rest }) => (