diff --git a/ui/components/component-library/avatar-with-badge/README.mdx b/ui/components/component-library/avatar-with-badge/README.mdx new file mode 100644 index 000000000..17989b178 --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/README.mdx @@ -0,0 +1,37 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; + +import { AvatarWithBadge } from './avatar-with-badge'; + +# AvatarWithBadge + +The `AvatarWithBadge` is a wrapper component that adds badge display options to avatars. + + + + + +## Props + +The `AvatarWithBadge` accepts all props below + + + +### Badge Position + +Use the `badgePosition` prop to set the position of the badge, it can have two values `Top` and `Bottom` + + + + + +### Badge + +Used to define the badge component to be rendered inside the `AvatarWithBadge` + +### Badge Props + +The required props to be passed to the badge. + +### Children + +The children to be rendered inside the AvatarWithBadge. Generally used with the `AvatarAccount` \ No newline at end of file diff --git a/ui/components/component-library/avatar-with-badge/avatar-with-badge.constants.js b/ui/components/component-library/avatar-with-badge/avatar-with-badge.constants.js new file mode 100644 index 000000000..9cb89439e --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/avatar-with-badge.constants.js @@ -0,0 +1,4 @@ +export const BADGE_POSITIONS = { + TOP: 'top', + BOTTOM: 'bottom', +}; diff --git a/ui/components/component-library/avatar-with-badge/avatar-with-badge.js b/ui/components/component-library/avatar-with-badge/avatar-with-badge.js new file mode 100644 index 000000000..d55147d4b --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/avatar-with-badge.js @@ -0,0 +1,56 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import Box from '../../ui/box/box'; +import { BADGE_POSITIONS } from './avatar-with-badge.constants'; + +export const AvatarWithBadge = ({ + children, + badgePosition, + className, + badge, + badgeWrapperProps, + ...props +}) => { + return ( + + {/* Generally the AvatarAccount */} + {children} + + {/* Generally the AvatarNetwork at SIZES.XS */} + {badge} + + + ); +}; + +AvatarWithBadge.propTypes = { + /** + * The position of the Badge + * Possible values could be 'top', 'bottom', + */ + badgePosition: PropTypes.oneOf(Object.values(BADGE_POSITIONS)), + /** + * The Badge Wrapper props of the component. All Box props can be used + */ + badgeWrapperProps: PropTypes.shape(Box.PropTypes), + /** + * The children to be rendered inside the AvatarWithBadge + */ + children: PropTypes.node, + /** + * The badge to be rendered inside the AvatarWithBadge + */ + badge: PropTypes.object, + /** + * Add custom css class + */ + className: PropTypes.string, +}; diff --git a/ui/components/component-library/avatar-with-badge/avatar-with-badge.scss b/ui/components/component-library/avatar-with-badge/avatar-with-badge.scss new file mode 100644 index 000000000..27adfcdd7 --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/avatar-with-badge.scss @@ -0,0 +1,16 @@ +.avatar-with-badge { + position: relative; + width: fit-content; + + &__badge-wrapper--position-top { + position: absolute; + top: -4px; + right: -4px; + } + + &__badge-wrapper--position-bottom { + position: absolute; + bottom: -4px; + right: -4px; + } +} diff --git a/ui/components/component-library/avatar-with-badge/avatar-with-badge.stories.js b/ui/components/component-library/avatar-with-badge/avatar-with-badge.stories.js new file mode 100644 index 000000000..b489c3671 --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/avatar-with-badge.stories.js @@ -0,0 +1,91 @@ +import React from 'react'; +import { AvatarAccount } from '../avatar-account'; +import { TYPES } from '../avatar-account/avatar-account.constants'; +import { AvatarNetwork } from '../avatar-network'; +import Box from '../../ui/box/box'; +import { + ALIGN_ITEMS, + DISPLAY, + SIZES, +} from '../../../helpers/constants/design-system'; +import { BADGE_POSITIONS } from './avatar-with-badge.constants'; +import README from './README.mdx'; +import { AvatarWithBadge } from './avatar-with-badge'; + +export default { + title: 'Components/ComponentLibrary/AvatarWithBadge', + id: __filename, + component: AvatarWithBadge, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + badgePosition: { + options: Object.values(BADGE_POSITIONS), + control: 'select', + }, + }, + args: { + badgePosition: BADGE_POSITIONS.top, + }, +}; + +export const DefaultStory = (args) => ( + + } + {...args} + > + + +); +DefaultStory.storyName = 'Default'; + +export const BadgePosition = () => ( + + + } + > + + + + + } + > + + + +); diff --git a/ui/components/component-library/avatar-with-badge/avatar-with-badge.test.js b/ui/components/component-library/avatar-with-badge/avatar-with-badge.test.js new file mode 100644 index 000000000..b8a317a88 --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/avatar-with-badge.test.js @@ -0,0 +1,90 @@ +/* eslint-disable jest/require-top-level-describe */ +import { render } from '@testing-library/react'; +import React from 'react'; +import { AvatarNetwork } from '../avatar-network/avatar-network'; +import { COLORS } from '../../../helpers/constants/design-system'; +import { AvatarWithBadge } from './avatar-with-badge'; +import { BADGE_POSITIONS } from './avatar-with-badge.constants'; + +describe('AvatarWithBadge', () => { + it('should render correctly', () => { + const { getByTestId } = render( + + } + />, + ); + expect(getByTestId('avatar-with-badge')).toBeDefined(); + expect(getByTestId('badge')).toBeDefined(); + }); + + it('should render badge network with bottom right position correctly', () => { + const { container } = render( + + } + />, + ); + + expect( + container.getElementsByClassName( + 'avatar-with-badge__badge-wrapper--position-bottom', + ), + ).toHaveLength(1); + }); + + it('should render badge network with top right position correctly', () => { + const { container } = render( + + } + />, + ); + + expect( + container.getElementsByClassName( + 'avatar-with-badge__badge-wrapper--position-top', + ), + ).toHaveLength(1); + }); + it('should render badge network with badgeWrapperProps', () => { + const container = ( + + } + /> + ); + expect(container.props.badgeWrapperProps.borderColor).toStrictEqual( + 'error-default', + ); + }); +}); diff --git a/ui/components/component-library/avatar-with-badge/index.js b/ui/components/component-library/avatar-with-badge/index.js new file mode 100644 index 000000000..5fcefde68 --- /dev/null +++ b/ui/components/component-library/avatar-with-badge/index.js @@ -0,0 +1 @@ +export { AvatarWithBadge } from './avatar-with-badge'; diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss index 0017b00e6..ba86f58f0 100644 --- a/ui/components/component-library/component-library-components.scss +++ b/ui/components/component-library/component-library-components.scss @@ -2,6 +2,7 @@ @import 'avatar-account/avatar-account'; @import 'avatar-network/avatar-network'; @import 'avatar-token/avatar-token'; +@import 'avatar-with-badge/avatar-with-badge'; @import 'base-avatar/base-avatar'; @import 'button-base/button-base'; @import 'button-primary/button-primary';