mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Updating AvatarWithBage to BadgeWrapper (#17851)
This commit is contained in:
parent
de83546d6d
commit
ed519d8b60
@ -1,75 +0,0 @@
|
||||
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.
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-avatarwithbadge--default-story" />
|
||||
</Canvas>
|
||||
|
||||
## Props
|
||||
|
||||
The `AvatarWithBadge` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) component props.
|
||||
|
||||
<ArgsTable of={AvatarWithBadge} />
|
||||
|
||||
### Badge Position
|
||||
|
||||
Use the `badgePosition` prop to set the position of the badge, it can have two values `Top` and `Bottom`
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-avatarwithbadge--badge-position" />
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { AvatarWithBadge } from '../ui/component-library';
|
||||
|
||||
<AvatarWithBadge
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.BOTTOM}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
size={Size.MD}
|
||||
type={TYPES.JAZZICON}
|
||||
/>
|
||||
</AvatarWithBadge>
|
||||
|
||||
<AvatarWithBadge
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.TOP}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
size={Size.MD}
|
||||
type={TYPES.JAZZICON}
|
||||
/>
|
||||
</AvatarWithBadge>
|
||||
```
|
||||
|
||||
### 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`
|
@ -1,25 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AvatarWithBadge should render correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box mm-avatar-with-badge box--flex-direction-row"
|
||||
data-testid="avatar-with-badge"
|
||||
>
|
||||
<div
|
||||
class="box mm-avatar-with-badge__badge-wrapper--position-bottom box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="box mm-avatar-base mm-avatar-base--size-md mm-avatar-network box--display-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-text-default box--background-color-background-alternative box--border-color-transparent box--border-style-solid box--border-width-1"
|
||||
data-testid="badge"
|
||||
>
|
||||
<img
|
||||
alt="Arbitrum One logo"
|
||||
class="mm-avatar-network__network-image"
|
||||
src="./images/arbitrum.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,4 +0,0 @@
|
||||
export const AVATAR_WITH_BADGE_POSTIONS = {
|
||||
TOP: 'top',
|
||||
BOTTOM: 'bottom',
|
||||
};
|
@ -1,60 +0,0 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import Box from '../../ui/box/box';
|
||||
import { AVATAR_WITH_BADGE_POSTIONS } from './avatar-with-badge.constants';
|
||||
|
||||
export const AvatarWithBadge = ({
|
||||
children,
|
||||
badgePosition,
|
||||
className,
|
||||
badge,
|
||||
badgeWrapperProps,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<Box className={classnames('mm-avatar-with-badge', className)} {...props}>
|
||||
{/* Generally the AvatarAccount */}
|
||||
{children}
|
||||
<Box
|
||||
className={
|
||||
badgePosition === 'top'
|
||||
? 'mm-avatar-with-badge__badge-wrapper--position-top'
|
||||
: 'mm-avatar-with-badge__badge-wrapper--position-bottom'
|
||||
}
|
||||
{...badgeWrapperProps}
|
||||
>
|
||||
{/* Generally the AvatarNetwork at SIZES.XS */}
|
||||
{badge}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
AvatarWithBadge.propTypes = {
|
||||
/**
|
||||
* The position of the Badge
|
||||
* Possible values could be 'top', 'bottom',
|
||||
*/
|
||||
badgePosition: PropTypes.oneOf(Object.values(AVATAR_WITH_BADGE_POSTIONS)),
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* AvatarWithBadge accepts all the props from Box
|
||||
*/
|
||||
...Box.propTypes,
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
.mm-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;
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
import React from 'react';
|
||||
import { AvatarAccount } from '../avatar-account';
|
||||
import { AVATAR_ACCOUNT_TYPES } from '../avatar-account/avatar-account.constants';
|
||||
import { AvatarNetwork } from '../avatar-network';
|
||||
import Box from '../../ui/box/box';
|
||||
import {
|
||||
AlignItems,
|
||||
DISPLAY,
|
||||
Size,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { AVATAR_WITH_BADGE_POSTIONS } from './avatar-with-badge.constants';
|
||||
import README from './README.mdx';
|
||||
import { AvatarWithBadge } from './avatar-with-badge';
|
||||
|
||||
export default {
|
||||
title: 'Components/ComponentLibrary/AvatarWithBadge',
|
||||
|
||||
component: AvatarWithBadge,
|
||||
parameters: {
|
||||
docs: {
|
||||
page: README,
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
badgePosition: {
|
||||
options: Object.values(AVATAR_WITH_BADGE_POSTIONS),
|
||||
control: 'select',
|
||||
},
|
||||
},
|
||||
args: {
|
||||
badgePosition: AVATAR_WITH_BADGE_POSTIONS.top,
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => (
|
||||
<AvatarWithBadge
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
/>
|
||||
}
|
||||
{...args}
|
||||
>
|
||||
<AvatarAccount
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
size={Size.MD}
|
||||
type={AVATAR_ACCOUNT_TYPES.JAZZICON}
|
||||
/>
|
||||
</AvatarWithBadge>
|
||||
);
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
||||
export const BadgePosition = () => (
|
||||
<Box display={DISPLAY.FLEX} alignItems={AlignItems.baseline} gap={1}>
|
||||
<AvatarWithBadge
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.BOTTOM}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
size={Size.MD}
|
||||
type={AVATAR_ACCOUNT_TYPES.JAZZICON}
|
||||
/>
|
||||
</AvatarWithBadge>
|
||||
|
||||
<AvatarWithBadge
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.TOP}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
size={Size.MD}
|
||||
type={AVATAR_ACCOUNT_TYPES.JAZZICON}
|
||||
/>
|
||||
</AvatarWithBadge>
|
||||
</Box>
|
||||
);
|
@ -1,102 +0,0 @@
|
||||
/* 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 { BorderColor } from '../../../helpers/constants/design-system';
|
||||
import { AvatarWithBadge } from './avatar-with-badge';
|
||||
import { AVATAR_WITH_BADGE_POSTIONS } from './avatar-with-badge.constants';
|
||||
|
||||
describe('AvatarWithBadge', () => {
|
||||
it('should render correctly', () => {
|
||||
const { getByTestId, container } = render(
|
||||
<AvatarWithBadge
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.BOTTOM}
|
||||
data-testid="avatar-with-badge"
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
data-testid="badge"
|
||||
/>
|
||||
}
|
||||
/>,
|
||||
);
|
||||
expect(getByTestId('avatar-with-badge')).toBeDefined();
|
||||
expect(getByTestId('badge')).toBeDefined();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render badge network with bottom right position correctly', () => {
|
||||
const { container } = render(
|
||||
<AvatarWithBadge
|
||||
data-testid="avatar-with-badge"
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.BOTTOM}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
data-testid="badge"
|
||||
/>
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
container.getElementsByClassName(
|
||||
'mm-avatar-with-badge__badge-wrapper--position-bottom',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should render badge network with top right position correctly', () => {
|
||||
const { container } = render(
|
||||
<AvatarWithBadge
|
||||
data-testid="avatar-with-badge"
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.TOP}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
data-testid="badge"
|
||||
/>
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
container.getElementsByClassName(
|
||||
'mm-avatar-with-badge__badge-wrapper--position-top',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
it('should render badge network with badgeWrapperProps', () => {
|
||||
const container = (
|
||||
<AvatarWithBadge
|
||||
data-testid="avatar-with-badge"
|
||||
badgePosition={AVATAR_WITH_BADGE_POSTIONS.TOP}
|
||||
badgeWrapperProps={{ borderColor: BorderColor.errorDefault }}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
name="Arbitrum One"
|
||||
src="./images/arbitrum.svg"
|
||||
data-testid="badge"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
expect(container.props.badgeWrapperProps.borderColor).toStrictEqual(
|
||||
'error-default',
|
||||
);
|
||||
});
|
||||
|
||||
// className
|
||||
it('should render with custom className', () => {
|
||||
const { getByTestId } = render(
|
||||
<AvatarWithBadge
|
||||
data-testid="avatar-with-badge"
|
||||
className="test-class"
|
||||
/>,
|
||||
);
|
||||
expect(getByTestId('avatar-with-badge')).toHaveClass('test-class');
|
||||
});
|
||||
});
|
@ -1,2 +0,0 @@
|
||||
export { AvatarWithBadge } from './avatar-with-badge';
|
||||
export { AVATAR_WITH_BADGE_POSTIONS } from './avatar-with-badge.constants';
|
378
ui/components/component-library/badge-wrapper/README.mdx
Normal file
378
ui/components/component-library/badge-wrapper/README.mdx
Normal file
@ -0,0 +1,378 @@
|
||||
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||
|
||||
import { BadgeWrapper } from './badge-wrapper';
|
||||
|
||||
# BadgeWrapper
|
||||
|
||||
The `BadgeWrapper` positions a badge on top of another component
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-badgewrapper--default-story" />
|
||||
</Canvas>
|
||||
|
||||
## Props
|
||||
|
||||
The `BadgeWrapper` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) component props.
|
||||
|
||||
<ArgsTable of={BadgeWrapper} />
|
||||
|
||||
### Children
|
||||
|
||||
Use the `children` prop to define the element to be wrapped by the `BadgeWrapper`. This element will be what the badge is positioned on top of.
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-badgewrapper--children" />
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { Color, Size } from '../../../helpers/constants/design-system';
|
||||
|
||||
import {
|
||||
AvatarAccount,
|
||||
AvatarNetwork,
|
||||
AvatarToken,
|
||||
BadgeWrapper,
|
||||
BadgeWrapperAnchorElementShape,
|
||||
BadgeWrapperPosition,
|
||||
} from '../../component-library';
|
||||
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarToken
|
||||
name="Eth"
|
||||
src="./images/eth_logo.svg"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.SM}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
>
|
||||
<Box
|
||||
as="img"
|
||||
src="./catnip-spicywright.png"
|
||||
borderRadius={BorderRadius.SM}
|
||||
borderColor={BorderColor.borderMuted}
|
||||
style={{ width: 100, height: 100 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
```
|
||||
|
||||
### Badge
|
||||
|
||||
Use the `badge` prop to define the badge component to be rendered on top of the `children` component.
|
||||
|
||||
To access the component containing the badge, use the `badgeContainerProps` prop. The wrapping component is a `Box` and accepts all box props.
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-badgewrapper--badge" />
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
|
||||
import {
|
||||
BorderColor,
|
||||
BorderRadius,
|
||||
Color,
|
||||
IconColor,
|
||||
Size,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
import {
|
||||
AvatarAccount,
|
||||
AvatarNetwork,
|
||||
BadgeWrapper,
|
||||
BadgeWrapperAnchorElementShape,
|
||||
Icon,
|
||||
ICON_NAMES,
|
||||
Tag,
|
||||
} from '../../component-library';
|
||||
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.successDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
borderColor={BorderColor.borderMuted}
|
||||
borderWidth={2}
|
||||
style={{ width: 12, height: 12 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name={ICON_NAMES.GLOBAL}
|
||||
size={Size.XL}
|
||||
color={IconColor.iconAlternative}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<Box
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingRight={1}
|
||||
paddingLeft={1}
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
borderRadius={BorderRadius.SM}
|
||||
style={{ alignSelf: 'flex-start' }}
|
||||
>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Tag
|
||||
label="9999"
|
||||
backgroundColor={Color.errorDefault}
|
||||
labelProps={{ color: Color.errorInverse }}
|
||||
borderColor={BorderColor.errorDefault}
|
||||
/>
|
||||
}
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
>
|
||||
<Box
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingRight={8}
|
||||
paddingLeft={8}
|
||||
borderRadius={BorderRadius.SM}
|
||||
borderColor={BorderColor.borderDefault}
|
||||
backgroundColor={Color.backgroundDefault}
|
||||
>
|
||||
NFTs
|
||||
</Box>
|
||||
</BadgeWrapper>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### Position
|
||||
|
||||
Use the `position` prop and the `BadgeWrapperPosition` enum to set the position of the badge. Possible positions are:
|
||||
|
||||
- top left `BadgeWrapperPosition.topLeft`
|
||||
- top right `BadgeWrapperPosition.topRight`
|
||||
- bottom left `BadgeWrapperPosition.bottomLeft`
|
||||
- bottom right `BadgeWrapperPosition.bottomRight`
|
||||
|
||||
If you require a custom position, you can use the `positionObj` prop see [Position Obj](/docs/components-componentlibrary-badgewrapper--position-obj#position-obj) for more details.
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-badgewrapper--position" />
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { BorderColor Size } from '../../../helpers/constants/design-system';
|
||||
|
||||
import {
|
||||
AvatarAccount,
|
||||
AvatarNetwork,
|
||||
BadgeWrapper,
|
||||
BadgeWrapperPosition
|
||||
} from '../../component-library';
|
||||
|
||||
<BadgeWrapper
|
||||
position={BadgeWrapperPosition.topLeft}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
position={BadgeWrapperPosition.bottomLeft}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
position={BadgeWrapperPosition.bottomRight}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
```
|
||||
|
||||
### Position Obj
|
||||
|
||||
Use the `positionObj` prop to set a custom position for the badge. The `positionObj` prop takes an object with the following properties:
|
||||
|
||||
- `top` - the top position of the badge
|
||||
- `right` - the right position of the badge
|
||||
- `bottom` - the bottom position of the badge
|
||||
- `left` - the left position of the badge
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-badgewrapper--position-obj" />
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { BorderColor Size } from '../../../helpers/constants/design-system';
|
||||
|
||||
import {
|
||||
AvatarAccount,
|
||||
AvatarNetwork,
|
||||
BadgeWrapper,
|
||||
} from '../../component-library';
|
||||
|
||||
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
positionObj={{ top: 4, right: -8 }}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"/>
|
||||
</BadgeWrapper>
|
||||
```
|
||||
|
||||
### Anchor Element Shape
|
||||
|
||||
Use the `anchorElementShape` prop and the `BadgeWrapperAnchorElementShape` enum to set the badge position relative to the shape of the anchor element. Possible shapes are:
|
||||
|
||||
- circular `BadgeWrapperAnchorElementShape.circular`
|
||||
- rectangular `BadgeWrapperAnchorElementShape.rectangular`
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-componentlibrary-badgewrapper--anchor-element-shape" />
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import Box from '../../ui/box/box';
|
||||
|
||||
import { BorderRadius, Color } from '../../../helpers/constants/design-system';
|
||||
|
||||
import { BadgeWrapper, BadgeWrapperAnchorElementShape } from '../../component-library';
|
||||
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 16, height: 16 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 8, height: 8 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 16, height: 16 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 8, height: 8 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
```
|
@ -0,0 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BadgeWrapper should render correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box mm-badge-wrapper box--display-inline-block box--flex-direction-row"
|
||||
>
|
||||
content
|
||||
<div
|
||||
class="box mm-badge-wrapper__badge-container mm-badge-wrapper__badge-container--circular-top-right box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
data-testid="badge"
|
||||
>
|
||||
badge
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Mixin that renders the CSS values for badge positions and value
|
||||
*/
|
||||
@mixin badgePosition($position, $value) {
|
||||
@if $position == top-right {
|
||||
top: $value;
|
||||
right: $value;
|
||||
transform: scale(1) translate(50%, -50%);
|
||||
transform-origin: 100% 0%;
|
||||
}
|
||||
|
||||
@else if $position == bottom-right {
|
||||
bottom: $value;
|
||||
right: $value;
|
||||
transform: scale(1) translate(50%, 50%);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
@else if $position == top-left {
|
||||
top: $value;
|
||||
left: $value;
|
||||
transform: scale(1) translate(-50%, -50%);
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
@else if $position == bottom-left {
|
||||
bottom: $value;
|
||||
left: $value;
|
||||
transform: scale(1) translate(-50%, 50%);
|
||||
transform-origin: 0% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mm-badge-wrapper {
|
||||
--badge-wrapper-position-circular: 14%;
|
||||
--badge-wrapper-position-rectangular: 0;
|
||||
|
||||
position: relative;
|
||||
align-self: start; // prevents stretching of badge-wrapper when in flexbox container to maintain badge positioning
|
||||
|
||||
&__badge-container {
|
||||
position: absolute;
|
||||
|
||||
&--circular {
|
||||
&-top-right {
|
||||
@include badgePosition('top-right', var(--badge-wrapper-position-circular));
|
||||
}
|
||||
|
||||
&-bottom-right {
|
||||
@include badgePosition('bottom-right', var(--badge-wrapper-position-circular));
|
||||
}
|
||||
|
||||
&-top-left {
|
||||
@include badgePosition('top-left', var(--badge-wrapper-position-circular));
|
||||
}
|
||||
|
||||
&-bottom-left {
|
||||
@include badgePosition('bottom-left', var(--badge-wrapper-position-circular));
|
||||
}
|
||||
}
|
||||
|
||||
&--rectangular {
|
||||
&-top-right {
|
||||
@include badgePosition('top-right', var(--badge-wrapper-position-rectangular));
|
||||
}
|
||||
|
||||
&-bottom-right {
|
||||
@include badgePosition('bottom-right', var(--badge-wrapper-position-rectangular));
|
||||
}
|
||||
|
||||
&-top-left {
|
||||
@include badgePosition('top-left', var(--badge-wrapper-position-rectangular));
|
||||
}
|
||||
|
||||
&-bottom-left {
|
||||
@include badgePosition('bottom-left', var(--badge-wrapper-position-rectangular));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,360 @@
|
||||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import {
|
||||
AlignItems,
|
||||
BorderColor,
|
||||
BorderRadius,
|
||||
Color,
|
||||
DISPLAY,
|
||||
IconColor,
|
||||
Size,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
import Box from '../../ui/box/box';
|
||||
|
||||
import {
|
||||
AvatarAccount,
|
||||
AvatarNetwork,
|
||||
AvatarToken,
|
||||
Icon,
|
||||
ICON_NAMES,
|
||||
Tag,
|
||||
} from '..';
|
||||
import {
|
||||
BadgeWrapperAnchorElementShape,
|
||||
BadgeWrapperPosition,
|
||||
} from './badge-wrapper.types';
|
||||
|
||||
import README from './README.mdx';
|
||||
|
||||
import { BadgeWrapper } from './badge-wrapper';
|
||||
|
||||
export default {
|
||||
title: 'Components/ComponentLibrary/BadgeWrapper',
|
||||
component: BadgeWrapper,
|
||||
parameters: {
|
||||
docs: {
|
||||
page: README,
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
children: {
|
||||
control: 'text',
|
||||
},
|
||||
badge: {
|
||||
control: 'text',
|
||||
},
|
||||
position: {
|
||||
options: Object.values(BadgeWrapperPosition),
|
||||
control: 'select',
|
||||
},
|
||||
positionObj: {
|
||||
control: 'object',
|
||||
},
|
||||
anchorElementShape: {
|
||||
options: Object.values(BadgeWrapperAnchorElementShape),
|
||||
control: 'select',
|
||||
},
|
||||
className: {
|
||||
control: 'text',
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof BadgeWrapper>;
|
||||
|
||||
const Template: ComponentStory<typeof BadgeWrapper> = (args) => (
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
{...args}
|
||||
>
|
||||
{args.children ? (
|
||||
args.children
|
||||
) : (
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
)}
|
||||
</BadgeWrapper>
|
||||
);
|
||||
|
||||
export const DefaultStory = Template.bind({});
|
||||
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
||||
export const Children = () => (
|
||||
<Box display={DISPLAY.FLEX} gap={4}>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarToken
|
||||
name="Eth"
|
||||
src="./images/eth_logo.svg"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.SM}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
>
|
||||
<Box
|
||||
as="img"
|
||||
src="./catnip-spicywright.png"
|
||||
borderRadius={BorderRadius.SM}
|
||||
borderColor={BorderColor.borderMuted}
|
||||
style={{ width: 100, height: 100 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Badge = () => (
|
||||
<Box display={DISPLAY.FLEX} gap={4}>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.successDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
borderColor={BorderColor.borderMuted}
|
||||
borderWidth={2}
|
||||
style={{ width: 12, height: 12 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name={ICON_NAMES.GLOBAL}
|
||||
size={Size.XL}
|
||||
color={IconColor.iconAlternative}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<Box
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingRight={1}
|
||||
paddingLeft={1}
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
borderRadius={BorderRadius.SM}
|
||||
style={{ alignSelf: 'flex-start' }}
|
||||
>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Tag
|
||||
label="9999"
|
||||
backgroundColor={Color.errorDefault}
|
||||
labelProps={{ color: Color.errorInverse }}
|
||||
borderColor={BorderColor.errorDefault}
|
||||
/>
|
||||
}
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
>
|
||||
<Box
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingRight={8}
|
||||
paddingLeft={8}
|
||||
borderRadius={BorderRadius.SM}
|
||||
borderColor={BorderColor.borderDefault}
|
||||
backgroundColor={Color.backgroundDefault}
|
||||
>
|
||||
NFTs
|
||||
</Box>
|
||||
</BadgeWrapper>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Position = () => (
|
||||
<Box display={DISPLAY.FLEX} gap={4}>
|
||||
<BadgeWrapper
|
||||
position={BadgeWrapperPosition.topLeft}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
position={BadgeWrapperPosition.bottomLeft}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
position={BadgeWrapperPosition.bottomRight}
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const PositionObj = () => (
|
||||
<Box display={DISPLAY.FLEX} alignItems={AlignItems.baseline} gap={4}>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<AvatarNetwork
|
||||
size={Size.XS}
|
||||
name="Avalanche"
|
||||
src="./images/avax-token.png"
|
||||
borderColor={BorderColor.borderMuted}
|
||||
/>
|
||||
}
|
||||
positionObj={{ top: 4, right: -8 }}
|
||||
>
|
||||
<AvatarAccount address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1" />
|
||||
</BadgeWrapper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const AnchorElementShape = () => (
|
||||
<Box display={DISPLAY.FLEX} gap={4}>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 16, height: 16 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 8, height: 8 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 16, height: 16 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 8, height: 8 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
style={{ width: 40, height: 40 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
badge={
|
||||
<Box
|
||||
backgroundColor={Color.errorDefault}
|
||||
borderRadius={BorderRadius.full}
|
||||
style={{ width: 16, height: 16 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={Color.infoDefault}
|
||||
style={{ width: 40, height: 80 }}
|
||||
/>
|
||||
</BadgeWrapper>
|
||||
</Box>
|
||||
);
|
@ -0,0 +1,143 @@
|
||||
/* eslint-disable jest/require-top-level-describe */
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { BadgeWrapper } from './badge-wrapper';
|
||||
import {
|
||||
BadgeWrapperPosition,
|
||||
BadgeWrapperAnchorElementShape,
|
||||
} from './badge-wrapper.types';
|
||||
|
||||
describe('BadgeWrapper', () => {
|
||||
it('should render correctly', () => {
|
||||
const { getByText, container } = render(
|
||||
<BadgeWrapper badge={<div data-testid="badge">badge</div>}>
|
||||
content
|
||||
</BadgeWrapper>,
|
||||
);
|
||||
expect(getByText('content')).toBeDefined();
|
||||
expect(getByText('badge')).toBeDefined();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with additional className', () => {
|
||||
const { getByTestId } = render(
|
||||
<BadgeWrapper data-testid="badge-wrapper" className="test-class">
|
||||
content
|
||||
</BadgeWrapper>,
|
||||
);
|
||||
expect(getByTestId('badge-wrapper')).toHaveClass('test-class');
|
||||
});
|
||||
|
||||
it('should render badge positions correctly', () => {
|
||||
const { getByTestId, getByText } = render(
|
||||
<>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-default' }}
|
||||
>
|
||||
content default
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-top-right' }}
|
||||
position={BadgeWrapperPosition.topRight}
|
||||
>
|
||||
content top-right
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-top-left' }}
|
||||
position={BadgeWrapperPosition.topLeft}
|
||||
>
|
||||
content top-left
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-bottom-right' }}
|
||||
position={BadgeWrapperPosition.bottomRight}
|
||||
>
|
||||
content bottom-right
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-bottom-left' }}
|
||||
position={BadgeWrapperPosition.bottomLeft}
|
||||
>
|
||||
content bottom-left
|
||||
</BadgeWrapper>
|
||||
</>,
|
||||
);
|
||||
expect(getByText('content default')).toBeDefined();
|
||||
expect(getByTestId('badge-default')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-top-right',
|
||||
);
|
||||
expect(getByText('content top-right')).toBeDefined();
|
||||
expect(getByTestId('badge-top-right')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-top-right',
|
||||
);
|
||||
expect(getByText('content top-left')).toBeDefined();
|
||||
expect(getByTestId('badge-top-left')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-top-left',
|
||||
);
|
||||
expect(getByText('content bottom-right')).toBeDefined();
|
||||
expect(getByTestId('badge-bottom-right')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-bottom-right',
|
||||
);
|
||||
expect(getByText('content bottom-left')).toBeDefined();
|
||||
expect(getByTestId('badge-bottom-left')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-bottom-left',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the badge with custom position', () => {
|
||||
const { getByTestId, getByText } = render(
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-custom' }}
|
||||
position={{
|
||||
top: -10,
|
||||
right: -10,
|
||||
}}
|
||||
>
|
||||
content custom
|
||||
</BadgeWrapper>,
|
||||
);
|
||||
expect(getByText('content custom')).toBeDefined();
|
||||
expect(getByTestId('badge-custom')).not.toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-top-right',
|
||||
);
|
||||
expect(getByTestId('badge-custom')).toHaveStyle({
|
||||
top: -10,
|
||||
right: -10,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render badge anchor element shape correctly', () => {
|
||||
const { getByTestId, getByText } = render(
|
||||
<>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-circular' }}
|
||||
>
|
||||
content circular
|
||||
</BadgeWrapper>
|
||||
<BadgeWrapper
|
||||
badge={<div>badge</div>}
|
||||
badgeContainerProps={{ 'data-testid': 'badge-rectangular' }}
|
||||
anchorElementShape={BadgeWrapperAnchorElementShape.rectangular}
|
||||
>
|
||||
content rectangular
|
||||
</BadgeWrapper>
|
||||
</>,
|
||||
);
|
||||
expect(getByText('content circular')).toBeDefined();
|
||||
expect(getByTestId('badge-circular')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--circular-top-right',
|
||||
);
|
||||
expect(getByText('content rectangular')).toBeDefined();
|
||||
expect(getByTestId('badge-rectangular')).toHaveClass(
|
||||
'mm-badge-wrapper__badge-container--rectangular-top-right',
|
||||
);
|
||||
});
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { DISPLAY } from '../../../helpers/constants/design-system';
|
||||
|
||||
import Box from '../../ui/box';
|
||||
|
||||
import {
|
||||
BadgeWrapperPosition,
|
||||
BadgeWrapperAnchorElementShape,
|
||||
BadgeWrapperProps,
|
||||
} from './badge-wrapper.types';
|
||||
|
||||
export const BadgeWrapper = ({
|
||||
children,
|
||||
badge,
|
||||
badgeContainerProps,
|
||||
position = BadgeWrapperPosition.topRight,
|
||||
positionObj,
|
||||
anchorElementShape = BadgeWrapperAnchorElementShape.circular,
|
||||
className = '',
|
||||
color,
|
||||
...props
|
||||
}: BadgeWrapperProps) => (
|
||||
<Box
|
||||
className={classnames('mm-badge-wrapper', className)}
|
||||
display={DISPLAY.INLINE_BLOCK}
|
||||
{...props}
|
||||
>
|
||||
{/* Generally the AvatarAccount or AvatarToken */}
|
||||
{children}
|
||||
<Box
|
||||
className={classnames('mm-badge-wrapper__badge-container', {
|
||||
[`mm-badge-wrapper__badge-container--${anchorElementShape}-${position}`]:
|
||||
!positionObj,
|
||||
})}
|
||||
style={{ ...positionObj }}
|
||||
{...badgeContainerProps}
|
||||
>
|
||||
{/* Generally the AvatarNetwork at SIZES.XS */}
|
||||
{badge}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
@ -0,0 +1,55 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import Box from '../../ui/box';
|
||||
import type { BoxProps } from '../../ui/box/box.d';
|
||||
|
||||
export enum BadgeWrapperPosition {
|
||||
topRight = 'top-right',
|
||||
bottomRight = 'bottom-right',
|
||||
topLeft = 'top-left',
|
||||
bottomLeft = 'bottom-left',
|
||||
}
|
||||
|
||||
export enum BadgeWrapperAnchorElementShape {
|
||||
rectangular = 'rectangular',
|
||||
circular = 'circular',
|
||||
}
|
||||
|
||||
export interface BadgeWrapperProps
|
||||
extends PropTypes.InferProps<typeof Box.propTypes>,
|
||||
React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* The element to be wrapped by the BadgeWrapper and for the badge to be positioned on top of
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Use the `badge` prop to define the badge component to be rendered on top of the `children` component
|
||||
*/
|
||||
badge?: React.ReactNode;
|
||||
/**
|
||||
* The BadgeWrapper props of the component. All Box props can be used
|
||||
*/
|
||||
badgeContainerProps?: BoxProps;
|
||||
/**
|
||||
* The position of the Badge. Possible values could be 'BadgeWrapperPosition.topRight', 'BadgeWrapperPosition.bottomRight','BadgeWrapperPosition.topLeft', 'BadgeWrapperPosition.bottomLeft'
|
||||
* Defaults to 'BadgeWrapperPosition.topRight'
|
||||
*/
|
||||
position?: BadgeWrapperPosition;
|
||||
/**
|
||||
* The positionObj can be used to override the default positioning of the badge it accepts an object with the following keys { top, right, bottom, left }
|
||||
*/
|
||||
positionObj?: {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
};
|
||||
/**
|
||||
* The shape of the anchor element. Possible values could be 'BadgeWrapperAnchorElementShape.circular', 'BadgeWrapperAnchorElementShape.square'
|
||||
* Defaults to
|
||||
*/
|
||||
anchorElementShape?: BadgeWrapperAnchorElementShape;
|
||||
/**
|
||||
* Additional classNames to be added to the BadgeWrapper component
|
||||
*/
|
||||
className?: string;
|
||||
}
|
7
ui/components/component-library/badge-wrapper/index.ts
Normal file
7
ui/components/component-library/badge-wrapper/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export { BadgeWrapper } from './badge-wrapper';
|
||||
export {
|
||||
BadgeWrapperPosition,
|
||||
BadgeWrapperAnchorElementShape,
|
||||
} from './badge-wrapper.types';
|
||||
|
||||
export type { BadgeWrapperProps } from './badge-wrapper.types';
|
@ -14,7 +14,7 @@
|
||||
@import 'avatar-favicon/avatar-favicon';
|
||||
@import 'avatar-network/avatar-network';
|
||||
@import 'avatar-token/avatar-token';
|
||||
@import 'avatar-with-badge/avatar-with-badge';
|
||||
@import 'badge-wrapper/badge-wrapper';
|
||||
@import 'button-base/button-base';
|
||||
@import 'button-icon/button-icon';
|
||||
@import 'button-link/button-link';
|
||||
|
@ -9,9 +9,10 @@ export { AvatarIcon, AVATAR_ICON_SIZES } from './avatar-icon';
|
||||
export { AvatarNetwork, AVATAR_NETWORK_SIZES } from './avatar-network';
|
||||
export { AvatarToken } from './avatar-token';
|
||||
export {
|
||||
AvatarWithBadge,
|
||||
AVATAR_WITH_BADGE_POSTIONS,
|
||||
} from './avatar-with-badge';
|
||||
BadgeWrapper,
|
||||
BadgeWrapperPosition,
|
||||
BadgeWrapperAnchorElementShape,
|
||||
} from './badge-wrapper';
|
||||
export { AvatarBase } from './avatar-base';
|
||||
export { Button, BUTTON_TYPES, BUTTON_SIZES } from './button';
|
||||
export { ButtonBase, BUTTON_BASE_SIZES } from './button-base';
|
||||
|
Loading…
Reference in New Issue
Block a user