Repository: zeit-ui/react (ts)

Issue:
Avatar with Icon
# Feature request 🚀
option to add Icon (badge) to Avatar Component.

<img width="251" alt="ss 2020-04-29 at 7 13 37 PM" src="https://user-images.githubusercontent.com/10662231/80655984-0be72000-8a4e-11ea-8c5c-90d8bf04b7f1.png">
<img width="272" alt="ss 2020-04-29 at 7 17 27 PM" src="https://user-images.githubusercontent.com/10662231/80655996-15708800-8a4e-11ea-93a5-35196d13d725.png">


### Expected:
  - No breaking changes

### Examples:
```js
```

### Programme:

### Others:






Pull request description:
feat(badge): supoort to anchor with other components
## PR Checklist

- [x] Fix linting errors
- [x] Label has been added


## Change information

resolved #156 


Patch:
diff --git a/components/badge/badge-anchor.tsx b/components/badge/badge-anchor.tsx
new file mode 100644
index 0000000..7120bd3
--- /dev/null
+++ b/components/badge/badge-anchor.tsx
@@ -0,0 +1,102 @@
+import React, { useMemo } from 'react'
+import withDefaults from '../utils/with-defaults'
+import { pickChild } from '../utils/collections'
+import { tuple } from '../utils/prop-types'
+import Badge from './badge'
+
+const placement = tuple(
+  'topLeft', 'topRight', 'bottomLeft', 'bottomRight',
+)
+
+type BadgeAnchorPlacement = typeof placement[number]
+
+interface Props {
+  placement?: BadgeAnchorPlacement
+  className?: string
+}
+
+const defaultProps = {
+  placement: 'topRight' as BadgeAnchorPlacement,
+  className: '',
+}
+
+type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
+export type BadgeAnchorProps = Props & typeof defaultProps & NativeAttrs
+
+type TransformStyles = {
+  top?: string
+  bottom?: string
+  left?: string
+  right?: string
+  value: string
+  origin: string
+}
+
+const getTransform = (placement: BadgeAnchorPlacement): TransformStyles => {
+  const styles: { [key in BadgeAnchorPlacement]: TransformStyles } = {
+    topLeft: {
+      top: '0', left: '0',
+      value: 'translate(-50%, -50%)',
+      origin: '0% 0%',
+    },
+    topRight: {
+      top: '0', right: '0',
+      value: 'translate(50%, -50%)',
+      origin: '100% 0%',
+    },
+    bottomLeft: {
+      left: '0', bottom: '0',
+      value: 'translate(-50%, 50%)',
+      origin: '0% 100%',
+    },
+    bottomRight: {
+      right: '0', bottom: '0',
+      value: 'translate(50%, 50%)',
+      origin: '100% 100%',
+    }
+  }
+  return styles[placement]
+}
+
+const BadgeAnchor: React.FC<React.PropsWithChildren<BadgeAnchorProps>> = ({
+  children, placement,
+}) => {
+  const [withoutBadgeChildren, badgeChldren] = pickChild(children, Badge)
+  const {
+    top, bottom, left, right, value, origin,
+  } = useMemo(() => getTransform(placement), [placement])
+  
+  return (
+    <div className="anchor">
+      {withoutBadgeChildren}
+      <sup>
+        {badgeChldren}
+      </sup>
+  
+      <style jsx>{`
+        .anchor {
+          position: relative;
+          display: inline-flex;
+          vertical-align: middle;
+          flex-shrink: 0;
+          box-sizing: border-box;
+        }
+
+        sup {
+          position: absolute;
+          top: ${top || 'auto'};
+          left: ${left || 'auto'};
+          right: ${right || 'auto'};
+          bottom: ${bottom || 'auto'};
+          transform: ${value};
+          transform-origin: ${origin};
+          z-index: 1;
+        }
+      `}</style>
+    </div>
+  )
+}
+
+const MemoBadgeAnchor = React.memo(BadgeAnchor)
+
+export default withDefaults(MemoBadgeAnchor, defaultProps)
diff --git a/components/badge/badge.tsx b/components/badge/badge.tsx
index 047fd96..64dfdec 100644
--- a/components/badge/badge.tsx
+++ b/components/badge/badge.tsx
@@ -1,18 +1,20 @@
 import React, { useMemo } from 'react'
-import withDefaults from '../utils/with-defaults'
 import useTheme from '../styles/use-theme'
 import { NormalSizes, NormalTypes } from '../utils/prop-types'
 import { ZeitUIThemesPalette } from 'components/styles/themes'
+import BadgeAnchor from './badge-anchor'
 
 interface Props {
   type?: NormalTypes
   size?: NormalSizes
+  dot?: boolean
   className?: string
 }
 
 const defaultProps = {
   type: 'default' as NormalTypes,
   size: 'medium' as NormalSizes,
+  dot: false,
   className: '',
 }
 
@@ -41,7 +43,7 @@ const getBgColor = (type: NormalTypes, palette: ZeitUIThemesPalette) => {
 }
 
 const Badge: React.FC<React.PropsWithChildren<BadgeProps>> = ({
-  type, size, className, children, ...props
+  type, size, className, children, dot, ...props
 }) => {
   const theme = useTheme()
   const bg = useMemo(() => getBgColor(type, theme.palette), [type, theme.palette])
@@ -52,8 +54,8 @@ const Badge: React.FC<React.PropsWithChildren<BadgeProps>> = ({
   }, [type, theme.palette.background])
   
   return (
-    <span className={className} {...props}>
-      {children}
+    <span className={`${dot ? 'dot' : ''} ${className}`} {...props}>
+      {!dot && children}
       <style jsx>{`
         span {
           display: inline-block;
@@ -67,11 +69,21 @@ const Badge: React.FC<React.PropsWithChildren<BadgeProps>> = ({
           font-size: ${font};
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       `}</style>
     </span>
   )
 }
 
-const MemoBadge = React.memo<React.PropsWithChildren<BadgeProps>>(Badge)
+type MemoBadgeComponent<P = {}> = React.NamedExoticComponent<P> & {
+  Anchor: typeof BadgeAnchor
+}
+type ComponentProps = Partial<typeof defaultProps> & Omit<Props, keyof typeof defaultProps> & NativeAttrs
+
+Badge.defaultProps = defaultProps
 
-export default withDefaults(MemoBadge, defaultProps)
+export default React.memo(Badge) as MemoBadgeComponent<ComponentProps>
diff --git a/components/badge/index.ts b/components/badge/index.ts
index a712806..191d733 100644
--- a/components/badge/index.ts
+++ b/components/badge/index.ts
@@ -1,3 +1,6 @@
 import Badge from './badge'
+import BadgeAnchor from './badge-anchor'
+
+Badge.Anchor = BadgeAnchor
 
 export default Badge
diff --git a/pages/en-us/components/badge.mdx b/pages/en-us/components/badge.mdx
index 953380e..38a0fbc 100644
--- a/pages/en-us/components/badge.mdx
+++ b/pages/en-us/components/badge.mdx
@@ -1,5 +1,5 @@
 import { Layout, Playground, Attributes } from 'lib/components'
-import { Badge, Spacer } from 'components'
+import { Badge, Spacer, Avatar, Button, Link } from 'components'
 
 export const meta = {
   title: 'Badge',
@@ -48,16 +48,78 @@ Display an indicator that requires attention.
   </>
 `} />
 
+<Playground
+  title="Anchor"
+  desc="Fix the `Badge` in the designated position."
+  scope={{ Badge, Avatar, Spacer, Button, Link }}
+  code={`
+<>
+  <Badge.Anchor>
+    <Badge size="mini">10</Badge>
+    <Avatar src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor placement="bottomRight">
+    <Badge size="mini" type="success">10</Badge>
+    <Avatar size={40} isSquare src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor>
+    <Badge size="mini" type="warning">99+</Badge>
+    <Button size="small" auto>Action</Button>
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor>
+    <Badge size="mini" type="error" dot />
+    <Link pure target="_blank" href="https://github.com/zeit-ui/react/">ZEIT UI</Link>
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor>
+    <Badge size="mini" type="error" dot style={{ padding: '7px' }} />
+    <Link pure target="_blank" href="https://github.com/zeit-ui/react/">Share Link</Link>
+  </Badge.Anchor>
+</>
+`} />
 
 <Attributes edit="/pages/en-us/components/badge.mdx">
 <Attributes.Title>Badge.Props</Attributes.Title>
 
 | Attribute | Description | Type | Accepted values | Default
 | ---------- | ---------- | ---- |  -------------- | ------ |
-| **type** | badge type | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |
-| **size** | badge size | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |
+| **type** | badge type | `NormalTypes` | [NormalTypes](#normaltypes) | `default` |
+| **size** | badge size | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
+| **dot** | show dot and ignore content | `boolean` | - | `false` |
+| ... | native props | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
+
+<Attributes.Title>Badge.Anchor.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **placement** | fixe position of Badge | `AnchorPlacement` | [AnchorPlacement](#anchorplacement) | `topRight` |
 | ... | native props | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
 
+<Attributes.Title>NormalTypes</Attributes.Title>
+
+```ts
+type NormalTypes = 'default'
+  | 'secondary'
+  | 'success'
+  | 'warning'
+  | 'error'
+```
+
+<Attributes.Title>NormalSizes</Attributes.Title>
+
+```ts
+type NormalSizes = 'medium' | 'mini' | 'small' | 'large'
+```
+
+<Attributes.Title>AnchorPlacement</Attributes.Title>
+
+```ts
+type AnchorPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
+```
+
 </Attributes>
 
 export default ({ children }) => <Layout meta={meta}>{children}</Layout>
diff --git a/pages/zh-cn/components/badge.mdx b/pages/zh-cn/components/badge.mdx
index 4ceabbd..b7409f7 100644
--- a/pages/zh-cn/components/badge.mdx
+++ b/pages/zh-cn/components/badge.mdx
@@ -1,5 +1,5 @@
 import { Layout, Playground, Attributes } from 'lib/components'
-import { Badge, Spacer } from 'components'
+import { Badge, Spacer, Button, Link, Avatar } from 'components'
 
 export const meta = {
   title: '徽章 Badge',
@@ -48,16 +48,78 @@ export const meta = {
   </>
 `} />
 
+<Playground
+  title="锚点"
+  desc="将徽章固定在指定位置。"
+  scope={{ Badge, Avatar, Spacer, Button, Link }}
+  code={`
+<>
+  <Badge.Anchor>
+    <Badge size="mini">10</Badge>
+    <Avatar src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor placement="bottomRight">
+    <Badge size="mini" type="success">10</Badge>
+    <Avatar size={40} isSquare src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor>
+    <Badge size="mini" type="warning">99+</Badge>
+    <Button size="small" auto>按钮</Button>
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor>
+    <Badge size="mini" type="error" dot />
+    <Link pure target="_blank" href="https://github.com/zeit-ui/react/">组件库</Link>
+  </Badge.Anchor>
+  <Spacer inline x={1.5} />
+  <Badge.Anchor>
+    <Badge size="mini" type="error" dot style={{ padding: '7px' }} />
+    <Link pure target="_blank" href="https://github.com/zeit-ui/react/">分享链接</Link>
+  </Badge.Anchor>
+</>
+`} />
 
 <Attributes edit="/pages/zh-cn/components/badge.mdx">
 <Attributes.Title>Badge.Props</Attributes.Title>
 
 | 属性 | 描述 | 类型 | 推荐值 | 默认
 | ---------- | ---------- | ---- |  -------------- | ------ |
-| **type** | 徽章的类型 | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |
-| **size** | 徽章的大小 | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |
+| **type** | 徽章的类型 | `NormalTypes` | [NormalTypes](#normaltypes) | `default` |
+| **size** | 徽章的大小 | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
+| **dot** | 忽略内容并显示圆点 | `boolean` | - | `false` |
+| ... | 原生属性 | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
+
+<Attributes.Title>Badge.Anchor.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **placement** | 固定徽章的位置 | `AnchorPlacement` | [AnchorPlacement](#anchorplacement) | `topRight` |
 | ... | 原生属性 | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
 
+<Attributes.Title>NormalTypes</Attributes.Title>
+
+```ts
+type NormalTypes = 'default'
+  | 'secondary'
+  | 'success'
+  | 'warning'
+  | 'error'
+```
+
+<Attributes.Title>NormalSizes</Attributes.Title>
+
+```ts
+type NormalSizes = 'medium' | 'mini' | 'small' | 'large'
+```
+
+<Attributes.Title>AnchorPlacement</Attributes.Title>
+
+```ts
+type AnchorPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
+```
+
 </Attributes>
 
 export default ({ children }) => <Layout meta={meta}>{children}</Layout>


Test patch:
diff --git a/components/badge/__tests__/__snapshots__/anchor.test.tsx.snap b/components/badge/__tests__/__snapshots__/anchor.test.tsx.snap
new file mode 100644
index 0000000..35c4750
--- /dev/null
+++ b/components/badge/__tests__/__snapshots__/anchor.test.tsx.snap
@@ -0,0 +1,124 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`BadgeAnchor should be support multiple position 1`] = `
+"<div class=\\"anchor\\"><button>btn</button><sup><span class=\\" \\">test<style>
+        span {
+          display: inline-block;
+          padding: 4px 7px;
+          border-radius: 16px;
+          font-variant: tabular-nums;
+          line-height: 1;
+          vertical-align: middle;
+          background-color: #000;
+          color: #fff;
+          font-size: .875rem;
+          border: 0;
+        }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
+      </style></span></sup><style>
+        .anchor {
+          position: relative;
+          display: inline-flex;
+          vertical-align: middle;
+          flex-shrink: 0;
+          box-sizing: border-box;
+        }
+
+        sup {
+          position: absolute;
+          top: 0;
+          left: auto;
+          right: 0;
+          bottom: auto;
+          transform: translate(50%, -50%);
+          transform-origin: 100% 0%;
+          z-index: 1;
+        }
+      </style></div>"
+`;
+
+exports[`BadgeAnchor should be support multiple position 2`] = `
+"<div class=\\"anchor\\"><button>btn</button><sup><span class=\\" \\">test<style>
+        span {
+          display: inline-block;
+          padding: 4px 7px;
+          border-radius: 16px;
+          font-variant: tabular-nums;
+          line-height: 1;
+          vertical-align: middle;
+          background-color: #000;
+          color: #fff;
+          font-size: .875rem;
+          border: 0;
+        }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
+      </style></span></sup><style>
+        .anchor {
+          position: relative;
+          display: inline-flex;
+          vertical-align: middle;
+          flex-shrink: 0;
+          box-sizing: border-box;
+        }
+
+        sup {
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: auto;
+          bottom: auto;
+          transform: translate(-50%, -50%);
+          transform-origin: 0% 0%;
+          z-index: 1;
+        }
+      </style></div>"
+`;
+
+exports[`BadgeAnchor should be support multiple position 3`] = `
+"<div class=\\"anchor\\"><button>btn</button><sup><span class=\\" \\">test<style>
+        span {
+          display: inline-block;
+          padding: 4px 7px;
+          border-radius: 16px;
+          font-variant: tabular-nums;
+          line-height: 1;
+          vertical-align: middle;
+          background-color: #000;
+          color: #fff;
+          font-size: .875rem;
+          border: 0;
+        }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
+      </style></span></sup><style>
+        .anchor {
+          position: relative;
+          display: inline-flex;
+          vertical-align: middle;
+          flex-shrink: 0;
+          box-sizing: border-box;
+        }
+
+        sup {
+          position: absolute;
+          top: auto;
+          left: auto;
+          right: 0;
+          bottom: 0;
+          transform: translate(50%, 50%);
+          transform-origin: 100% 100%;
+          z-index: 1;
+        }
+      </style></div>"
+`;
diff --git a/components/badge/__tests__/__snapshots__/index.test.tsx.snap b/components/badge/__tests__/__snapshots__/index.test.tsx.snap
index 761d646..ec27c54 100644
--- a/components/badge/__tests__/__snapshots__/index.test.tsx.snap
+++ b/components/badge/__tests__/__snapshots__/index.test.tsx.snap
@@ -11,7 +11,7 @@ initialize {
     "children": Array [
       Object {
         "attribs": Object {
-          "class": "",
+          "class": " ",
         },
         "children": Array [
           Object {
@@ -33,6 +33,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -70,6 +75,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                 "next": null,
                 "parent": [Circular],
@@ -97,7 +107,7 @@ initialize {
         "namespace": "http://www.w3.org/1999/xhtml",
         "next": Object {
           "attribs": Object {
-            "class": "",
+            "class": " ",
           },
           "children": Array [
             Object {
@@ -119,6 +129,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -156,6 +171,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -183,7 +203,7 @@ initialize {
           "namespace": "http://www.w3.org/1999/xhtml",
           "next": Object {
             "attribs": Object {
-              "class": "",
+              "class": " ",
             },
             "children": Array [
               Object {
@@ -205,6 +225,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                       "next": null,
                       "parent": [Circular],
@@ -242,6 +267,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -269,7 +299,7 @@ initialize {
             "namespace": "http://www.w3.org/1999/xhtml",
             "next": Object {
               "attribs": Object {
-                "class": "",
+                "class": " ",
               },
               "children": Array [
                 Object {
@@ -291,6 +321,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                         "next": null,
                         "parent": [Circular],
@@ -328,6 +363,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                       "next": null,
                       "parent": [Circular],
@@ -396,7 +436,7 @@ initialize {
       },
       Object {
         "attribs": Object {
-          "class": "",
+          "class": " ",
         },
         "children": Array [
           Object {
@@ -418,6 +458,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -455,6 +500,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                 "next": null,
                 "parent": [Circular],
@@ -482,7 +532,7 @@ initialize {
         "namespace": "http://www.w3.org/1999/xhtml",
         "next": Object {
           "attribs": Object {
-            "class": "",
+            "class": " ",
           },
           "children": Array [
             Object {
@@ -504,6 +554,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -541,6 +596,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -568,7 +628,7 @@ initialize {
           "namespace": "http://www.w3.org/1999/xhtml",
           "next": Object {
             "attribs": Object {
-              "class": "",
+              "class": " ",
             },
             "children": Array [
               Object {
@@ -590,6 +650,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                       "next": null,
                       "parent": [Circular],
@@ -627,6 +692,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -676,7 +746,7 @@ initialize {
         "parent": [Circular],
         "prev": Object {
           "attribs": Object {
-            "class": "",
+            "class": " ",
           },
           "children": Array [
             Object {
@@ -698,6 +768,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -735,6 +810,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -781,7 +861,7 @@ initialize {
       },
       Object {
         "attribs": Object {
-          "class": "",
+          "class": " ",
         },
         "children": Array [
           Object {
@@ -803,6 +883,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -840,6 +925,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                 "next": null,
                 "parent": [Circular],
@@ -867,7 +957,7 @@ initialize {
         "namespace": "http://www.w3.org/1999/xhtml",
         "next": Object {
           "attribs": Object {
-            "class": "",
+            "class": " ",
           },
           "children": Array [
             Object {
@@ -889,6 +979,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -926,6 +1021,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -965,7 +1065,7 @@ initialize {
         "parent": [Circular],
         "prev": Object {
           "attribs": Object {
-            "class": "",
+            "class": " ",
           },
           "children": Array [
             Object {
@@ -987,6 +1087,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -1024,6 +1129,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -1053,7 +1163,7 @@ initialize {
           "parent": [Circular],
           "prev": Object {
             "attribs": Object {
-              "class": "",
+              "class": " ",
             },
             "children": Array [
               Object {
@@ -1075,6 +1185,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                       "next": null,
                       "parent": [Circular],
@@ -1112,6 +1227,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -1166,7 +1286,7 @@ initialize {
       },
       Object {
         "attribs": Object {
-          "class": "",
+          "class": " ",
         },
         "children": Array [
           Object {
@@ -1188,6 +1308,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -1225,6 +1350,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                 "next": null,
                 "parent": [Circular],
@@ -1254,7 +1384,7 @@ initialize {
         "parent": [Circular],
         "prev": Object {
           "attribs": Object {
-            "class": "",
+            "class": " ",
           },
           "children": Array [
             Object {
@@ -1276,6 +1406,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -1313,6 +1448,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                   "next": null,
                   "parent": [Circular],
@@ -1342,7 +1482,7 @@ initialize {
           "parent": [Circular],
           "prev": Object {
             "attribs": Object {
-              "class": "",
+              "class": " ",
             },
             "children": Array [
               Object {
@@ -1364,6 +1504,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                       "next": null,
                       "parent": [Circular],
@@ -1401,6 +1546,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                     "next": null,
                     "parent": [Circular],
@@ -1430,7 +1580,7 @@ initialize {
             "parent": [Circular],
             "prev": Object {
               "attribs": Object {
-                "class": "",
+                "class": " ",
               },
               "children": Array [
                 Object {
@@ -1452,6 +1602,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                         "next": null,
                         "parent": [Circular],
@@ -1489,6 +1644,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
                       "next": null,
                       "parent": [Circular],
@@ -1588,7 +1748,7 @@ exports[`Badge should supoort text 1`] = `
 initialize {
   "0": Object {
     "attribs": Object {
-      "class": "",
+      "class": " ",
     },
     "children": Array [
       Object {
@@ -1610,6 +1770,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
               "next": null,
               "parent": [Circular],
@@ -1647,6 +1812,11 @@ initialize {
           font-size: .875rem;
           border: 0;
         }
+        
+        .dot {
+          padding: 4px;
+          border-radius: 50%;
+        }
       ",
             "next": null,
             "parent": [Circular],
diff --git a/components/badge/__tests__/anchor.test.tsx b/components/badge/__tests__/anchor.test.tsx
new file mode 100644
index 0000000..e722141
--- /dev/null
+++ b/components/badge/__tests__/anchor.test.tsx
@@ -0,0 +1,40 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { Badge } from 'components'
+
+describe('BadgeAnchor', () => {
+  it('should render correctly', () => {
+    const wrapper = mount(
+      <Badge.Anchor>
+        <Badge>test</Badge>
+        <a>link</a>
+      </Badge.Anchor>
+    )
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
+  
+  it('should be work without Badge', () => {
+    const wrapper = mount(
+      <Badge.Anchor>
+        <a>link</a>
+      </Badge.Anchor>
+    )
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
+  
+  it('should be support multiple position', () => {
+    const wrapper = mount(
+      <Badge.Anchor>
+        <Badge>test</Badge>
+        <button>btn</button>
+      </Badge.Anchor>
+    )
+    expect(wrapper.html()).toMatchSnapshot()
+    
+    wrapper.setProps({ placement: 'topLeft' })
+    expect(wrapper.html()).toMatchSnapshot()
+  
+    wrapper.setProps({ placement: 'bottomRight' })
+    expect(wrapper.html()).toMatchSnapshot()
+  })
+})
diff --git a/components/badge/__tests__/index.test.tsx b/components/badge/__tests__/index.test.tsx
index 808491b..45f4d34 100644
--- a/components/badge/__tests__/index.test.tsx
+++ b/components/badge/__tests__/index.test.tsx
@@ -52,4 +52,10 @@ describe('Badge', () => {
     expect(span.props().style).not.toBeUndefined()
     expect((span.props().style as any).background).toBe('white')
   })
+  
+  it('should hide content when in dot mode', () => {
+    const wrapper = mount(<Badge dot>test-value</Badge>)
+    expect(wrapper.html()).not.toContain('test-value')
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
 })