Repository: zeit-ui/react
Pull request #281 (2020-06-20 05:26:31)

chore: release v1.7.0
## PR Checklist

- [x] Fix linting errors
- [x] Label has been added


## Change information





Patch:
diff --git a/components/auto-complete/auto-complete-item.tsx b/components/auto-complete/auto-complete-item.tsx
index 63b102b..6d6fb08 100644
--- a/components/auto-complete/auto-complete-item.tsx
+++ b/components/auto-complete/auto-complete-item.tsx
@@ -28,9 +28,10 @@ const AutoCompleteItem: React.FC<React.PropsWithChildren<AutoCompleteItemProps>>
   children,
 }) => {
   const theme = useTheme()
-  const { value, updateValue, size } = useAutoCompleteContext()
+  const { value, updateValue, size, updateVisible } = useAutoCompleteContext()
   const selectHandler = () => {
     updateValue && updateValue(identValue)
+    updateVisible && updateVisible(false)
   }
 
   const isActive = useMemo(() => value === identValue, [identValue, value])
diff --git a/components/auto-complete/auto-complete.tsx b/components/auto-complete/auto-complete.tsx
index cb5b551..0e51a45 100644
--- a/components/auto-complete/auto-complete.tsx
+++ b/components/auto-complete/auto-complete.tsx
@@ -8,6 +8,7 @@ import { AutoCompleteContext, AutoCompleteConfig } from './auto-complete-context
 import { NormalSizes, NormalTypes } from '../utils/prop-types'
 import Loading from '../loading'
 import { pickChild } from '../utils/collections'
+import useCurrentState from '../utils/use-current-state'
 
 export type AutoCompleteOption = {
   label: string
@@ -31,6 +32,7 @@ interface Props {
   dropdownClassName?: string
   dropdownStyle?: object
   disableMatchWidth?: boolean
+  disableFreeSolo?: boolean
   className?: string
 }
 
@@ -41,6 +43,7 @@ const defaultProps = {
   clearable: false,
   size: 'medium' as NormalSizes,
   disableMatchWidth: false,
+  disableFreeSolo: false,
   className: '',
 }
 
@@ -83,11 +86,16 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
   dropdownClassName,
   dropdownStyle,
   disableMatchWidth,
+  disableFreeSolo,
   ...props
 }) => {
   const ref = useRef<HTMLDivElement>(null)
-  const [state, setState] = useState<string>(customInitialValue)
+  const inputRef = useRef<HTMLInputElement>(null)
+  const resetTimer = useRef<number>()
+  const [state, setState, stateRef] = useCurrentState<string>(customInitialValue)
+  const [selectVal, setSelectVal] = useState<string>(customInitialValue)
   const [visible, setVisible] = useState<boolean>(false)
+
   const [, searchChild] = pickChild(children, AutoCompleteSearching)
   const [, emptyChild] = pickChild(children, AutoCompleteEmpty)
   const autoCompleteItems = useMemo(() => {
@@ -110,14 +118,24 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
 
   const updateValue = (val: string) => {
     if (disabled) return
+    setSelectVal(val)
     onSelect && onSelect(val)
     setState(val)
+    inputRef.current && inputRef.current.focus()
   }
   const updateVisible = (next: boolean) => setVisible(next)
   const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    setVisible(true)
     onSearch && onSearch(event.target.value)
     setState(event.target.value)
   }
+  const resetInputValue = () => {
+    if (!disableFreeSolo) return
+    if (!state || state === '') return
+    if (state !== selectVal) {
+      setState(selectVal)
+    }
+  }
 
   useEffect(() => {
     onChange && onChange(state)
@@ -140,9 +158,15 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
   )
 
   const toggleFocusHandler = (next: boolean) => {
+    clearTimeout(resetTimer.current)
     setVisible(next)
     if (next) {
-      onSearch && onSearch(state)
+      onSearch && onSearch(stateRef.current)
+    } else {
+      resetTimer.current = window.setTimeout(() => {
+        resetInputValue()
+        clearTimeout(resetTimer.current)
+      }, 100)
     }
   }
 
@@ -157,6 +181,7 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
     <AutoCompleteContext.Provider value={initialValue}>
       <div ref={ref} className="auto-complete">
         <Input
+          ref={inputRef}
           size={size}
           status={status}
           onChange={onInputChange}
diff --git a/components/breadcrumbs/breadcrumbs-item.tsx b/components/breadcrumbs/breadcrumbs-item.tsx
new file mode 100644
index 0000000..2ab1e95
--- /dev/null
+++ b/components/breadcrumbs/breadcrumbs-item.tsx
@@ -0,0 +1,61 @@
+import Link from '../link'
+import { Props as LinkBasicProps } from '../link/link'
+import React, { useMemo } from 'react'
+import withDefaults from '../utils/with-defaults'
+import { pickChild } from '../utils/collections'
+import BreadcrumbsSeparator from './breadcrumbs-separator'
+
+interface Props {
+  href?: string
+  nextLink?: boolean
+  onClick?: (event: React.MouseEvent) => void
+  className?: string
+}
+
+const defaultProps = {
+  nextLink: false,
+  className: '',
+}
+
+type NativeAttrs = Omit<React.AnchorHTMLAttributes<any>, keyof Props>
+type NativeLinkAttrs = Omit<NativeAttrs, keyof LinkBasicProps>
+export type BreadcrumbsProps = Props & typeof defaultProps & NativeLinkAttrs
+
+const BreadcrumbsItem = React.forwardRef<
+  HTMLAnchorElement,
+  React.PropsWithChildren<BreadcrumbsProps>
+>(
+  (
+    { href, nextLink, onClick, children, className, ...props },
+    ref: React.Ref<HTMLAnchorElement>,
+  ) => {
+    const isLink = useMemo(() => href !== undefined || nextLink, [href, nextLink])
+    const [withoutSepChildren] = pickChild(children, BreadcrumbsSeparator)
+    const clickHandler = (event: React.MouseEvent) => {
+      onClick && onClick(event)
+    }
+
+    if (!isLink) {
+      return (
+        <span className={`breadcrums-item ${className}`} onClick={clickHandler}>
+          {withoutSepChildren}
+        </span>
+      )
+    }
+
+    return (
+      <Link
+        className={`breadcrums-item ${className}`}
+        href={href}
+        onClick={clickHandler}
+        ref={ref}
+        {...props}>
+        {withoutSepChildren}
+      </Link>
+    )
+  },
+)
+
+const MemoBreadcrumbsItem = React.memo(BreadcrumbsItem)
+
+export default withDefaults(MemoBreadcrumbsItem, defaultProps)
diff --git a/components/breadcrumbs/breadcrumbs-separator.tsx b/components/breadcrumbs/breadcrumbs-separator.tsx
new file mode 100644
index 0000000..80e12f5
--- /dev/null
+++ b/components/breadcrumbs/breadcrumbs-separator.tsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import withDefaults from '../utils/with-defaults'
+
+interface Props {
+  className?: string
+}
+
+const defaultProps = {
+  className: '',
+}
+
+type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
+export type BreadcrumbsProps = Props & typeof defaultProps & NativeAttrs
+
+const BreadcrumbsSeparator: React.FC<React.PropsWithChildren<BreadcrumbsProps>> = ({
+  children,
+  className,
+}) => {
+  return (
+    <div className={`separator ${className}`}>
+      {children}
+      <style jsx>{`
+        .separator {
+          display: inline-flex;
+          margin: 0 8px;
+          user-select: none;
+          pointer-events: none;
+          align-items: center;
+        }
+      `}</style>
+    </div>
+  )
+}
+
+const MemoBreadcrumbsSeparator = React.memo(BreadcrumbsSeparator)
+
+export default withDefaults(MemoBreadcrumbsSeparator, defaultProps)
diff --git a/components/breadcrumbs/breadcrumbs.tsx b/components/breadcrumbs/breadcrumbs.tsx
new file mode 100644
index 0000000..7850b4d
--- /dev/null
+++ b/components/breadcrumbs/breadcrumbs.tsx
@@ -0,0 +1,114 @@
+import React, { ReactNode, useMemo } from 'react'
+import useTheme from '../styles/use-theme'
+import BreadcrumbsItem from './breadcrumbs-item'
+import BreadcrumbsSeparator from './breadcrumbs-separator'
+import { addColorAlpha } from '../utils/color'
+import { NormalSizes } from '../utils/prop-types'
+
+interface Props {
+  size: NormalSizes
+  separator?: string | ReactNode
+  className?: string
+}
+
+const defaultProps = {
+  size: 'medium' as NormalSizes,
+  separator: '/',
+  className: '',
+}
+
+type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
+export type BreadcrumbsProps = Props & typeof defaultProps & NativeAttrs
+
+const getSize = (size: NormalSizes) => {
+  const sizes: { [key in NormalSizes]: string } = {
+    mini: '.75rem',
+    small: '.875rem',
+    medium: '1rem',
+    large: '1.125rem',
+  }
+  return sizes[size]
+}
+
+const Breadcrumbs: React.FC<React.PropsWithChildren<BreadcrumbsProps>> = ({
+  size,
+  separator,
+  children,
+  className,
+}) => {
+  const theme = useTheme()
+  const fontSize = useMemo(() => getSize(size), [size])
+  const hoverColor = useMemo(() => {
+    return addColorAlpha(theme.palette.link, 0.85)
+  }, [theme.palette.link])
+
+  const childrenArray = React.Children.toArray(children)
+  const withSeparatorChildren = childrenArray.map((item, index) => {
+    if (!React.isValidElement(item)) return item
+    const last = childrenArray[index - 1]
+    const lastIsSeparator = React.isValidElement(last) && last.type === BreadcrumbsSeparator
+    const currentIsSeparator = item.type === BreadcrumbsSeparator
+    if (!lastIsSeparator && !currentIsSeparator && index > 0) {
+      return (
+        <React.Fragment key={index}>
+          <BreadcrumbsSeparator>{separator}</BreadcrumbsSeparator>
+          {item}
+        </React.Fragment>
+      )
+    }
+    return item
+  })
+
+  return (
+    <nav className={className}>
+      {withSeparatorChildren}
+      <style jsx>{`
+        nav {
+          margin: 0;
+          padding: 0;
+          line-height: inherit;
+          color: ${theme.palette.accents_4};
+          font-size: ${fontSize};
+          box-sizing: border-box;
+          display: flex;
+          align-items: center;
+        }
+
+        nav :global(.link:hover) {
+          color: ${hoverColor};
+        }
+
+        nav > :global(span:last-of-type) {
+          color: ${theme.palette.accents_6};
+        }
+
+        nav > :global(.separator:last-child) {
+          display: none;
+        }
+
+        nav :global(svg) {
+          width: 1em;
+          height: 1em;
+          margin: 0 4px;
+        }
+
+        nav :global(.breadcrums-item) {
+          display: inline-flex;
+          align-items: center;
+        }
+      `}</style>
+    </nav>
+  )
+}
+
+type MemoBreadcrumbsComponent<P = {}> = React.NamedExoticComponent<P> & {
+  Item: typeof BreadcrumbsItem
+  Separator: typeof BreadcrumbsSeparator
+}
+type ComponentProps = Partial<typeof defaultProps> &
+  Omit<Props, keyof typeof defaultProps> &
+  NativeAttrs
+
+Breadcrumbs.defaultProps = defaultProps
+
+export default React.memo(Breadcrumbs) as MemoBreadcrumbsComponent<ComponentProps>
diff --git a/components/breadcrumbs/index.ts b/components/breadcrumbs/index.ts
new file mode 100644
index 0000000..fe5edd8
--- /dev/null
+++ b/components/breadcrumbs/index.ts
@@ -0,0 +1,8 @@
+import Breadcrumbs from './breadcrumbs'
+import BreadcrumbsItem from './breadcrumbs-item'
+import BreadcrumbsSeparator from './breadcrumbs-separator'
+
+Breadcrumbs.Item = BreadcrumbsItem
+Breadcrumbs.Separator = BreadcrumbsSeparator
+
+export default Breadcrumbs
diff --git a/components/css-baseline/css-baseline.tsx b/components/css-baseline/css-baseline.tsx
index 3eeadfb..9f65267 100644
--- a/components/css-baseline/css-baseline.tsx
+++ b/components/css-baseline/css-baseline.tsx
@@ -216,7 +216,7 @@ const CssBaseline: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
 
         pre code {
           color: ${theme.palette.foreground};
-          font-size: 0.75rem;
+          font-size: 0.8125rem;
           line-height: 1.25rem;
           white-space: pre;
         }
diff --git a/components/grid/basic-item.tsx b/components/grid/basic-item.tsx
index 6e0fb97..1233b3e 100644
--- a/components/grid/basic-item.tsx
+++ b/components/grid/basic-item.tsx
@@ -32,19 +32,23 @@ type ItemLayoutValue = {
   grow: number
   width: string
   basis: string
+  display: string
 }
 const getItemLayout = (val: BreakpointsValue): ItemLayoutValue => {
+  const display = val === 0 ? 'none' : 'flex'
   if (typeof val === 'number') {
     const width = (100 / 24) * val
     const ratio = width > 100 ? '100%' : width < 0 ? '0' : `${width}%`
     return {
       grow: 0,
+      display,
       width: ratio,
       basis: ratio,
     }
   }
   return {
     grow: 1,
+    display,
     width: '100%',
     basis: '0',
   }
@@ -77,11 +81,11 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
       xl,
     }
     const classString = Object.keys(aligns).reduce((pre, name) => {
-      if (Boolean(aligns[name]) && aligns[name] !== 0) return `${pre} ${name}`
+      if (aligns[name] !== undefined && aligns[name] !== false) return `${pre} ${name}`
       return pre
     }, '')
     return classString.trim()
-  }, [justify, direction, alignItems, alignContent])
+  }, [justify, direction, alignItems, alignContent, xs, sm, md, lg, xl])
 
   const layout = useMemo<
     {
@@ -125,6 +129,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
           flex-grow: ${layout.xs.grow};
           max-width: ${layout.xs.width};
           flex-basis: ${layout.xs.basis};
+          display: ${layout.xs.display};
         }
 
         @media only screen and (max-width: ${theme.breakpoints.xs.max}) {
@@ -132,6 +137,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
             flex-grow: ${layout.xs.grow};
             max-width: ${layout.xs.width};
             flex-basis: ${layout.xs.basis};
+            display: ${layout.xs.display};
           }
         }
 
@@ -140,6 +146,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
             flex-grow: ${layout.sm.grow};
             max-width: ${layout.sm.width};
             flex-basis: ${layout.sm.basis};
+            display: ${layout.sm.display};
           }
         }
 
@@ -148,6 +155,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
             flex-grow: ${layout.md.grow};
             max-width: ${layout.md.width};
             flex-basis: ${layout.md.basis};
+            display: ${layout.md.display};
           }
         }
 
@@ -156,6 +164,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
             flex-grow: ${layout.lg.grow};
             max-width: ${layout.lg.width};
             flex-basis: ${layout.lg.basis};
+            display: ${layout.lg.display};
           }
         }
 
@@ -164,6 +173,7 @@ const GridBasicItem: React.FC<React.PropsWithChildren<GridBasicItemProps>> = ({
             flex-grow: ${layout.xl.grow};
             max-width: ${layout.xl.width};
             flex-basis: ${layout.xl.basis};
+            display: ${layout.xl.display};
           }
         }
       `}</style>
diff --git a/components/image/image-browser.tsx b/components/image/image-browser.tsx
index 14f5e1a..ce420f9 100644
--- a/components/image/image-browser.tsx
+++ b/components/image/image-browser.tsx
@@ -1,21 +1,26 @@
 import React, { useMemo } from 'react'
 import Link from '../link'
+import { Props as LinkProps } from '../link/link'
 import useTheme from '../styles/use-theme'
 import withDefaults from '../utils/with-defaults'
 import ImageBrowserHttpsIcon from './image-browser-https-icon'
 import { getBrowserColors, BrowserColors } from './styles'
 
+type AnchorProps = Omit<React.AnchorHTMLAttributes<any>, keyof LinkProps>
+
 interface Props {
   title?: string
   url?: string
   showFullLink?: boolean
   invert?: boolean
+  anchorProps?: AnchorProps
   className?: string
 }
 
 const defaultProps = {
   className: '',
   showFullLink: false,
+  anchorProps: {} as AnchorProps,
   invert: false,
 }
 
@@ -42,12 +47,17 @@ const getTitle = (title: string, colors: BrowserColors) => (
   </div>
 )
 
-const getAddressInput = (url: string, showFullLink: boolean, colors: BrowserColors) => (
+const getAddressInput = (
+  url: string,
+  showFullLink: boolean,
+  colors: BrowserColors,
+  anchorProps: AnchorProps,
+) => (
   <div className="address-input">
     <span className="https">
       <ImageBrowserHttpsIcon />
     </span>
-    <Link href={url} title={url} target="_blank">
+    <Link href={url} title={url} target="_blank" {...anchorProps}>
       {showFullLink ? url : getHostFromUrl(url)}
     </Link>
     <style jsx>{`
@@ -94,16 +104,16 @@ const getAddressInput = (url: string, showFullLink: boolean, colors: BrowserColo
 
 const ImageBrowser = React.forwardRef<HTMLDivElement, React.PropsWithChildren<ImageBrowserProps>>(
   (
-    { url, title, children, showFullLink, invert, className, ...props },
+    { url, title, children, showFullLink, invert, anchorProps, className, ...props },
     ref: React.Ref<HTMLDivElement>,
   ) => {
     const theme = useTheme()
     const colors = useMemo(() => getBrowserColors(invert, theme.palette), [invert, theme.palette])
     const input = useMemo(() => {
-      if (url) return getAddressInput(url, showFullLink, colors)
+      if (url) return getAddressInput(url, showFullLink, colors, anchorProps)
       if (title) return getTitle(title, colors)
       return null
-    }, [url, showFullLink, title, colors])
+    }, [url, showFullLink, title, colors, anchorProps])
 
     return (
       <div className={`bowser ${className}`} ref={ref} {...props}>
diff --git a/components/index.ts b/components/index.ts
index 506ae92..517e943 100644
--- a/components/index.ts
+++ b/components/index.ts
@@ -58,3 +58,5 @@ export { default as User } from './user'
 export { default as Page } from './page'
 export { default as Grid } from './grid'
 export { default as ButtonGroup } from './button-group'
+export { default as Breadcrumbs } from './breadcrumbs'
+export { default as Pagination } from './pagination'
diff --git a/components/input/use-input.ts b/components/input/use-input.ts
index c64564e..c80792f 100644
--- a/components/input/use-input.ts
+++ b/components/input/use-input.ts
@@ -1,6 +1,10 @@
 import React, { Dispatch, MutableRefObject, SetStateAction } from 'react'
 import useCurrentState from '../utils/use-current-state'
 
+export type BindingsChangeTarget =
+  | React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
+  | string
+
 const useInput = (
   initialValue: string,
 ): {
@@ -10,7 +14,7 @@ const useInput = (
   reset: () => void
   bindings: {
     value: string
-    onChange: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
+    onChange: (event: BindingsChangeTarget) => void
   }
 } => {
   const [state, setState, currentRef] = useCurrentState<string>(initialValue)
@@ -22,8 +26,12 @@ const useInput = (
     reset: () => setState(initialValue),
     bindings: {
       value: state,
-      onChange: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
-        setState(event.target.value)
+      onChange: (event: BindingsChangeTarget) => {
+        if (typeof event === 'object' && event.target) {
+          setState(event.target.value)
+        } else {
+          setState(event as string)
+        }
       },
     },
   }
diff --git a/components/link/link.tsx b/components/link/link.tsx
index 39cb172..887f377 100644
--- a/components/link/link.tsx
+++ b/components/link/link.tsx
@@ -4,7 +4,7 @@ import useTheme from '../styles/use-theme'
 import useWarning from '../utils/use-warning'
 import LinkIcon from './icon'
 
-interface Props {
+export interface Props {
   href?: string
   color?: boolean
   pure?: boolean
diff --git a/components/pagination/index.ts b/components/pagination/index.ts
new file mode 100644
index 0000000..9a60112
--- /dev/null
+++ b/components/pagination/index.ts
@@ -0,0 +1,8 @@
+import Pagination from './pagination'
+import PaginationPrevious from './pagination-previous'
+import PaginationNext from './pagination-next'
+
+Pagination.Previous = PaginationPrevious
+Pagination.Next = PaginationNext
+
+export default Pagination
diff --git a/components/pagination/pagination-context.ts b/components/pagination/pagination-context.ts
new file mode 100644
index 0000000..8342ec7
--- /dev/null
+++ b/components/pagination/pagination-context.ts
@@ -0,0 +1,18 @@
+import React from 'react'
+import { tuple } from '../utils/prop-types'
+const paginationUpdateTypes = tuple('prev', 'next', 'click')
+
+export type PaginationUpdateType = typeof paginationUpdateTypes[number]
+
+export interface PaginationConfig {
+  isFirst?: boolean
+  isLast?: boolean
+  update?: (type: PaginationUpdateType) => void
+}
+
+const defaultContext = {}
+
+export const PaginationContext = React.createContext<PaginationConfig>(defaultContext)
+
+export const usePaginationContext = (): PaginationConfig =>
+  React.useContext<PaginationConfig>(PaginationContext)
diff --git a/components/pagination/pagination-ellipsis.tsx b/components/pagination/pagination-ellipsis.tsx
new file mode 100644
index 0000000..e4d759d
--- /dev/null
+++ b/components/pagination/pagination-ellipsis.tsx
@@ -0,0 +1,60 @@
+import React, { useState } from 'react'
+import PaginationItem from './pagination-item'
+
+interface Props {
+  isBefore?: boolean
+  onClick?: (e: React.MouseEvent) => void
+}
+
+const PaginationEllipsis: React.FC<Props> = ({ isBefore, onClick }) => {
+  const [showMore, setShowMore] = useState(false)
+
+  return (
+    <PaginationItem
+      onClick={e => onClick && onClick(e)}
+      onMouseEnter={() => setShowMore(true)}
+      onMouseLeave={() => setShowMore(false)}>
+      {showMore ? (
+        <svg
+          className="more"
+          viewBox="0 0 24 24"
+          stroke="currentColor"
+          strokeWidth="1.5"
+          strokeLinecap="round"
+          strokeLinejoin="round"
+          fill="none"
+          shapeRendering="geometricPrecision">
+          <path d="M13 17l5-5-5-5" />
+          <path d="M6 17l5-5-5-5" />
+        </svg>
+      ) : (
+        <svg
+          viewBox="0 0 24 24"
+          strokeWidth="1.5"
+          strokeLinecap="round"
+          strokeLinejoin="round"
+          fill="none"
+          shapeRendering="geometricPrecision">
+          <circle cx="12" cy="12" r="1" fill="currentColor" />
+          <circle cx="19" cy="12" r="1" fill="currentColor" />
+          <circle cx="5" cy="12" r="1" fill="currentColor" />
+        </svg>
+      )}
+
+      <style jsx>{`
+        svg {
+          color: currentColor;
+          stroke: currentColor;
+          width: 1rem;
+          height: 1rem;
+        }
+
+        .more {
+          transform: rotate(${isBefore ? '180deg' : '0deg'});
+        }
+      `}</style>
+    </PaginationItem>
+  )
+}
+
+export default PaginationEllipsis
diff --git a/components/pagination/pagination-item.tsx b/components/pagination/pagination-item.tsx
new file mode 100644
index 0000000..74824d7
--- /dev/null
+++ b/components/pagination/pagination-item.tsx
@@ -0,0 +1,101 @@
+import React, { useMemo } from 'react'
+import useTheme from '../styles/use-theme'
+import { addColorAlpha } from '../utils/color'
+
+interface Props {
+  active?: boolean
+  disabled?: boolean
+  onClick?: (e: React.MouseEvent) => void
+}
+
+type NativeAttrs = Omit<React.ButtonHTMLAttributes<any>, keyof Props>
+export type PaginationItemProps = Props & NativeAttrs
+
+const PaginationItem: React.FC<React.PropsWithChildren<PaginationItemProps>> = ({
+  active,
+  children,
+  disabled,
+  onClick,
+  ...props
+}) => {
+  const theme = useTheme()
+  const [hover, activeHover] = useMemo(
+    () => [addColorAlpha(theme.palette.success, 0.1), addColorAlpha(theme.palette.success, 0.8)],
+    [theme.palette.success],
+  )
+  const clickHandler = (event: React.MouseEvent) => {
+    if (disabled) return
+    onClick && onClick(event)
+  }
+
+  return (
+    <li>
+      <button
+        className={`${active ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
+        onClick={clickHandler}
+        {...props}>
+        {children}
+      </button>
+      <style jsx>{`
+        li {
+          margin-right: 6px;
+          display: inline-block;
+        }
+        button {
+          border: none;
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          box-sizing: border-box;
+          text-transform: capitalize;
+          user-select: none;
+          white-space: nowrap;
+          text-align: center;
+          vertical-align: middle;
+          box-shadow: none;
+          outline: none;
+          height: var(--pagination-size);
+          min-width: var(--pagination-size);
+          font-size: inherit;
+          cursor: pointer;
+          color: ${theme.palette.success};
+          border-radius: ${theme.layout.radius};
+          background-color: ${theme.palette.background};
+          transition: all linear 200ms 0ms;
+        }
+
+        button:hover {
+          background-color: ${hover};
+        }
+
+        .active {
+          font-weight: bold;
+          background-color: ${theme.palette.success};
+          color: ${theme.palette.background};
+          box-shadow: ${theme.expressiveness.shadowSmall};
+        }
+
+        .active:hover {
+          background-color: ${activeHover};
+          box-shadow: ${theme.expressiveness.shadowMedium};
+        }
+
+        .disabled {
+          color: ${theme.palette.accents_4};
+          cursor: not-allowed;
+        }
+
+        .disabled:hover {
+          background-color: ${theme.palette.accents_2};
+        }
+
+        button :global(svg) {
+          width: 1.3em;
+          height: 1.3em;
+        }
+      `}</style>
+    </li>
+  )
+}
+
+export default PaginationItem
diff --git a/components/pagination/pagination-next.tsx b/components/pagination/pagination-next.tsx
new file mode 100644
index 0000000..d01f893
--- /dev/null
+++ b/components/pagination/pagination-next.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+import PaginationItem from './pagination-item'
+import { usePaginationContext } from './pagination-context'
+
+export type PaginationNextProps = React.ButtonHTMLAttributes<any>
+
+const PaginationNext: React.FC<React.PropsWithChildren<PaginationNextProps>> = ({
+  children,
+  ...props
+}) => {
+  const { update, isLast } = usePaginationContext()
+  return (
+    <PaginationItem onClick={() => update && update('next')} disabled={isLast} {...props}>
+      {children}
+    </PaginationItem>
+  )
+}
+
+export default PaginationNext
diff --git a/components/pagination/pagination-pages.tsx b/components/pagination/pagination-pages.tsx
new file mode 100644
index 0000000..a90f58f
--- /dev/null
+++ b/components/pagination/pagination-pages.tsx
@@ -0,0 +1,104 @@
+import React, { Dispatch, SetStateAction, useCallback, useMemo } from 'react'
+import PaginationItem from './pagination-item'
+import PaginationEllipsis from './pagination-ellipsis'
+
+interface Props {
+  limit: number
+  count: number
+  current: number
+  setPage: Dispatch<SetStateAction<number>>
+}
+
+const PaginationPages: React.FC<Props> = ({ limit, count, current, setPage }) => {
+  const showPages = useMemo(() => {
+    const oddLimit = limit % 2 === 0 ? limit - 1 : limit
+    return oddLimit - 2
+  }, [limit])
+  const middleNumber = (showPages + 1) / 2
+
+  const [showBeforeEllipsis, showAfterEllipsis] = useMemo(() => {
+    const showEllipsis = count > limit
+    return [
+      showEllipsis && current > middleNumber + 1,
+      showEllipsis && current < count - middleNumber,
+    ]
+  }, [current, showPages, middleNumber, count, limit])
+  const pagesArray = useMemo(() => [...new Array(showPages)], [showPages])
+
+  const renderItem = useCallback(
+    (value: number, active: number) => (
+      <PaginationItem
+        key={`pagination-item-${value}`}
+        active={value === active}
+        onClick={() => setPage(value)}>
+        {value}
+      </PaginationItem>
+    ),
+    [],
+  )
+  const startPages = pagesArray.map((_, index) => {
+    const value = index + 2
+    return renderItem(value, current)
+  })
+  const middlePages = pagesArray.map((_, index) => {
+    const middleIndexNumber = middleNumber - (index + 1)
+    const value = current - middleIndexNumber
+    return (
+      <PaginationItem
+        key={`pagination-middle-${index}`}
+        active={index + 1 === middleNumber}
+        onClick={() => setPage(value)}>
+        {value}
+      </PaginationItem>
+    )
+  })
+  const endPages = pagesArray.map((_, index) => {
+    const value = count - (showPages - index)
+    return renderItem(value, current)
+  })
+  if (count <= limit) {
+    /* eslint-disable react/jsx-no-useless-fragment */
+    return (
+      <>
+        {[...new Array(count)].map((_, index) => {
+          const value = index + 1
+          return (
+            <PaginationItem
+              key={`pagination-item-${value}`}
+              active={value === current}
+              onClick={() => setPage(value)}>
+              {value}
+            </PaginationItem>
+          )
+        })}
+      </>
+    )
+    /* eslint-enable */
+  }
+  return (
+    <>
+      {renderItem(1, current)}
+      {showBeforeEllipsis && (
+        <PaginationEllipsis
+          key="pagination-ellipsis-before"
+          isBefore
+          onClick={() => setPage(last => (last - 5 >= 1 ? last - 5 : 1))}
+        />
+      )}
+      {showBeforeEllipsis && showAfterEllipsis
+        ? middlePages
+        : showBeforeEllipsis
+        ? endPages
+        : startPages}
+      {showAfterEllipsis && (
+        <PaginationEllipsis
+          key="pagination-ellipsis-after"
+          onClick={() => setPage(last => (last + 5 <= count ? last + 5 : count))}
+        />
+      )}
+      {renderItem(count, current)}
+    </>
+  )
+}
+
+export default PaginationPages
diff --git a/components/pagination/pagination-previous.tsx b/components/pagination/pagination-previous.tsx
new file mode 100644
index 0000000..79b2bf9
--- /dev/null
+++ b/components/pagination/pagination-previous.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+import PaginationItem from './pagination-item'
+import { usePaginationContext } from './pagination-context'
+
+export type PaginationNextProps = React.ButtonHTMLAttributes<any>
+
+const PaginationPrevious: React.FC<React.PropsWithChildren<PaginationNextProps>> = ({
+  children,
+  ...props
+}) => {
+  const { update, isFirst } = usePaginationContext()
+  return (
+    <PaginationItem onClick={() => update && update('prev')} disabled={isFirst} {...props}>
+      {children}
+    </PaginationItem>
+  )
+}
+
+export default PaginationPrevious
diff --git a/components/pagination/pagination.tsx b/components/pagination/pagination.tsx
new file mode 100644
index 0000000..8072785
--- /dev/null
+++ b/components/pagination/pagination.tsx
@@ -0,0 +1,142 @@
+import React, { useEffect, useMemo } from 'react'
+import PaginationPrevious from './pagination-previous'
+import PaginationNext from './pagination-next'
+import PaginationPages from './pagination-pages'
+import { PaginationContext, PaginationConfig, PaginationUpdateType } from './pagination-context'
+import useCurrentState from '../utils/use-current-state'
+import { pickChild } from '../utils/collections'
+import { NormalSizes } from '../utils/prop-types'
+
+interface Props {
+  size?: NormalSizes
+  page?: number
+  initialPage?: number
+  count?: number
+  limit?: number
+  onChange?: (val: number) => void
+}
+
+const defaultProps = {
+  size: 'medium' as NormalSizes,
+  initialPage: 1,
+  count: 1,
+  limit: 7,
+}
+
+type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
+export type PaginationProps = Props & typeof defaultProps & NativeAttrs
+
+type PaginationSize = {
+  font: string
+  width: string
+}
+
+const getPaginationSizes = (size: NormalSizes) => {
+  const sizes: { [key in NormalSizes]: PaginationSize } = {
+    mini: {
+      font: '.75rem',
+      width: '1.25rem',
+    },
+    small: {
+      font: '.75rem',
+      width: '1.65rem',
+    },
+    medium: {
+      font: '.875rem',
+      width: '2rem',
+    },
+    large: {
+      font: '1rem',
+      width: '2.4rem',
+    },
+  }
+  return sizes[size]
+}
+
+const Pagination: React.FC<React.PropsWithChildren<PaginationProps>> = ({
+  page: customPage,
+  initialPage,
+  count,
+  limit,
+  size,
+  children,
+  onChange,
+}) => {
+  const [page, setPage, pageRef] = useCurrentState(initialPage)
+  const [, prevChildren] = pickChild(children, PaginationPrevious)
+  const [, nextChildren] = pickChild(children, PaginationNext)
+
+  const [prevItem, nextItem] = useMemo(() => {
+    const hasChildren = (c: any) => React.Children.count(c) > 0
+    const prevDefault = <PaginationPrevious>prev</PaginationPrevious>
+    const nextDefault = <PaginationNext>next</PaginationNext>
+    return [
+      hasChildren(prevChildren) ? prevChildren : prevDefault,
+      hasChildren(nextChildren) ? nextChildren : nextDefault,
+    ]
+  }, [prevChildren, nextChildren])
+  const { font, width } = useMemo(() => getPaginationSizes(size), [size])
+
+  const update = (type: PaginationUpdateType) => {
+    if (type === 'prev' && pageRef.current > 1) {
+      setPage(last => last - 1)
+    }
+    if (type === 'next' && pageRef.current < count) {
+      setPage(last => last + 1)
+    }
+  }
+  const values = useMemo<PaginationConfig>(
+    () => ({
+      isFirst: page <= 1,
+      isLast: page >= count,
+      update,
+    }),
+    [page],
+  )
+
+  useEffect(() => {
+    onChange && onChange(page)
+  }, [page])
+  useEffect(() => {
+    if (customPage !== undefined) {
+      setPage(customPage)
+    }
+  }, [customPage])
+
+  return (
+    <PaginationContext.Provider value={values}>
+      <nav>
+        {prevItem}
+        <PaginationPages count={count} current={page} limit={limit} setPage={setPage} />
+        {nextItem}
+      </nav>
+      <style jsx>{`
+        nav {
+          margin: 0;
+          padding: 0;
+          font-variant: tabular-nums;
+          font-feature-settings: 'tnum';
+          font-size: ${font};
+          --pagination-size: ${width};
+        }
+
+        nav :global(button:last-of-type) {
+          margin-right: 0;
+        }
+      `}</style>
+    </PaginationContext.Provider>
+  )
+}
+
+type MemoPaginationComponent<P = {}> = React.NamedExoticComponent<P> & {
+  Previous: typeof PaginationPrevious
+  Next: typeof PaginationNext
+}
+
+type ComponentProps = Partial<typeof defaultProps> &
+  Omit<Props, keyof typeof defaultProps> &
+  NativeAttrs
+
+Pagination.defaultProps = defaultProps
+
+export default React.memo(Pagination) as MemoPaginationComponent<ComponentProps>
diff --git a/components/radio/radio-context.ts b/components/radio/radio-context.ts
index b29271b..41fcce4 100644
--- a/components/radio/radio-context.ts
+++ b/components/radio/radio-context.ts
@@ -1,9 +1,9 @@
 import React from 'react'
 
 export interface RadioConfig {
-  updateState?: (value: string) => void
+  updateState?: (value: string | number) => void
   disabledAll: boolean
-  value?: string
+  value?: string | number
   inGroup: boolean
 }
 
diff --git a/components/radio/radio-group.tsx b/components/radio/radio-group.tsx
index 802d3ce..21954f1 100644
--- a/components/radio/radio-group.tsx
+++ b/components/radio/radio-group.tsx
@@ -4,11 +4,11 @@ import { RadioContext } from './radio-context'
 import { NormalSizes } from 'components/utils/prop-types'
 
 interface Props {
-  value?: string
-  initialValue?: string
+  value?: string | number
+  initialValue?: string | number
   disabled?: boolean
   size?: NormalSizes
-  onChange?: (value: string) => void
+  onChange?: (value: string | number) => void
   className?: string
   useRow?: boolean
 }
@@ -44,8 +44,8 @@ const RadioGroup: React.FC<React.PropsWithChildren<RadioGroupProps>> = ({
   useRow,
   ...props
 }) => {
-  const [selfVal, setSelfVal] = useState<string | undefined>(initialValue)
-  const updateState = (nextValue: string) => {
+  const [selfVal, setSelfVal] = useState<string | number | undefined>(initialValue)
+  const updateState = (nextValue: string | number) => {
     setSelfVal(nextValue)
     onChange && onChange(nextValue)
   }
diff --git a/components/radio/radio.tsx b/components/radio/radio.tsx
index b449ad7..15109e4 100644
--- a/components/radio/radio.tsx
+++ b/components/radio/radio.tsx
@@ -20,7 +20,7 @@ export interface RadioEvent {
 
 interface Props {
   checked?: boolean
-  value?: string
+  value?: string | number
   size?: NormalSizes
   className?: string
   disabled?: boolean
@@ -77,7 +77,7 @@ const Radio: React.FC<React.PropsWithChildren<RadioProps>> = ({
     }
     setSelfChecked(!selfChecked)
     if (inGroup) {
-      updateState && updateState(radioValue as string)
+      updateState && updateState(radioValue as string | number)
     }
     onChange && onChange(selfEvent)
   }
diff --git a/lib/data/metadata-en-us.json b/lib/data/metadata-en-us.json
index ded3353..edc4c22 100644
--- a/lib/data/metadata-en-us.json
+++ b/lib/data/metadata-en-us.json
@@ -1,1 +1,1 @@
-[{"name":"guide","children":[{"name":"getting-started","children":[{"name":"introduction","url":"/en-us/guide/introduction","index":5,"group":"getting-started"},{"name":"installation","url":"/en-us/guide/installation","index":10,"group":"getting-started"},{"name":"Server Render","url":"/en-us/guide/server-render","index":15,"group":"getting-started"}]},{"name":"customization","children":[{"name":"Colors","url":"/en-us/guide/colors","index":100,"group":"customization"},{"name":"Themes","url":"/en-us/guide/themes","index":100,"group":"customization"}]}]},{"name":"components","children":[{"name":"General","children":[{"name":"text","url":"/en-us/components/text","index":10,"group":"General"},{"name":"button","url":"/en-us/components/button","index":100,"group":"General"},{"name":"Code","url":"/en-us/components/code","index":100,"group":"General"},{"name":"Icons","url":"/en-us/components/icons","index":100,"group":"General"}]},{"name":"layout","children":[{"name":"Grid","url":"/en-us/components/grid","index":100,"group":"layout"},{"name":"layout","url":"/en-us/components/layout","index":100,"group":"layout"},{"name":"Page","url":"/en-us/components/page","index":100,"group":"layout"},{"name":"Spacer","url":"/en-us/components/spacer","index":100,"group":"layout"}]},{"name":"Surfaces","children":[{"name":"card","url":"/en-us/components/card","index":100,"group":"Surfaces"},{"name":"collapse","url":"/en-us/components/collapse","index":100,"group":"Surfaces"},{"name":"fieldset","url":"/en-us/components/fieldset","index":100,"group":"Surfaces"}]},{"name":"Data Entry","children":[{"name":"Auto-Complete","url":"/en-us/components/auto-complete","index":100,"group":"Data Entry"},{"name":"Button-Group","url":"/en-us/components/button-group","index":100,"group":"Data Entry"},{"name":"checkbox","url":"/en-us/components/checkbox","index":100,"group":"Data Entry"},{"name":"Input","url":"/en-us/components/input","index":100,"group":"Data Entry"},{"name":"radio","url":"/en-us/components/radio","index":100,"group":"Data Entry"},{"name":"select","url":"/en-us/components/select","index":100,"group":"Data Entry"},{"name":"Slider","url":"/en-us/components/slider","index":100,"group":"Data Entry"},{"name":"textarea","url":"/en-us/components/textarea","index":100,"group":"Data Entry"},{"name":"Toggle","url":"/en-us/components/toggle","index":100,"group":"Data Entry"}]},{"name":"Data Display","children":[{"name":"avatar","url":"/en-us/components/avatar","index":100,"group":"Data Display"},{"name":"Badge","url":"/en-us/components/badge","index":100,"group":"Data Display"},{"name":"Capacity","url":"/en-us/components/capacity","index":100,"group":"Data Display"},{"name":"Description","url":"/en-us/components/description","index":100,"group":"Data Display"},{"name":"Display","url":"/en-us/components/display","index":100,"group":"Data Display"},{"name":"Dot","url":"/en-us/components/dot","index":100,"group":"Data Display"},{"name":"File-Tree","url":"/en-us/components/file-tree","index":100,"group":"Data Display"},{"name":"Image","url":"/en-us/components/image","index":100,"group":"Data Display"},{"name":"keyboard","url":"/en-us/components/keyboard","index":100,"group":"Data Display"},{"name":"Popover","url":"/en-us/components/popover","index":100,"group":"Data Display"},{"name":"Table","url":"/en-us/components/table","index":100,"group":"Data Display"},{"name":"Tag","url":"/en-us/components/tag","index":100,"group":"Data Display"},{"name":"Tooltip","url":"/en-us/components/tooltip","index":100,"group":"Data Display"},{"name":"User","url":"/en-us/components/user","index":100,"group":"Data Display"}]},{"name":"Feedback","children":[{"name":"Loading","url":"/en-us/components/loading","index":100,"group":"Feedback"},{"name":"modal","url":"/en-us/components/modal","index":100,"group":"Feedback"},{"name":"note","url":"/en-us/components/note","index":100,"group":"Feedback"},{"name":"Progress","url":"/en-us/components/progress","index":100,"group":"Feedback"},{"name":"Spinner","url":"/en-us/components/spinner","index":100,"group":"Feedback"},{"name":"toast","url":"/en-us/components/toast","index":100,"group":"Feedback"}]},{"name":"Navigation","children":[{"name":"link","url":"/en-us/components/link","index":100,"group":"Navigation"},{"name":"tabs","url":"/en-us/components/tabs","index":100,"group":"Navigation"},{"name":"button-dropdown","url":"/en-us/components/button-dropdown","index":101,"group":"Navigation"}]},{"name":"Others","children":[{"name":"Divider","url":"/en-us/components/divider","index":100,"group":"Others"},{"name":"Snippet","url":"/en-us/components/snippet","index":100,"group":"Others"}]},{"name":"Utils","children":[{"name":"use-body-scroll","url":"/en-us/components/use-body-scroll","index":100,"group":"Utils"},{"name":"use-click-away","url":"/en-us/components/use-click-away","index":100,"group":"Utils"},{"name":"use-clipboard","url":"/en-us/components/use-clipboard","index":100,"group":"Utils"},{"name":"use-current-state","url":"/en-us/components/use-current-state","index":100,"group":"Utils"},{"name":"use-media-query","url":"/en-us/components/use-media-query","index":100,"group":"Utils"}]}]},{"name":"customization","children":[]}]
+[{"name":"guide","children":[{"name":"getting-started","children":[{"name":"introduction","url":"/en-us/guide/introduction","index":5,"group":"getting-started"},{"name":"installation","url":"/en-us/guide/installation","index":10,"group":"getting-started"},{"name":"Server Render","url":"/en-us/guide/server-render","index":15,"group":"getting-started"}]},{"name":"customization","children":[{"name":"Colors","url":"/en-us/guide/colors","index":100,"group":"customization"},{"name":"Themes","url":"/en-us/guide/themes","index":100,"group":"customization"}]}]},{"name":"components","children":[{"name":"General","children":[{"name":"text","url":"/en-us/components/text","index":10,"group":"General"},{"name":"button","url":"/en-us/components/button","index":100,"group":"General"},{"name":"Code","url":"/en-us/components/code","index":100,"group":"General"},{"name":"Icons","url":"/en-us/components/icons","index":100,"group":"General"}]},{"name":"layout","children":[{"name":"Grid","url":"/en-us/components/grid","index":100,"group":"layout"},{"name":"layout","url":"/en-us/components/layout","index":100,"group":"layout"},{"name":"Page","url":"/en-us/components/page","index":100,"group":"layout"},{"name":"Spacer","url":"/en-us/components/spacer","index":100,"group":"layout"}]},{"name":"Surfaces","children":[{"name":"card","url":"/en-us/components/card","index":100,"group":"Surfaces"},{"name":"collapse","url":"/en-us/components/collapse","index":100,"group":"Surfaces"},{"name":"fieldset","url":"/en-us/components/fieldset","index":100,"group":"Surfaces"}]},{"name":"Data Entry","children":[{"name":"Auto-Complete","url":"/en-us/components/auto-complete","index":100,"group":"Data Entry"},{"name":"Button-Group","url":"/en-us/components/button-group","index":100,"group":"Data Entry"},{"name":"checkbox","url":"/en-us/components/checkbox","index":100,"group":"Data Entry"},{"name":"Input","url":"/en-us/components/input","index":100,"group":"Data Entry"},{"name":"radio","url":"/en-us/components/radio","index":100,"group":"Data Entry"},{"name":"select","url":"/en-us/components/select","index":100,"group":"Data Entry"},{"name":"Slider","url":"/en-us/components/slider","index":100,"group":"Data Entry"},{"name":"textarea","url":"/en-us/components/textarea","index":100,"group":"Data Entry"},{"name":"Toggle","url":"/en-us/components/toggle","index":100,"group":"Data Entry"}]},{"name":"Data Display","children":[{"name":"avatar","url":"/en-us/components/avatar","index":100,"group":"Data Display"},{"name":"Badge","url":"/en-us/components/badge","index":100,"group":"Data Display"},{"name":"Capacity","url":"/en-us/components/capacity","index":100,"group":"Data Display"},{"name":"Description","url":"/en-us/components/description","index":100,"group":"Data Display"},{"name":"Display","url":"/en-us/components/display","index":100,"group":"Data Display"},{"name":"Dot","url":"/en-us/components/dot","index":100,"group":"Data Display"},{"name":"File-Tree","url":"/en-us/components/file-tree","index":100,"group":"Data Display"},{"name":"Image","url":"/en-us/components/image","index":100,"group":"Data Display"},{"name":"keyboard","url":"/en-us/components/keyboard","index":100,"group":"Data Display"},{"name":"Popover","url":"/en-us/components/popover","index":100,"group":"Data Display"},{"name":"Table","url":"/en-us/components/table","index":100,"group":"Data Display"},{"name":"Tag","url":"/en-us/components/tag","index":100,"group":"Data Display"},{"name":"Tooltip","url":"/en-us/components/tooltip","index":100,"group":"Data Display"},{"name":"User","url":"/en-us/components/user","index":100,"group":"Data Display"}]},{"name":"Feedback","children":[{"name":"Loading","url":"/en-us/components/loading","index":100,"group":"Feedback"},{"name":"modal","url":"/en-us/components/modal","index":100,"group":"Feedback"},{"name":"note","url":"/en-us/components/note","index":100,"group":"Feedback"},{"name":"Progress","url":"/en-us/components/progress","index":100,"group":"Feedback"},{"name":"Spinner","url":"/en-us/components/spinner","index":100,"group":"Feedback"},{"name":"toast","url":"/en-us/components/toast","index":100,"group":"Feedback"}]},{"name":"Navigation","children":[{"name":"breadcrumbs","url":"/en-us/components/breadcrumbs","index":100,"group":"Navigation"},{"name":"link","url":"/en-us/components/link","index":100,"group":"Navigation"},{"name":"Pagination","url":"/en-us/components/pagination","index":100,"group":"Navigation"},{"name":"tabs","url":"/en-us/components/tabs","index":100,"group":"Navigation"},{"name":"button-dropdown","url":"/en-us/components/button-dropdown","index":101,"group":"Navigation"}]},{"name":"Others","children":[{"name":"Divider","url":"/en-us/components/divider","index":100,"group":"Others"},{"name":"Snippet","url":"/en-us/components/snippet","index":100,"group":"Others"}]},{"name":"Utils","children":[{"name":"use-body-scroll","url":"/en-us/components/use-body-scroll","index":100,"group":"Utils"},{"name":"use-click-away","url":"/en-us/components/use-click-away","index":100,"group":"Utils"},{"name":"use-clipboard","url":"/en-us/components/use-clipboard","index":100,"group":"Utils"},{"name":"use-current-state","url":"/en-us/components/use-current-state","index":100,"group":"Utils"},{"name":"use-media-query","url":"/en-us/components/use-media-query","index":100,"group":"Utils"}]}]},{"name":"customization","children":[]}]
diff --git a/lib/data/metadata-zh-cn.json b/lib/data/metadata-zh-cn.json
index 124e1e1..059d54a 100644
--- a/lib/data/metadata-zh-cn.json
+++ b/lib/data/metadata-zh-cn.json
@@ -1,1 +1,1 @@
-[{"name":"guide","children":[{"name":"快速上手","children":[{"name":"什么是 ZEIT UI","url":"/zh-cn/guide/introduction","index":5,"group":"快速上手"},{"name":"安装","url":"/zh-cn/guide/installation","index":10,"group":"快速上手"},{"name":"服务端渲染","url":"/zh-cn/guide/server-render","index":15,"group":"快速上手"}]},{"name":"定制化","children":[{"name":"色彩","url":"/zh-cn/guide/colors","index":100,"group":"定制化"},{"name":"主题","url":"/zh-cn/guide/themes","index":100,"group":"定制化"}]}],"localeName":"上手指南"},{"name":"components","children":[{"name":"通用","children":[{"name":"文本 Text","url":"/zh-cn/components/text","index":10,"group":"通用"},{"name":"按钮 Button","url":"/zh-cn/components/button","index":100,"group":"通用"},{"name":"代码 Code","url":"/zh-cn/components/code","index":100,"group":"通用"},{"name":"图标 Icons","url":"/zh-cn/components/icons","index":100,"group":"通用"}]},{"name":"布局","children":[{"name":"栅格 Grid","url":"/zh-cn/components/grid","index":100,"group":"布局"},{"name":"布局 Layout","url":"/zh-cn/components/layout","index":100,"group":"布局"},{"name":"页面 Page","url":"/zh-cn/components/page","index":100,"group":"布局"},{"name":"间距 Spacer","url":"/zh-cn/components/spacer","index":100,"group":"布局"}]},{"name":"表面","children":[{"name":"卡片 Card","url":"/zh-cn/components/card","index":100,"group":"表面"},{"name":"折叠框 Collapse","url":"/zh-cn/components/collapse","index":100,"group":"表面"},{"name":"控件组 Fieldset","url":"/zh-cn/components/fieldset","index":100,"group":"表面"}]},{"name":"数据录入","children":[{"name":"按钮组 Button-Group","url":"/zh-cn/components/button-group","index":100,"group":"数据录入"},{"name":"复选框 Checkbox","url":"/zh-cn/components/checkbox","index":100,"group":"数据录入"},{"name":"输入框 Input","url":"/zh-cn/components/input","index":100,"group":"数据录入"},{"name":"单选框 Radio","url":"/zh-cn/components/radio","index":100,"group":"数据录入"},{"name":"选择器 Select","url":"/zh-cn/components/select","index":100,"group":"数据录入"},{"name":"滑动输入 Slider","url":"/zh-cn/components/slider","index":100,"group":"数据录入"},{"name":"文本输入框 Textarea","url":"/zh-cn/components/textarea","index":100,"group":"数据录入"},{"name":"开关 Toggle","url":"/zh-cn/components/toggle","index":100,"group":"数据录入"},{"name":"自动完成 Auto-Complete","url":"/zh-cn/components/auto-complete","index":104,"group":"数据录入"}]},{"name":"数据展示","children":[{"name":"头像 Avatar","url":"/zh-cn/components/avatar","index":100,"group":"数据展示"},{"name":"徽章 Badge","url":"/zh-cn/components/badge","index":100,"group":"数据展示"},{"name":"容量 Capacity","url":"/zh-cn/components/capacity","index":100,"group":"数据展示"},{"name":"描述 Description","url":"/zh-cn/components/description","index":100,"group":"数据展示"},{"name":"陈列框 Display","url":"/zh-cn/components/display","index":100,"group":"数据展示"},{"name":"点 Dot","url":"/zh-cn/components/dot","index":100,"group":"数据展示"},{"name":"文件树 File Tree","url":"/zh-cn/components/file-tree","index":100,"group":"数据展示"},{"name":"图片 Image","url":"/zh-cn/components/image","index":100,"group":"数据展示"},{"name":"键盘 keyboard","url":"/zh-cn/components/keyboard","index":100,"group":"数据展示"},{"name":"气泡卡片 Popover","url":"/zh-cn/components/popover","index":100,"group":"数据展示"},{"name":"表格 Table","url":"/zh-cn/components/table","index":100,"group":"数据展示"},{"name":"标签 Tag","url":"/zh-cn/components/tag","index":100,"group":"数据展示"},{"name":"文字提示 Tooltip","url":"/zh-cn/components/tooltip","index":100,"group":"数据展示"},{"name":"用户 User","url":"/zh-cn/components/user","index":100,"group":"数据展示"}]},{"name":"反馈","children":[{"name":"加载中 Loading","url":"/zh-cn/components/loading","index":100,"group":"反馈"},{"name":"对话框 Modal","url":"/zh-cn/components/modal","index":100,"group":"反馈"},{"name":"提示 Note","url":"/zh-cn/components/note","index":100,"group":"反馈"},{"name":"进度条 Progress","url":"/zh-cn/components/progress","index":100,"group":"反馈"},{"name":"指示器 Spinner","url":"/zh-cn/components/spinner","index":100,"group":"反馈"},{"name":"通知 Toast","url":"/zh-cn/components/toast","index":100,"group":"反馈"}]},{"name":"导航","children":[{"name":"链接 Link","url":"/zh-cn/components/link","index":100,"group":"导航"},{"name":"选项卡 Tabs","url":"/zh-cn/components/tabs","index":100,"group":"导航"},{"name":"下拉按钮 Btn Dropdown","url":"/zh-cn/components/button-dropdown","index":105,"group":"导航"}]},{"name":"其他","children":[{"name":"分割线 Divider","url":"/zh-cn/components/divider","index":100,"group":"其他"},{"name":"片段 Snippet","url":"/zh-cn/components/snippet","index":100,"group":"其他"}]},{"name":"工具包","children":[{"name":"锁定滚动 useBodyScroll","url":"/zh-cn/components/use-body-scroll","index":100,"group":"工具包"},{"name":"点击他处 useClickAway","url":"/zh-cn/components/use-click-away","index":100,"group":"工具包"},{"name":"剪切板 useClipboard","url":"/zh-cn/components/use-clipboard","index":100,"group":"工具包"},{"name":" 当前值 useCurrentState","url":"/zh-cn/components/use-current-state","index":100,"group":"工具包"},{"name":"媒体查询 useMediaQuery","url":"/zh-cn/components/use-media-query","index":100,"group":"工具包"}]}],"localeName":"所有组件"},{"name":"customization","children":[],"localeName":"定制化"}]
+[{"name":"guide","children":[{"name":"快速上手","children":[{"name":"什么是 ZEIT UI","url":"/zh-cn/guide/introduction","index":5,"group":"快速上手"},{"name":"安装","url":"/zh-cn/guide/installation","index":10,"group":"快速上手"},{"name":"服务端渲染","url":"/zh-cn/guide/server-render","index":15,"group":"快速上手"}]},{"name":"定制化","children":[{"name":"色彩","url":"/zh-cn/guide/colors","index":100,"group":"定制化"},{"name":"主题","url":"/zh-cn/guide/themes","index":100,"group":"定制化"}]}],"localeName":"上手指南"},{"name":"components","children":[{"name":"通用","children":[{"name":"文本 Text","url":"/zh-cn/components/text","index":10,"group":"通用"},{"name":"按钮 Button","url":"/zh-cn/components/button","index":100,"group":"通用"},{"name":"代码 Code","url":"/zh-cn/components/code","index":100,"group":"通用"},{"name":"图标 Icons","url":"/zh-cn/components/icons","index":100,"group":"通用"}]},{"name":"布局","children":[{"name":"栅格 Grid","url":"/zh-cn/components/grid","index":100,"group":"布局"},{"name":"布局 Layout","url":"/zh-cn/components/layout","index":100,"group":"布局"},{"name":"页面 Page","url":"/zh-cn/components/page","index":100,"group":"布局"},{"name":"间距 Spacer","url":"/zh-cn/components/spacer","index":100,"group":"布局"}]},{"name":"表面","children":[{"name":"卡片 Card","url":"/zh-cn/components/card","index":100,"group":"表面"},{"name":"折叠框 Collapse","url":"/zh-cn/components/collapse","index":100,"group":"表面"},{"name":"控件组 Fieldset","url":"/zh-cn/components/fieldset","index":100,"group":"表面"}]},{"name":"数据录入","children":[{"name":"按钮组 Button-Group","url":"/zh-cn/components/button-group","index":100,"group":"数据录入"},{"name":"复选框 Checkbox","url":"/zh-cn/components/checkbox","index":100,"group":"数据录入"},{"name":"输入框 Input","url":"/zh-cn/components/input","index":100,"group":"数据录入"},{"name":"单选框 Radio","url":"/zh-cn/components/radio","index":100,"group":"数据录入"},{"name":"选择器 Select","url":"/zh-cn/components/select","index":100,"group":"数据录入"},{"name":"滑动输入 Slider","url":"/zh-cn/components/slider","index":100,"group":"数据录入"},{"name":"文本输入框 Textarea","url":"/zh-cn/components/textarea","index":100,"group":"数据录入"},{"name":"开关 Toggle","url":"/zh-cn/components/toggle","index":100,"group":"数据录入"},{"name":"自动完成 Auto-Complete","url":"/zh-cn/components/auto-complete","index":104,"group":"数据录入"}]},{"name":"数据展示","children":[{"name":"头像 Avatar","url":"/zh-cn/components/avatar","index":100,"group":"数据展示"},{"name":"徽章 Badge","url":"/zh-cn/components/badge","index":100,"group":"数据展示"},{"name":"容量 Capacity","url":"/zh-cn/components/capacity","index":100,"group":"数据展示"},{"name":"描述 Description","url":"/zh-cn/components/description","index":100,"group":"数据展示"},{"name":"陈列框 Display","url":"/zh-cn/components/display","index":100,"group":"数据展示"},{"name":"点 Dot","url":"/zh-cn/components/dot","index":100,"group":"数据展示"},{"name":"文件树 File Tree","url":"/zh-cn/components/file-tree","index":100,"group":"数据展示"},{"name":"图片 Image","url":"/zh-cn/components/image","index":100,"group":"数据展示"},{"name":"键盘 keyboard","url":"/zh-cn/components/keyboard","index":100,"group":"数据展示"},{"name":"气泡卡片 Popover","url":"/zh-cn/components/popover","index":100,"group":"数据展示"},{"name":"表格 Table","url":"/zh-cn/components/table","index":100,"group":"数据展示"},{"name":"标签 Tag","url":"/zh-cn/components/tag","index":100,"group":"数据展示"},{"name":"文字提示 Tooltip","url":"/zh-cn/components/tooltip","index":100,"group":"数据展示"},{"name":"用户 User","url":"/zh-cn/components/user","index":100,"group":"数据展示"}]},{"name":"反馈","children":[{"name":"加载中 Loading","url":"/zh-cn/components/loading","index":100,"group":"反馈"},{"name":"对话框 Modal","url":"/zh-cn/components/modal","index":100,"group":"反馈"},{"name":"提示 Note","url":"/zh-cn/components/note","index":100,"group":"反馈"},{"name":"进度条 Progress","url":"/zh-cn/components/progress","index":100,"group":"反馈"},{"name":"指示器 Spinner","url":"/zh-cn/components/spinner","index":100,"group":"反馈"},{"name":"通知 Toast","url":"/zh-cn/components/toast","index":100,"group":"反馈"}]},{"name":"导航","children":[{"name":"面包屑 Breadcrumbs","url":"/zh-cn/components/breadcrumbs","index":100,"group":"导航"},{"name":"链接 Link","url":"/zh-cn/components/link","index":100,"group":"导航"},{"name":"分页 Pagination","url":"/zh-cn/components/pagination","index":100,"group":"导航"},{"name":"选项卡 Tabs","url":"/zh-cn/components/tabs","index":100,"group":"导航"},{"name":"下拉按钮 Btn Dropdown","url":"/zh-cn/components/button-dropdown","index":105,"group":"导航"}]},{"name":"其他","children":[{"name":"分割线 Divider","url":"/zh-cn/components/divider","index":100,"group":"其他"},{"name":"片段 Snippet","url":"/zh-cn/components/snippet","index":100,"group":"其他"}]},{"name":"工具包","children":[{"name":"锁定滚动 useBodyScroll","url":"/zh-cn/components/use-body-scroll","index":100,"group":"工具包"},{"name":"点击他处 useClickAway","url":"/zh-cn/components/use-click-away","index":100,"group":"工具包"},{"name":"剪切板 useClipboard","url":"/zh-cn/components/use-clipboard","index":100,"group":"工具包"},{"name":" 当前值 useCurrentState","url":"/zh-cn/components/use-current-state","index":100,"group":"工具包"},{"name":"媒体查询 useMediaQuery","url":"/zh-cn/components/use-media-query","index":100,"group":"工具包"}]}],"localeName":"所有组件"},{"name":"customization","children":[],"localeName":"定制化"}]
diff --git a/package.json b/package.json
index 70e87d8..d69d7a7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@zeit-ui/react",
-  "version": "1.6.3",
+  "version": "1.7.0-canary.5",
   "main": "dist/index.js",
   "module": "esm/index.js",
   "types": "dist/index.d.ts",
diff --git a/pages/en-us/components/auto-complete.mdx b/pages/en-us/components/auto-complete.mdx
index 89e3d91..38f6c63 100644
--- a/pages/en-us/components/auto-complete.mdx
+++ b/pages/en-us/components/auto-complete.mdx
@@ -40,10 +40,25 @@ AutoComplete control of input field.
 }
 `} />
 
+<Playground
+  title="Only allow selected"
+  desc="You can only change the value of the input by select."
+  scope={{ AutoComplete }}
+  code={`
+() => {
+  const options = [
+    { label: 'London', value: 'london' },
+    { label: 'Sydney', value: 'sydney' },
+    { label: 'Shanghai', value: 'shanghai' },
+  ]
+  return <AutoComplete disableFreeSolo options={options} initialValue="sydney" />
+}
+`} />
+
 <Playground
   desc="Update the contents of drop-down list based on input."
   title="search"
-  scope={{ AutoComplete, useState }}
+  scope={{ AutoComplete }}
   code={`
 () => {
   const allOptions = [
@@ -51,7 +66,7 @@ AutoComplete control of input field.
     { label: 'Sydney', value: 'sydney' },
     { label: 'Shanghai', value: 'shanghai' },
   ]
-  const [options, setOptions] = useState()
+  const [options, setOptions] = React.useState()
   const searchHandler = (currentValue) => {
     if (!currentValue) return setOptions([])
     const relatedOptions = allOptions.filter(item => item.value.includes(currentValue))
@@ -216,6 +231,31 @@ AutoComplete control of input field.
 }
 `} />
 
+<Playground
+  title="Creatable"
+  desc="Add an entry to be selected for any value."
+  scope={{ AutoComplete }}
+  code={`
+() => {
+  const allOptions = [
+    { label: 'London', value: 'london' },
+    { label: 'Sydney', value: 'sydney' },
+    { label: 'Shanghai', value: 'shanghai' },
+  ]
+  const [options, setOptions] = React.useState()
+  const searchHandler = (currentValue) => {
+    const createOptions = [{
+      value: currentValue, label: 'Add "' + currentValue + '"'
+    }]
+    if (!currentValue) return setOptions([])
+    const relatedOptions = allOptions.filter(item => item.value.includes(currentValue))
+    const optionsWithCreatable = relatedOptions.length !== 0 ? relatedOptions : createOptions
+    setOptions(optionsWithCreatable)
+  }
+  return <AutoComplete options={options} clearable disableFreeSolo placeholder="Enter here" onSearch={searchHandler} />
+}
+`} />
+
 <Attributes edit="/pages/en-us/components/auto-complete.mdx">
 <Attributes.Title>AutoComplete.Props</Attributes.Title>
 
@@ -235,6 +275,7 @@ AutoComplete control of input field.
 | **dropdownClassName** | className of dropdown box | `string` | - | - |
 | **dropdownStyle** | style of dropdown box | `object` | - | - |
 | **disableMatchWidth** | disable Option from follow parent width | `boolean` | - | `false` |
+| **disableFreeSolo** | only values can be changed through Select | `boolean` | - | `false` |
 | ... | native props | `InputHTMLAttributes` | `'id', 'className', ...` | - |
 
 <Attributes.Title alias="AutoComplete.Option">AutoComplete.Item</Attributes.Title>
diff --git a/pages/en-us/components/breadcrumbs.mdx b/pages/en-us/components/breadcrumbs.mdx
new file mode 100644
index 0000000..ded12ee
--- /dev/null
+++ b/pages/en-us/components/breadcrumbs.mdx
@@ -0,0 +1,136 @@
+import { Layout, Playground, Attributes } from 'lib/components'
+import { Breadcrumbs, Spacer } from 'components'
+import NextLink from 'next/link'
+import Home from '@zeit-ui/react-icons/home'
+import Inbox from '@zeit-ui/react-icons/inbox'
+
+export const meta = {
+  title: 'breadcrumbs',
+  group: 'Navigation',
+}
+
+## Breadcrumbs
+
+Show where users are in the application.
+
+<Playground
+  scope={{ Breadcrumbs }}
+  code={`
+<Breadcrumbs>
+  <Breadcrumbs.Item>Home</Breadcrumbs.Item>
+  <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+  <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+</Breadcrumbs>
+`} />
+
+<Playground
+  title="Sizes"
+  scope={{ Breadcrumbs }}
+  code={`
+<>
+  <Breadcrumbs size="mini">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Breadcrumbs size="small">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Breadcrumbs size="medium">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Breadcrumbs size="large">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+</>
+`} />
+
+<Playground
+  title="Separator"
+  desc="Custom separator in characters."
+  scope={{ Breadcrumbs, Spacer }}
+  code={`
+<>
+  <Breadcrumbs separator="-">
+    <Breadcrumbs.Item>Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Spacer y={.5} />
+  <Breadcrumbs separator=">">
+    <Breadcrumbs.Item>Home</Breadcrumbs.Item>
+    <Breadcrumbs.Separator>:</Breadcrumbs.Separator>
+    <Breadcrumbs.Item href="">Components</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Basic</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Button</Breadcrumbs.Item>
+  </Breadcrumbs>
+</>
+`} />
+
+<Playground
+  title="Icons"
+  desc="Show more information with icons."
+  scope={{ Breadcrumbs, Home, Inbox }}
+  code={`
+<Breadcrumbs>
+  <Breadcrumbs.Item><Home /></Breadcrumbs.Item>
+  <Breadcrumbs.Item href=""><Inbox /> Inbox</Breadcrumbs.Item>
+  <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+</Breadcrumbs>
+`} />
+
+<Playground
+  title="With NextJS"
+  desc="Example for use with `next.js`."
+  scope={{ Breadcrumbs, NextLink }}
+  code={`
+<Breadcrumbs>
+  <NextLink href="/">
+    <Breadcrumbs.Item nextLink>Home</Breadcrumbs.Item>
+  </NextLink>
+  <NextLink href="/en-us/components">
+    <Breadcrumbs.Item nextLink>Components</Breadcrumbs.Item>
+  </NextLink>
+  <Breadcrumbs.Item>Breadcrumbs</Breadcrumbs.Item>
+</Breadcrumbs>
+`} />
+
+<Attributes edit="/pages/en-us/components/breadcrumbs.mdx">
+<Attributes.Title>Breadcrumbs.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **separator** | separator string | `string` | - | `/` |
+| **size** | breadcrumbs size | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
+| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Breadcrumbs.Item.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **href** | link address | `string` | - | - |
+| **nextLink** | in `next.js` route | `boolean` | - | `false` |
+| **onClick** | click event | `(event: MouseEvent) => void` | - | - |
+| ... | native props | `AnchorHTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Breadcrumbs.Separator.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>NormalSizes</Attributes.Title>
+
+```ts
+type NormalSizes = 'mini' | 'small' | 'medium' | 'large'
+```
+
+</Attributes>
+
+export default ({ children }) => <Layout meta={meta}>{children}</Layout>
diff --git a/pages/en-us/components/grid.mdx b/pages/en-us/components/grid.mdx
index b4ed13c..242bfc6 100644
--- a/pages/en-us/components/grid.mdx
+++ b/pages/en-us/components/grid.mdx
@@ -79,6 +79,26 @@ and very small size. Of course, it still supports dynamic props and custom break
 }
 `} />
 
+<Playground
+  title="Hide elements"
+  desc="Hide elements when unit size is 0."
+  scope={{ Grid, Card }}
+  code={`
+() => {
+  const MockItem = () => {
+    return <Card shadow style={{ width: '100%', height: '50px' }} />
+  }
+  return (
+    <Grid.Container gap={2} justify="center">
+      <Grid xs={12} sm={0}><MockItem /></Grid>
+      <Grid xs={12} sm={0}><MockItem /></Grid>
+      <Grid xs={24}><MockItem /></Grid>
+      <Grid xs={24}><MockItem /></Grid>
+    </Grid.Container>
+  )
+}
+`} />
+
 <Playground
   title="Auto width"
   desc="Auto fill remaining width."
diff --git a/pages/en-us/components/image.mdx b/pages/en-us/components/image.mdx
index 3de86a8..25122c5 100644
--- a/pages/en-us/components/image.mdx
+++ b/pages/en-us/components/image.mdx
@@ -31,7 +31,7 @@ Display image content.
   desc="Add a browser style wrapper to the image."
   scope={{ Image, Display, Code }}
   code={`
-<Image.Browser url="https://react.zeit-ui.co/en-us/guide/introduction" >
+<Image.Browser url="https://react.zeit-ui.co/en-us/guide/introduction" anchorProps={{ rel: 'nofollow' }}>
   <Image width="540" height="246" src="https://user-images.githubusercontent.com/11304944/76085431-fd036480-5fec-11ea-8412-9e581425344a.png" />
 </Image.Browser>
 `} />
@@ -77,6 +77,7 @@ Display image content.
 | **url** | show url on browser address input | `string` | - | - |
 | **showFullLink** | show full url | `boolean` | - | `false` |
 | **invert** | invert colors | `boolean` | - | `false` |
+| **anchorProps** | props of element `a` | `AnchorHTMLAttributes` | - | `{}` |
 | ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
 
 </Attributes>
diff --git a/pages/en-us/components/pagination.mdx b/pages/en-us/components/pagination.mdx
new file mode 100644
index 0000000..3c7fd85
--- /dev/null
+++ b/pages/en-us/components/pagination.mdx
@@ -0,0 +1,108 @@
+import { Layout, Playground, Attributes } from 'lib/components'
+import { Pagination, Spacer } from 'components'
+import ChevronRight from '@zeit-ui/react-icons/chevronRight'
+import ChevronLeft from '@zeit-ui/react-icons/chevronLeft'
+import ChevronRightCircle from '@zeit-ui/react-icons/chevronRightCircle'
+import ChevronLeftCircle from '@zeit-ui/react-icons/chevronLeftCircle'
+import ChevronRightCircleFill from '@zeit-ui/react-icons/chevronRightCircleFill'
+import ChevronLeftCircleFill from '@zeit-ui/react-icons/chevronLeftCircleFill'
+
+export const meta = {
+  title: 'Pagination',
+  group: 'Navigation',
+}
+
+## Pagination
+
+Navigation and identification between multiple pages.
+
+<Playground
+  scope={{ Pagination }}
+  code={`
+<Pagination count={20} initialPage={3} />
+`} />
+
+<Playground
+  title="Limit"
+  desc="The maximum number of pages that can be displayed"
+  scope={{ Pagination }}
+  code={`
+<>
+  <Pagination count={10} limit={10} />
+  <Pagination count={5} />
+  <Pagination count={10} initialPage={6} limit={5} />
+  <Pagination count={10} initialPage={6} />
+  <Pagination count={30} initialPage={6} limit={10} />
+</>
+`} />
+
+<Playground
+  title="Icon"
+  desc="Customize buttons as icons."
+  scope={{ Spacer, Pagination, ChevronRight, ChevronLeft, ChevronRightCircle, ChevronLeftCircle, ChevronRightCircleFill, ChevronLeftCircleFill }}
+  code={`
+<>
+  <Pagination count={5}>
+   <Pagination.Next><ChevronRight /></Pagination.Next>
+   <Pagination.Previous><ChevronLeft /></Pagination.Previous>
+  </Pagination>
+  <Spacer y={.5} />
+  <Pagination count={5}>
+   <Pagination.Next><ChevronRightCircle /></Pagination.Next>
+   <Pagination.Previous><ChevronLeftCircle /></Pagination.Previous>
+  </Pagination>
+  <Spacer y={.5} />
+  <Pagination count={5}>
+   <Pagination.Next><ChevronRightCircleFill /></Pagination.Next>
+   <Pagination.Previous><ChevronLeftCircleFill /></Pagination.Previous>
+  </Pagination>
+</>
+`} />
+
+<Playground
+  title="Sizes"
+  desc="Pagination of different sizes."
+  scope={{ Pagination }}
+  code={`
+<>
+  <Pagination count={20} size="mini" />
+  <Pagination count={20} size="small" />
+  <Pagination count={20} size="medium" />
+  <Pagination count={20} size="large" />
+</>
+`} />
+
+<Attributes edit="/pages/en-us/components/pagination.mdx">
+<Attributes.Title>Pagination.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **initialPage** | the page selected by default | `number` | - | 1 |
+| **page** | current page | `number` | - | 1 |
+| **count** | the total number of pages | `number` | - | 1 |
+| **limit** | limit of display page | `number` | - | 7 |
+| **size** | pagination size | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
+| **onChange** | change event | `(page: number) => void` | - | - |
+| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Pagination.Previous.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| ... | native props | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Pagination.Next.Props</Attributes.Title>
+
+| Attribute | Description | Type | Accepted values | Default
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| ... | native props | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>NormalSizes</Attributes.Title>
+
+```ts
+type NormalSizes = 'mini' | 'small' | 'medium' | 'large'
+```
+
+</Attributes>
+
+export default ({ children }) => <Layout meta={meta}>{children}</Layout>
diff --git a/pages/zh-cn/components/auto-complete.mdx b/pages/zh-cn/components/auto-complete.mdx
index d83ab30..f3fc33e 100644
--- a/pages/zh-cn/components/auto-complete.mdx
+++ b/pages/zh-cn/components/auto-complete.mdx
@@ -26,6 +26,21 @@ export const meta = {
 }
 `} />
 
+<Playground
+  title="只允许选择输入"
+  desc="只通过 Select 事件更改值。"
+  scope={{ AutoComplete }}
+  code={`
+() => {
+  const options = [
+    { label: 'London', value: 'london' },
+    { label: 'Sydney', value: 'sydney' },
+    { label: 'Shanghai', value: 'shanghai' },
+  ]
+  return <AutoComplete disableFreeSolo options={options} initialValue="sydney" />
+}
+`} />
+
 <Playground
   desc="禁用所有的交互。"
   title="禁用"
@@ -217,6 +232,32 @@ export const meta = {
 }
 `} />
 
+
+<Playground
+  title="可创建的"
+  desc="为任意值添加待选条目。"
+  scope={{ AutoComplete }}
+  code={`
+() => {
+  const allOptions = [
+    { label: 'London', value: 'london' },
+    { label: 'Sydney', value: 'sydney' },
+    { label: 'Shanghai', value: 'shanghai' },
+  ]
+  const [options, setOptions] = React.useState()
+  const searchHandler = (currentValue) => {
+    const createOptions = [{
+      value: currentValue, label: 'Add "' + currentValue + '"'
+    }]
+    if (!currentValue) return setOptions([])
+    const relatedOptions = allOptions.filter(item => item.value.includes(currentValue))
+    const optionsWithCreatable = relatedOptions.length !== 0 ? relatedOptions : createOptions
+    setOptions(optionsWithCreatable)
+  }
+  return <AutoComplete options={options} clearable disableFreeSolo placeholder="Enter here" onSearch={searchHandler} />
+}
+`} />
+
 <Attributes edit="/pages/zh-cn/components/auto-complete.mdx">
 <Attributes.Title>AutoComplete.Props</Attributes.Title>
 
@@ -236,6 +277,7 @@ export const meta = {
 | **dropdownClassName** | 自定义下拉框的类名 | `string` | - | - |
 | **dropdownStyle** | 自定义下拉框的样式 | `object` | - | - |
 | **disableMatchWidth** | 禁止 Option 跟随父元素的宽度 | `boolean` | - | `false` |
+| **disableFreeSolo** | 只允许通过 Select 事件更改值 (禁止 Input 输入随意值) | `boolean` | - | `false` |
 | ... | 原生属性 | `InputHTMLAttributes` | `'id', 'className', ...` | - |
 
 <Attributes.Title alias="AutoComplete.Option">AutoComplete.Item</Attributes.Title>
diff --git a/pages/zh-cn/components/breadcrumbs.mdx b/pages/zh-cn/components/breadcrumbs.mdx
new file mode 100644
index 0000000..2fc0dc1
--- /dev/null
+++ b/pages/zh-cn/components/breadcrumbs.mdx
@@ -0,0 +1,136 @@
+import { Layout, Playground, Attributes } from 'lib/components'
+import { Breadcrumbs, Spacer } from 'components'
+import NextLink from 'next/link'
+import Home from '@zeit-ui/react-icons/home'
+import Inbox from '@zeit-ui/react-icons/inbox'
+
+export const meta = {
+  title: '面包屑 Breadcrumbs',
+  group: '导航',
+}
+
+## Breadcrumbs / 面包屑导航
+
+显示用户在应用中的层级位置。
+
+<Playground
+  scope={{ Breadcrumbs }}
+  code={`
+<Breadcrumbs>
+  <Breadcrumbs.Item>Home</Breadcrumbs.Item>
+  <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+  <Breadcrumbs.Item >Page</Breadcrumbs.Item>
+</Breadcrumbs>
+`} />
+
+<Playground
+  title="大小"
+  scope={{ Breadcrumbs }}
+  code={`
+<>
+  <Breadcrumbs size="mini">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Breadcrumbs size="small">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Breadcrumbs size="medium">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Breadcrumbs size="large">
+    <Breadcrumbs.Item href="">Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item>Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+</>
+`} />
+
+<Playground
+  title="分隔符"
+  desc="定制字符中的分隔符。"
+  scope={{ Breadcrumbs, Spacer }}
+  code={`
+<>
+  <Breadcrumbs separator="-">
+    <Breadcrumbs.Item>Home</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
+    <Breadcrumbs.Item >Page</Breadcrumbs.Item>
+  </Breadcrumbs>
+  <Spacer y={.5} />
+  <Breadcrumbs separator=">">
+    <Breadcrumbs.Item>Home</Breadcrumbs.Item>
+    <Breadcrumbs.Separator>:</Breadcrumbs.Separator>
+    <Breadcrumbs.Item href="">Components</Breadcrumbs.Item>
+    <Breadcrumbs.Item href="">Basic</Breadcrumbs.Item>
+    <Breadcrumbs.Item >Button</Breadcrumbs.Item>
+  </Breadcrumbs>
+</>
+`} />
+
+<Playground
+  title="图标"
+  desc="以图标表达更多信息。"
+  scope={{ Breadcrumbs, Home, Inbox }}
+  code={`
+<Breadcrumbs>
+  <Breadcrumbs.Item><Home /></Breadcrumbs.Item>
+  <Breadcrumbs.Item href=""><Inbox /> Inbox</Breadcrumbs.Item>
+  <Breadcrumbs.Item >Page</Breadcrumbs.Item>
+</Breadcrumbs>
+`} />
+
+<Playground
+  title="结合 NextJS"
+  desc="与 `next.js` 结合使用的示例。"
+  scope={{ Breadcrumbs, NextLink }}
+  code={`
+<Breadcrumbs>
+  <NextLink href="/">
+    <Breadcrumbs.Item nextLink>Home</Breadcrumbs.Item>
+  </NextLink>
+  <NextLink href="/en-us/components">
+    <Breadcrumbs.Item nextLink>Components</Breadcrumbs.Item>
+  </NextLink>
+  <Breadcrumbs.Item>Breadcrumbs</Breadcrumbs.Item>
+</Breadcrumbs>
+`} />
+
+<Attributes edit="/pages/zh-cn/components/breadcrumbs.mdx">
+<Attributes.Title>Breadcrumbs.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **separator** | 分隔符 | `string` | - | `/` |
+| **size** | 面包屑导航的大小 | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
+| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Breadcrumbs.Item.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **href** |  链接地址 | `string` | - | - |
+| **nextLink** | 是否为 `next.js` 路由 | `boolean` | - | `false` |
+| **onClick** | 点击事件 | `(event: MouseEvent) => void` | - | - |
+| ... | 原生属性 | `AnchorHTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Breadcrumbs.Separator.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>NormalSizes</Attributes.Title>
+
+```ts
+type NormalSizes = 'mini' | 'small' | 'medium' | 'large'
+```
+
+</Attributes>
+
+export default ({ children }) => <Layout meta={meta}>{children}</Layout>
diff --git a/pages/zh-cn/components/grid.mdx b/pages/zh-cn/components/grid.mdx
index c0b178b..084cc9e 100644
--- a/pages/zh-cn/components/grid.mdx
+++ b/pages/zh-cn/components/grid.mdx
@@ -79,6 +79,26 @@ export const meta = {
 }
 `} />
 
+<Playground
+  title="隐藏元素"
+  desc="当单位大小为 0 时会自动隐藏当前元素。"
+  scope={{ Grid, Card }}
+  code={`
+() => {
+  const MockItem = () => {
+    return <Card shadow style={{ width: '100%', height: '50px' }} />
+  }
+  return (
+    <Grid.Container gap={2} justify="center">
+      <Grid xs={12} sm={0}><MockItem /></Grid>
+      <Grid xs={12} sm={0}><MockItem /></Grid>
+      <Grid xs={24}><MockItem /></Grid>
+      <Grid xs={24}><MockItem /></Grid>
+    </Grid.Container>
+  )
+}
+`} />
+
 <Playground
   title="自动宽度"
   desc="自动分配剩余宽度。"
diff --git a/pages/zh-cn/components/image.mdx b/pages/zh-cn/components/image.mdx
index e6734bf..5a21ad8 100644
--- a/pages/zh-cn/components/image.mdx
+++ b/pages/zh-cn/components/image.mdx
@@ -76,6 +76,7 @@ export const meta = {
 | **url** | 在浏览器地址栏显示链接 | `string` | - | - |
 | **showFullLink** | 显示完整的链接而非域名 | `boolean` | - | `false` |
 | **invert** | 反转所有颜色 | `boolean` | - | `false` |
+| **anchorProps** |  设置 `a` 的其他属性 | `AnchorHTMLAttributes` | - | `{}` |
 | ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
 
 </Attributes>
diff --git a/pages/zh-cn/components/pagination.mdx b/pages/zh-cn/components/pagination.mdx
new file mode 100644
index 0000000..66721c0
--- /dev/null
+++ b/pages/zh-cn/components/pagination.mdx
@@ -0,0 +1,109 @@
+import { Layout, Playground, Attributes } from 'lib/components'
+import { Pagination, Spacer } from 'components'
+import ChevronRight from '@zeit-ui/react-icons/chevronRight'
+import ChevronLeft from '@zeit-ui/react-icons/chevronLeft'
+import ChevronRightCircle from '@zeit-ui/react-icons/chevronRightCircle'
+import ChevronLeftCircle from '@zeit-ui/react-icons/chevronLeftCircle'
+import ChevronRightCircleFill from '@zeit-ui/react-icons/chevronRightCircleFill'
+import ChevronLeftCircleFill from '@zeit-ui/react-icons/chevronLeftCircleFill'
+
+export const meta = {
+  title: '分页 Pagination',
+  group: '导航',
+}
+
+## Pagination / 分页
+
+多个页面之间的导航与鉴别。
+
+<Playground
+  scope={{ Pagination }}
+  desc="默认的分页会自动收缩与扩展。"
+  code={`
+<Pagination count={20} initialPage={3} />
+`} />
+
+<Playground
+  title="Limit"
+  desc="调整最大可展示页面数。"
+  scope={{ Pagination }}
+  code={`
+<>
+  <Pagination count={10} limit={10} />
+  <Pagination count={5} />
+  <Pagination count={10} initialPage={6} limit={5} />
+  <Pagination count={10} initialPage={6} />
+  <Pagination count={30} initialPage={6} limit={10} />
+</>
+`} />
+
+<Playground
+  title="Icon"
+  desc="用图标定制按钮。"
+  scope={{ Spacer, Pagination, ChevronRight, ChevronLeft, ChevronRightCircle, ChevronLeftCircle, ChevronRightCircleFill, ChevronLeftCircleFill }}
+  code={`
+<>
+  <Pagination count={5}>
+   <Pagination.Next><ChevronRight /></Pagination.Next>
+   <Pagination.Previous><ChevronLeft /></Pagination.Previous>
+  </Pagination>
+  <Spacer y={.5} />
+  <Pagination count={5}>
+   <Pagination.Next><ChevronRightCircle /></Pagination.Next>
+   <Pagination.Previous><ChevronLeftCircle /></Pagination.Previous>
+  </Pagination>
+  <Spacer y={.5} />
+  <Pagination count={5}>
+   <Pagination.Next><ChevronRightCircleFill /></Pagination.Next>
+   <Pagination.Previous><ChevronLeftCircleFill /></Pagination.Previous>
+  </Pagination>
+</>
+`} />
+
+<Playground
+  title="Sizes"
+  desc="不同大小的分页器。"
+  scope={{ Pagination }}
+  code={`
+<>
+  <Pagination count={20} size="mini" />
+  <Pagination count={20} size="small" />
+  <Pagination count={20} size="medium" />
+  <Pagination count={20} size="large" />
+</>
+`} />
+
+<Attributes edit="/pages/zh-cn/components/pagination.mdx">
+<Attributes.Title>Pagination.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| **initialPage** | 初始选中的页面 | `number` | - | 1 |
+| **page** | 当前页码 | `number` | - | 1 |
+| **count** | 页码数量 | `number` | - | 1 |
+| **limit** | 一次可展示页面的最大值 | `number` | - | 7 |
+| **size** | 组件的大小 | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
+| **onChange** | 分页器的事件 | `(page: number) => void` | - | - |
+| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Pagination.Previous.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| ... | 原生属性 | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>Pagination.Next.Props</Attributes.Title>
+
+| 属性 | 描述 | 类型 | 推荐值 | 默认
+| ---------- | ---------- | ---- |  -------------- | ------ |
+| ... | 原生属性 | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
+
+<Attributes.Title>NormalSizes</Attributes.Title>
+
+```ts
+type NormalSizes = 'mini' | 'small' | 'medium' | 'large'
+```
+
+</Attributes>
+
+export default ({ children }) => <Layout meta={meta}>{children}</Layout>


Test patch:
diff --git a/components/auto-complete/__tests__/__snapshots__/index.test.tsx.snap b/components/auto-complete/__tests__/__snapshots__/index.test.tsx.snap
index 1aaf209..eaf1c54 100644
--- a/components/auto-complete/__tests__/__snapshots__/index.test.tsx.snap
+++ b/components/auto-complete/__tests__/__snapshots__/index.test.tsx.snap
@@ -4,6 +4,7 @@ exports[`AutoComplete should render correctly 1`] = `
 <AutoComplete
   className=""
   clearable={false}
+  disableFreeSolo={false}
   disableMatchWidth={false}
   disabled={false}
   initialValue=""
diff --git a/components/auto-complete/__tests__/index.test.tsx b/components/auto-complete/__tests__/index.test.tsx
index b01ce72..911bc00 100644
--- a/components/auto-complete/__tests__/index.test.tsx
+++ b/components/auto-complete/__tests__/index.test.tsx
@@ -2,6 +2,7 @@ import React from 'react'
 import { mount } from 'enzyme'
 import { AutoComplete } from 'components'
 import { nativeEvent } from 'tests/utils'
+import { act } from 'react-dom/test-utils'
 
 describe('AutoComplete', () => {
   it('should render correctly', () => {
@@ -32,11 +33,13 @@ describe('AutoComplete', () => {
     expect((input as HTMLInputElement).value).toEqual('value2')
   })
 
-  it('should render clear icon', () => {
+  it('should render clear icon', async () => {
     const wrapper = mount(<AutoComplete initialValue="value" />)
     expect(wrapper.find('svg').length).toBe(0)
 
-    wrapper.setProps({ clearable: true })
+    await act(async () => {
+      wrapper.setProps({ clearable: true })
+    })
     expect(wrapper.find('svg').length).toBe(1)
 
     wrapper.find('svg').simulate('click', nativeEvent)
@@ -44,11 +47,13 @@ describe('AutoComplete', () => {
     expect((input as HTMLInputElement).value).toEqual('')
   })
 
-  it('should reponse width change', () => {
+  it('should reponse width change', async () => {
     const wrapper = mount(<AutoComplete initialValue="value" width="100px" />)
     expect(wrapper.prop('width')).toEqual('100px')
+    await act(async () => {
+      wrapper.setProps({ width: '200px' })
+    })
 
-    wrapper.setProps({ width: '200px' })
     expect(wrapper.prop('width')).toEqual('200px')
   })
 })
diff --git a/components/auto-complete/__tests__/search.test.tsx b/components/auto-complete/__tests__/search.test.tsx
index 6b55b91..13b325f 100644
--- a/components/auto-complete/__tests__/search.test.tsx
+++ b/components/auto-complete/__tests__/search.test.tsx
@@ -1,7 +1,8 @@
 import React from 'react'
 import { mount, render } from 'enzyme'
 import { AutoComplete } from 'components'
-import { nativeEvent } from 'tests/utils'
+import { nativeEvent, updateWrapper } from 'tests/utils'
+import { act } from 'react-dom/test-utils'
 const mockOptions = [{ label: 'London', value: 'london' }]
 
 describe('AutoComplete Search', () => {
@@ -33,9 +34,11 @@ describe('AutoComplete Search', () => {
     expect(value).not.toEqual('london')
   })
 
-  it('should render searching component', () => {
+  it('should render searching component', async () => {
     let wrapper = mount(<AutoComplete searching={false} options={mockOptions} />)
-    wrapper.setProps({ searching: true })
+    await act(async () => {
+      wrapper.setProps({ searching: true })
+    })
     wrapper.find('input').at(0).simulate('focus')
     let dropdown = wrapper.find('.auto-complete-dropdown')
     expect(dropdown.text()).not.toContain('london')
@@ -136,4 +139,15 @@ describe('AutoComplete Search', () => {
     const wrapper = mount(<AutoComplete options={[]} />)
     expect(() => wrapper.unmount()).not.toThrow()
   })
+
+  it('value should be reset when freeSolo disabled', async () => {
+    const wrapper = mount(<AutoComplete initialValue="value" disableFreeSolo />)
+    const input = wrapper.find('input').at(0)
+    input.simulate('focus')
+    input.simulate('change', { target: { value: 'test' } })
+    expect((input.getDOMNode() as HTMLInputElement).value).toEqual('test')
+    input.simulate('blur')
+    await updateWrapper(wrapper, 200)
+    expect((input.getDOMNode() as HTMLInputElement).value).toEqual('value')
+  })
 })
diff --git a/components/auto-complete/__tests__/use-input.test.tsx b/components/auto-complete/__tests__/use-input.test.tsx
new file mode 100644
index 0000000..21ebd95
--- /dev/null
+++ b/components/auto-complete/__tests__/use-input.test.tsx
@@ -0,0 +1,60 @@
+import React, { useEffect } from 'react'
+import { mount } from 'enzyme'
+import { AutoComplete, useInput } from 'components'
+
+describe('UseInput', () => {
+  it('should follow change with use-input', () => {
+    let log = ''
+    const logSpy = jest.spyOn(console, 'log').mockImplementation(msg => (log = msg))
+    const MockInput: React.FC<{ value?: string }> = ({ value }) => {
+      const { state, setState, bindings } = useInput('')
+      useEffect(() => {
+        if (value) setState(value)
+      }, [value])
+      useEffect(() => {
+        if (state) console.log(state)
+      }, [state])
+      return <AutoComplete {...bindings} />
+    }
+
+    const wrapper = mount(<MockInput />)
+    wrapper.setProps({ value: 'test' })
+    const input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
+
+    expect(input.value).toEqual('test')
+    expect(log).toContain('test')
+
+    log = ''
+    wrapper
+      .find('input')
+      .at(0)
+      .simulate('change', { target: { value: 'test-change' } })
+    expect(log).toContain('test-change')
+    logSpy.mockRestore()
+  })
+
+  it('should follow change with use-input', () => {
+    const MockInput: React.FC<{ value?: string; resetValue?: boolean }> = ({
+      value,
+      resetValue,
+    }) => {
+      const { reset, setState, bindings } = useInput('')
+      useEffect(() => {
+        if (value) setState(value)
+      }, [value])
+      useEffect(() => {
+        if (resetValue) reset()
+      }, [resetValue])
+      return <AutoComplete {...bindings} />
+    }
+
+    const wrapper = mount(<MockInput />)
+    wrapper.setProps({ value: 'test' })
+    let input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
+    expect(input.value).toEqual('test')
+
+    wrapper.setProps({ resetValue: true })
+    input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
+    expect(input.value).toEqual('')
+  })
+})
diff --git a/components/breadcrumbs/__tests__/__snapshots__/breadcrumbs.test.tsx.snap b/components/breadcrumbs/__tests__/__snapshots__/breadcrumbs.test.tsx.snap
new file mode 100644
index 0000000..58a621c
--- /dev/null
+++ b/components/breadcrumbs/__tests__/__snapshots__/breadcrumbs.test.tsx.snap
@@ -0,0 +1,85 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Breadcrumbs should redefined all separators 1`] = `
+"<nav class=\\"\\"><span class=\\"breadcrums-item \\">test-1</span><div class=\\"separator \\">*<style>
+        .separator {
+          display: inline-flex;
+          margin: 0 8px;
+          user-select: none;
+          pointer-events: none;
+          align-items: center;
+        }
+      </style></div><span class=\\"breadcrums-item \\">test-2</span><style>
+        nav {
+          margin: 0;
+          padding: 0;
+          line-height: inherit;
+          color: #888;
+          font-size: 1rem;
+          box-sizing: border-box;
+          display: flex;
+          align-items: center;
+        }
+
+        nav :global(.link:hover) {
+          color: rgba(0, 112, 243, 0.85);
+        }
+
+        nav > :global(span:last-of-type) {
+          color: #444;
+        }
+
+        nav > :global(.separator:last-child) {
+          display: none;
+        }
+
+        nav :global(svg) {
+          width: 1em;
+          height: 1em;
+          margin: 0 4px;
+        }
+
+        nav :global(.breadcrums-item) {
+          display: inline-flex;
+          align-items: center;
+        }
+      </style></nav>"
+`;
+
+exports[`Breadcrumbs should render correctly 1`] = `
+"<nav class=\\"\\"><span class=\\"breadcrums-item \\">test-1</span><style>
+        nav {
+          margin: 0;
+          padding: 0;
+          line-height: inherit;
+          color: #888;
+          font-size: 1rem;
+          box-sizing: border-box;
+          display: flex;
+          align-items: center;
+        }
+
+        nav :global(.link:hover) {
+          color: rgba(0, 112, 243, 0.85);
+        }
+
+        nav > :global(span:last-of-type) {
+          color: #444;
+        }
+
+        nav > :global(.separator:last-child) {
+          display: none;
+        }
+
+        nav :global(svg) {
+          width: 1em;
+          height: 1em;
+          margin: 0 4px;
+        }
+
+        nav :global(.breadcrums-item) {
+          display: inline-flex;
+          align-items: center;
+        }
+      </style></nav>"
+`;
diff --git a/components/breadcrumbs/__tests__/breadcrumbs.test.tsx b/components/breadcrumbs/__tests__/breadcrumbs.test.tsx
new file mode 100644
index 0000000..b0ff8cf
--- /dev/null
+++ b/components/breadcrumbs/__tests__/breadcrumbs.test.tsx
@@ -0,0 +1,75 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { Breadcrumbs } from 'components'
+
+describe('Breadcrumbs', () => {
+  it('should render correctly', () => {
+    const wrapper = mount(
+      <Breadcrumbs>
+        <Breadcrumbs.Item>test-1</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    expect(wrapper.html()).toMatchSnapshot()
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
+
+  it('should redefined all separators', () => {
+    const wrapper = mount(
+      <Breadcrumbs separator="*">
+        <Breadcrumbs.Item>test-1</Breadcrumbs.Item>
+        <Breadcrumbs.Item>test-2</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    expect(wrapper.html()).toMatchSnapshot()
+    expect(wrapper.html()).toContain('*')
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
+
+  it('the specified separator should be redefined', () => {
+    const wrapper = mount(
+      <Breadcrumbs separator="*">
+        <Breadcrumbs.Item>test-1</Breadcrumbs.Item>
+        <Breadcrumbs.Separator>%</Breadcrumbs.Separator>
+        <Breadcrumbs.Item>test-2</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    expect(wrapper.html()).toContain('%')
+  })
+
+  it('should render string when href missing', () => {
+    let wrapper = mount(
+      <Breadcrumbs>
+        <Breadcrumbs.Item>test-1</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    let dom = wrapper.find('.breadcrums-item').at(0).getDOMNode()
+    expect(dom.tagName).toEqual('SPAN')
+
+    wrapper = mount(
+      <Breadcrumbs>
+        <Breadcrumbs.Item href="">test-1</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    dom = wrapper.find('.breadcrums-item').at(0).getDOMNode()
+    expect(dom.tagName).toEqual('A')
+
+    wrapper = mount(
+      <Breadcrumbs>
+        <Breadcrumbs.Item nextLink>test-1</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    dom = wrapper.find('.breadcrums-item').at(0).getDOMNode()
+    expect(dom.tagName).toEqual('A')
+  })
+
+  it('should trigger click event', () => {
+    const handler = jest.fn()
+    const wrapper = mount(
+      <Breadcrumbs>
+        <Breadcrumbs.Item onClick={handler}>test-1</Breadcrumbs.Item>
+      </Breadcrumbs>,
+    )
+    wrapper.find('.breadcrums-item').at(0).simulate('click')
+    expect(handler).toHaveBeenCalled()
+  })
+})
diff --git a/components/css-baseline/__tests__/__snapshots__/baseline.test.tsx.snap b/components/css-baseline/__tests__/__snapshots__/baseline.test.tsx.snap
index 4c5ff67..07fc084 100644
--- a/components/css-baseline/__tests__/__snapshots__/baseline.test.tsx.snap
+++ b/components/css-baseline/__tests__/__snapshots__/baseline.test.tsx.snap
@@ -213,7 +213,7 @@ initialize {
 
         pre code {
           color: #000;
-          font-size: 0.75rem;
+          font-size: 0.8125rem;
           line-height: 1.25rem;
           white-space: pre;
         }
@@ -547,7 +547,7 @@ initialize {
 
         pre code {
           color: #fff;
-          font-size: 0.75rem;
+          font-size: 0.8125rem;
           line-height: 1.25rem;
           white-space: pre;
         }
diff --git a/components/grid/__tests__/__snapshots__/index.test.tsx.snap b/components/grid/__tests__/__snapshots__/index.test.tsx.snap
index 1f8e917..c0253ff 100644
--- a/components/grid/__tests__/__snapshots__/index.test.tsx.snap
+++ b/components/grid/__tests__/__snapshots__/index.test.tsx.snap
@@ -25,6 +25,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
           flex-grow: 0;
           max-width: 4.166666666666667%;
           flex-basis: 4.166666666666667%;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -32,6 +33,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 4.166666666666667%;
             flex-basis: 4.166666666666667%;
+            display: flex;
           }
         }
 
@@ -40,6 +42,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 8.333333333333334%;
             flex-basis: 8.333333333333334%;
+            display: flex;
           }
         }
 
@@ -48,6 +51,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 12.5%;
             flex-basis: 12.5%;
+            display: flex;
           }
         }
 
@@ -56,6 +60,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 16.666666666666668%;
             flex-basis: 16.666666666666668%;
+            display: flex;
           }
         }
 
@@ -64,6 +69,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 20.833333333333336%;
             flex-basis: 20.833333333333336%;
+            display: flex;
           }
         }
       </style></div><style>
@@ -90,6 +96,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
           flex-grow: 0;
           max-width: 4.166666666666667%;
           flex-basis: 4.166666666666667%;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -97,6 +104,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 4.166666666666667%;
             flex-basis: 4.166666666666667%;
+            display: flex;
           }
         }
 
@@ -105,6 +113,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 8.333333333333334%;
             flex-basis: 8.333333333333334%;
+            display: flex;
           }
         }
 
@@ -113,6 +122,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 12.5%;
             flex-basis: 12.5%;
+            display: flex;
           }
         }
 
@@ -121,6 +131,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 16.666666666666668%;
             flex-basis: 16.666666666666668%;
+            display: flex;
           }
         }
 
@@ -129,6 +140,7 @@ exports[`Grid all breakpoint values should be supported 1`] = `
             flex-grow: 0;
             max-width: 20.833333333333336%;
             flex-basis: 20.833333333333336%;
+            display: flex;
           }
         }
       </style></div>"
@@ -159,6 +171,7 @@ exports[`Grid css value should be passed through 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -166,6 +179,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -174,6 +188,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -182,6 +197,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -190,6 +206,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -198,6 +215,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><style>
@@ -224,6 +242,7 @@ exports[`Grid css value should be passed through 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -231,6 +250,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -239,6 +259,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -247,6 +268,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -255,6 +277,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -263,6 +286,7 @@ exports[`Grid css value should be passed through 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>"
@@ -293,6 +317,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -300,6 +325,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -308,6 +334,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -316,6 +343,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -324,6 +352,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -332,6 +361,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><style>
@@ -358,6 +388,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -365,6 +396,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -373,6 +405,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -381,6 +414,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -389,6 +423,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -397,6 +432,7 @@ exports[`Grid decimal spacing should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>"
@@ -427,6 +463,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -434,6 +471,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -442,6 +480,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -450,6 +489,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -458,6 +498,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -466,6 +507,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><div class=\\"item  mock \\"><div class=\\"item  mock \\">test<style>
@@ -492,6 +534,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -499,6 +542,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -507,6 +551,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -515,6 +560,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -523,6 +569,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -531,6 +578,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><div class=\\"item  mock \\"><div class=\\"item  mock \\">test<style>
@@ -557,6 +605,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -564,6 +613,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -572,6 +622,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -580,6 +631,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -588,6 +640,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -596,6 +649,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><div class=\\"item  mock \\"><div class=\\"item  mock \\">test<style>
@@ -622,6 +676,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -629,6 +684,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -637,6 +693,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -645,6 +702,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -653,6 +711,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -661,6 +720,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><style>
@@ -687,6 +747,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -694,6 +755,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -702,6 +764,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -710,6 +773,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -718,6 +782,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -726,6 +791,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>,<style>
@@ -752,6 +818,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -759,6 +826,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -767,6 +835,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -775,6 +844,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -783,6 +853,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -791,6 +862,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>,<style>
@@ -817,6 +889,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -824,6 +897,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -832,6 +906,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -840,6 +915,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -848,6 +924,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -856,6 +933,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>,<style>
@@ -882,6 +960,7 @@ exports[`Grid nested components should be supported 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -889,6 +968,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -897,6 +977,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -905,6 +986,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -913,6 +995,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -921,6 +1004,7 @@ exports[`Grid nested components should be supported 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>"
@@ -951,6 +1035,7 @@ exports[`Grid should render correctly 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -958,6 +1043,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -966,6 +1052,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -974,6 +1061,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -982,6 +1070,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -990,6 +1079,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><div class=\\"item  mock \\">test<style>
@@ -1016,6 +1106,7 @@ exports[`Grid should render correctly 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -1023,6 +1114,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1031,6 +1123,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1039,6 +1132,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1047,6 +1141,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1055,6 +1150,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><style>
@@ -1081,6 +1177,7 @@ exports[`Grid should render correctly 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -1088,6 +1185,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1096,6 +1194,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1104,6 +1203,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1112,6 +1212,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1120,6 +1221,7 @@ exports[`Grid should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>"
@@ -1150,6 +1252,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
           flex-grow: 0;
           max-width: 100%;
           flex-basis: 100%;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -1157,6 +1260,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 0;
             max-width: 100%;
             flex-basis: 100%;
+            display: flex;
           }
         }
 
@@ -1165,6 +1269,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1173,6 +1278,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1181,6 +1287,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1189,6 +1296,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><div class=\\"item xs mock \\">test<style>
@@ -1215,6 +1323,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
           flex-grow: 0;
           max-width: 0;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -1222,6 +1331,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 0;
             max-width: 0;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1230,6 +1340,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1238,6 +1349,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1246,6 +1358,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1254,6 +1367,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><style>
@@ -1280,6 +1394,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -1287,6 +1402,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1295,6 +1411,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1303,6 +1420,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1311,6 +1429,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -1319,6 +1438,7 @@ exports[`Grid should work correctly when size exceeds 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div>"
diff --git a/components/grid/__tests__/index.test.tsx b/components/grid/__tests__/index.test.tsx
index 72250d0..f40b00a 100644
--- a/components/grid/__tests__/index.test.tsx
+++ b/components/grid/__tests__/index.test.tsx
@@ -111,4 +111,10 @@ describe('Grid', () => {
     expect(wrapper.html()).toMatchSnapshot()
     expect(() => wrapper.unmount()).not.toThrow()
   })
+
+  it('Grid should be hidden when value is 0', () => {
+    let wrapper = mount(<Grid.Container xs={0} />)
+    expect(wrapper.find('.item').hasClass('xs')).toBeTruthy()
+    expect(wrapper.find('.item').html()).toContain('display: none')
+  })
 })
diff --git a/components/image/__tests__/browser.test.tsx b/components/image/__tests__/browser.test.tsx
index 7533daa..db6c828 100644
--- a/components/image/__tests__/browser.test.tsx
+++ b/components/image/__tests__/browser.test.tsx
@@ -59,4 +59,15 @@ describe('Image Browser', () => {
     const wrapper = mount(<Image.Browser />)
     expect(() => wrapper.unmount()).not.toThrow()
   })
+
+  it('anchor props should be passed through', () => {
+    const anchorRel = 'noreferrer'
+    const wrapper = mount(
+      <Image.Browser url={link} anchorProps={{ rel: anchorRel }}>
+        <Image src={url} />
+      </Image.Browser>,
+    )
+    const rel = wrapper.find('a').getDOMNode().getAttribute('rel')
+    expect(anchorRel).toEqual(anchorRel)
+  })
 })
diff --git a/components/pagination/__tests__/__snapshots__/pagination.test.tsx.snap b/components/pagination/__tests__/__snapshots__/pagination.test.tsx.snap
new file mode 100644
index 0000000..142bc1f
--- /dev/null
+++ b/components/pagination/__tests__/__snapshots__/pagination.test.tsx.snap
@@ -0,0 +1,189 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Pagination should render correctly 1`] = `
+"<nav><li><button class=\\" disabled\\">prev</button><style>
+        li {
+          margin-right: 6px;
+          display: inline-block;
+        }
+        button {
+          border: none;
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          box-sizing: border-box;
+          text-transform: capitalize;
+          user-select: none;
+          white-space: nowrap;
+          text-align: center;
+          vertical-align: middle;
+          box-shadow: none;
+          outline: none;
+          height: var(--pagination-size);
+          min-width: var(--pagination-size);
+          font-size: inherit;
+          cursor: pointer;
+          color: #0070f3;
+          border-radius: 5px;
+          background-color: #fff;
+          transition: all linear 200ms 0ms;
+        }
+
+        button:hover {
+          background-color: rgba(0, 112, 243, 0.1);
+        }
+
+        .active {
+          font-weight: bold;
+          background-color: #0070f3;
+          color: #fff;
+          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
+        }
+
+        .active:hover {
+          background-color: rgba(0, 112, 243, 0.8);
+          box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+        }
+
+        .disabled {
+          color: #888;
+          cursor: not-allowed;
+        }
+
+        .disabled:hover {
+          background-color: #eaeaea;
+        }
+
+        button :global(svg) {
+          width: 1.3em;
+          height: 1.3em;
+        }
+      </style></li><li><button class=\\"active \\">1</button><style>
+        li {
+          margin-right: 6px;
+          display: inline-block;
+        }
+        button {
+          border: none;
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          box-sizing: border-box;
+          text-transform: capitalize;
+          user-select: none;
+          white-space: nowrap;
+          text-align: center;
+          vertical-align: middle;
+          box-shadow: none;
+          outline: none;
+          height: var(--pagination-size);
+          min-width: var(--pagination-size);
+          font-size: inherit;
+          cursor: pointer;
+          color: #0070f3;
+          border-radius: 5px;
+          background-color: #fff;
+          transition: all linear 200ms 0ms;
+        }
+
+        button:hover {
+          background-color: rgba(0, 112, 243, 0.1);
+        }
+
+        .active {
+          font-weight: bold;
+          background-color: #0070f3;
+          color: #fff;
+          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
+        }
+
+        .active:hover {
+          background-color: rgba(0, 112, 243, 0.8);
+          box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+        }
+
+        .disabled {
+          color: #888;
+          cursor: not-allowed;
+        }
+
+        .disabled:hover {
+          background-color: #eaeaea;
+        }
+
+        button :global(svg) {
+          width: 1.3em;
+          height: 1.3em;
+        }
+      </style></li><li><button class=\\" disabled\\">next</button><style>
+        li {
+          margin-right: 6px;
+          display: inline-block;
+        }
+        button {
+          border: none;
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          box-sizing: border-box;
+          text-transform: capitalize;
+          user-select: none;
+          white-space: nowrap;
+          text-align: center;
+          vertical-align: middle;
+          box-shadow: none;
+          outline: none;
+          height: var(--pagination-size);
+          min-width: var(--pagination-size);
+          font-size: inherit;
+          cursor: pointer;
+          color: #0070f3;
+          border-radius: 5px;
+          background-color: #fff;
+          transition: all linear 200ms 0ms;
+        }
+
+        button:hover {
+          background-color: rgba(0, 112, 243, 0.1);
+        }
+
+        .active {
+          font-weight: bold;
+          background-color: #0070f3;
+          color: #fff;
+          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
+        }
+
+        .active:hover {
+          background-color: rgba(0, 112, 243, 0.8);
+          box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+        }
+
+        .disabled {
+          color: #888;
+          cursor: not-allowed;
+        }
+
+        .disabled:hover {
+          background-color: #eaeaea;
+        }
+
+        button :global(svg) {
+          width: 1.3em;
+          height: 1.3em;
+        }
+      </style></li></nav><style>
+        nav {
+          margin: 0;
+          padding: 0;
+          font-variant: tabular-nums;
+          font-feature-settings: 'tnum';
+          font-size: .875rem;
+          --pagination-size: 2rem;
+        }
+
+        nav :global(button:last-of-type) {
+          margin-right: 0;
+        }
+      </style>"
+`;
diff --git a/components/pagination/__tests__/pagination.test.tsx b/components/pagination/__tests__/pagination.test.tsx
new file mode 100644
index 0000000..8bf9dfb
--- /dev/null
+++ b/components/pagination/__tests__/pagination.test.tsx
@@ -0,0 +1,148 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import { Pagination } from 'components'
+import { act } from 'react-dom/test-utils'
+import { updateWrapper } from 'tests/utils'
+
+describe('Pagination', () => {
+  it('should render correctly', () => {
+    const wrapper = mount(<Pagination />)
+    expect(wrapper.html()).toMatchSnapshot()
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
+
+  it('the specified page should be activated', async () => {
+    const wrapper = mount(<Pagination count={10} initialPage={2} />)
+    expect(wrapper.find('.active').text()).toEqual('2')
+    await act(async () => {
+      wrapper.setProps({ page: 10 })
+    })
+    await updateWrapper(wrapper, 200)
+    expect(wrapper.find('.active').text()).toEqual('10')
+  })
+
+  it('should trigger change event', async () => {
+    let current = 1
+    const handler = jest.fn().mockImplementation(val => (current = val))
+    const wrapper = mount(<Pagination count={10} initialPage={2} onChange={handler} />)
+
+    await act(async () => {
+      wrapper.setProps({ page: 10 })
+    })
+    await updateWrapper(wrapper, 200)
+    expect(handler).toHaveBeenCalled()
+    expect(current).toEqual(10)
+
+    const btns = wrapper.find('button')
+    btns.at(0).simulate('click')
+    await updateWrapper(wrapper, 200)
+    expect(current).toEqual(9)
+
+    btns.at(btns.length - 1).simulate('click')
+    btns.at(btns.length - 1).simulate('click')
+    btns.at(btns.length - 1).simulate('click')
+    btns.at(btns.length - 1).simulate('click')
+    await updateWrapper(wrapper, 200)
+    expect(current).toEqual(10)
+    handler.mockRestore()
+  })
+
+  it('the page should be rendered to follow the specified limit', async () => {
+    const wrapper = mount(<Pagination count={20} limit={20} />)
+    expect(wrapper.find('button').length).toBeGreaterThanOrEqual(20)
+    await act(async () => {
+      wrapper.setProps({ limit: 5 })
+    })
+    await updateWrapper(wrapper, 200)
+    expect(wrapper.find('button').length).toBeLessThanOrEqual(10)
+  })
+
+  it('should be render all pages when limit is greater than the total', async () => {
+    const handler = jest.fn()
+    const wrapper = mount(<Pagination count={15} limit={40} onChange={handler} />)
+    expect(wrapper.find('button').length).toBeGreaterThanOrEqual(15)
+    wrapper.find('button').at(10).simulate('click')
+    await updateWrapper(wrapper, 200)
+
+    expect(handler).toHaveBeenCalled()
+    handler.mockRestore()
+  })
+
+  it('omit pages by limit value', async () => {
+    const wrapper = mount(<Pagination count={20} limit={5} />)
+    const btn4 = wrapper.find('button').at(4)
+    expect(btn4.text()).toEqual('4')
+    btn4.simulate('click')
+    await updateWrapper(wrapper, 200)
+    let btns = wrapper.find('button').map(btn => btn.text())
+    expect(btns.includes('2')).not.toBeTruthy()
+    expect(btns.includes('1')).toBeTruthy()
+    expect(btns.includes('3')).toBeTruthy()
+    expect(btns.includes('4')).toBeTruthy()
+    expect(btns.includes('5')).toBeTruthy()
+    expect(btns.includes('6')).not.toBeTruthy()
+    expect(btns.includes('20')).toBeTruthy()
+
+    const btn5 = wrapper.find('button').at(5)
+    expect(btn5.text()).toEqual('5')
+    btn5.simulate('click')
+    await updateWrapper(wrapper, 200)
+    btns = wrapper.find('button').map(btn => btn.text())
+    expect(btns.includes('1')).toBeTruthy()
+    expect(btns.includes('2')).not.toBeTruthy()
+    expect(btns.includes('3')).not.toBeTruthy()
+    expect(btns.includes('4')).toBeTruthy()
+    expect(btns.includes('5')).toBeTruthy()
+    expect(btns.includes('6')).toBeTruthy()
+    expect(btns.includes('7')).not.toBeTruthy()
+    expect(btns.includes('8')).not.toBeTruthy()
+    expect(btns.includes('20')).toBeTruthy()
+  })
+
+  it('should trigger change event when ellipsis clicked', async () => {
+    let current = 20
+    const handler = jest.fn().mockImplementation(val => (current = val))
+    const wrapper = mount(<Pagination count={20} initialPage={20} onChange={handler} />)
+    const btn = wrapper.find('svg').at(0).parents('button')
+    btn.at(0).simulate('click')
+    await updateWrapper(wrapper, 200)
+    expect(handler).toHaveBeenCalled()
+    expect(current).toEqual(15)
+
+    await act(async () => {
+      wrapper.setProps({ page: 1 })
+    })
+    await updateWrapper(wrapper, 200)
+    const lastBtn = wrapper.find('svg').at(0).parents('button')
+    lastBtn.at(0).simulate('click')
+    await updateWrapper(wrapper, 200)
+    expect(current).toEqual(1 + 5)
+  })
+
+  it('another SVG should be displayed when the mouse is moved in', async () => {
+    const wrapper = mount(<Pagination count={20} initialPage={20} />)
+    const svg = wrapper.find('svg').at(0)
+    const btn = svg.parents('button')
+
+    const html = svg.html()
+    btn.simulate('mouseEnter')
+    await updateWrapper(wrapper)
+    expect(html).not.toEqual(wrapper.find('svg').at(0).html())
+
+    btn.simulate('mouseLeave')
+    await updateWrapper(wrapper)
+    expect(html).toEqual(wrapper.find('svg').at(0).html())
+  })
+
+  it('custom buttons should be display', () => {
+    const wrapper = mount(
+      <Pagination count={20}>
+        <Pagination.Previous>custom-prev</Pagination.Previous>
+        <Pagination.Next>custom-next</Pagination.Next>
+      </Pagination>,
+    )
+    const btns = wrapper.find('button')
+    expect(btns.at(0).text()).toEqual('custom-prev')
+    expect(btns.at(btns.length - 1).text()).toEqual('custom-next')
+  })
+})
diff --git a/components/radio/__tests__/group.test.tsx b/components/radio/__tests__/group.test.tsx
index 9cb15c5..22bd1e1 100644
--- a/components/radio/__tests__/group.test.tsx
+++ b/components/radio/__tests__/group.test.tsx
@@ -61,6 +61,28 @@ describe('Radio Group', () => {
     changeHandler.mockRestore()
   })
 
+  it('the radio value should be support number', () => {
+    let value = ''
+    const changeHandler = jest.fn().mockImplementation(val => (value = val))
+    const wrapper = mount(
+      <Radio.Group onChange={changeHandler}>
+        <Radio value={5}>Option 1</Radio>
+        <Radio value={10}>Option 2</Radio>
+      </Radio.Group>,
+    )
+
+    wrapper
+      .find('input')
+      .at(0)
+      .simulate('change', {
+        ...nativeEvent,
+        target: { checked: true },
+      })
+    expect(changeHandler).toHaveBeenCalled()
+    expect(value).toEqual(5)
+    changeHandler.mockRestore()
+  })
+
   it('should ignore events when disabled', () => {
     const changeHandler = jest.fn()
     const wrapper = mount(
diff --git a/components/select/__tests__/__snapshots__/multiple.test.tsx.snap b/components/select/__tests__/__snapshots__/multiple.test.tsx.snap
index fa8a627..f850dbe 100644
--- a/components/select/__tests__/__snapshots__/multiple.test.tsx.snap
+++ b/components/select/__tests__/__snapshots__/multiple.test.tsx.snap
@@ -32,6 +32,7 @@ exports[`Select Multiple should render correctly 1`] = `
           flex-grow: 1;
           max-width: 100%;
           flex-basis: 0;
+          display: flex;
         }
 
         @media only screen and (max-width: 650px) {
@@ -39,6 +40,7 @@ exports[`Select Multiple should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -47,6 +49,7 @@ exports[`Select Multiple should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -55,6 +58,7 @@ exports[`Select Multiple should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -63,6 +67,7 @@ exports[`Select Multiple should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
 
@@ -71,6 +76,7 @@ exports[`Select Multiple should render correctly 1`] = `
             flex-grow: 1;
             max-width: 100%;
             flex-basis: 0;
+            display: flex;
           }
         }
       </style></div><div class=\\"icon\\"><svg viewBox=\\"0 0 24 24\\" width=\\"1.25em\\" height=\\"1.25em\\" stroke-width=\\"1\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\"><path d=\\"M6 9l6 6 6-6\\"></path><style>