Repository: zeit-ui/react
Pull request #287 (2020-06-23 23:52:37)

feat(snippet): add custom symbol and toast
## PR Checklist

- [x] Fix linting errors
- [x] Label has been added


## Change information




codecov[bot]: # [Codecov](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=h1) Report
> Merging [#287](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=desc) into [master](https://codecov.io/gh/zeit-ui/react/commit/965654f91afca19d4741d5c73d3d8c86116813ee&el=desc) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/zeit-ui/react/pull/287/graphs/tree.svg?width=650&height=150&src=pr&token=209SV0GGAX)](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master     #287   +/-   ##
=======================================
  Coverage   99.95%   99.95%           
=======================================
  Files         186      186           
  Lines        2366     2366           
  Branches      541      563   +22     
=======================================
  Hits         2365     2365           
  Misses          1        1           
```


| [Impacted Files](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [components/snippet/snippet.tsx](https://codecov.io/gh/zeit-ui/react/pull/287/diff?src=pr&el=tree#diff-Y29tcG9uZW50cy9zbmlwcGV0L3NuaXBwZXQudHN4) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=footer). Last update [965654f...728cf0e](https://codecov.io/gh/zeit-ui/react/pull/287?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

unix: For everyone: This feature does enhance the components, I think it's a good change. If anyone else wants to contribute any features, open an issue to describe it please.
I can't make sure everyone's work can be merged, so it's necessary to communicate in advance, thank you all for efforts.

Patch:
diff --git a/components/snippet/snippet.tsx b/components/snippet/snippet.tsx
index b7dda7b..73599e2 100644
--- a/components/snippet/snippet.tsx
+++ b/components/snippet/snippet.tsx
@@ -1,7 +1,7 @@
 import React, { useMemo, useRef } from 'react'
 import useTheme from '../styles/use-theme'
 import withDefaults from '../utils/with-defaults'
-import { SnippetTypes, CopyTypes } from '../utils/prop-types'
+import { SnippetTypes, CopyTypes, NormalTypes } from '../utils/prop-types'
 import { getStyles } from './styles'
 import SnippetIcon from './snippet-icon'
 import useClipboard from '../utils/use-clipboard'
@@ -9,6 +9,9 @@ import useToasts from '../use-toasts'
 
 interface Props {
   text?: string | string[]
+  symbol?: string
+  toastText?: string
+  toastType?: NormalTypes
   filled?: boolean
   width?: string
   copy?: CopyTypes
@@ -18,6 +21,9 @@ interface Props {
 
 const defaultProps = {
   filled: false,
+  symbol: '$',
+  toastText: 'Copied to clipboard!',
+  toastType: 'success' as NormalTypes,
   width: 'initial',
   copy: 'default' as CopyTypes,
   type: 'default' as SnippetTypes,
@@ -38,6 +44,9 @@ const Snippet: React.FC<React.PropsWithChildren<SnippetProps>> = ({
   type,
   filled,
   children,
+  symbol,
+  toastText,
+  toastType,
   text,
   width,
   copy: copyType,
@@ -63,7 +72,7 @@ const Snippet: React.FC<React.PropsWithChildren<SnippetProps>> = ({
     if (!childText || !showCopyIcon) return
     copy(childText)
     if (copyType === 'slient') return
-    setToast({ text: 'Copied to clipboard!', type: 'success' })
+    setToast({ text: toastText, type: toastType })
   }
 
   return (
@@ -101,7 +110,7 @@ const Snippet: React.FC<React.PropsWithChildren<SnippetProps>> = ({
         }
 
         pre::before {
-          content: '$ ';
+          content: '${symbol} ';
           user-select: none;
         }
 
diff --git a/pages/en-us/components/snippet.mdx b/pages/en-us/components/snippet.mdx
index 4109cc1..137b860 100644
--- a/pages/en-us/components/snippet.mdx
+++ b/pages/en-us/components/snippet.mdx
@@ -60,6 +60,20 @@ Display a snippet of copyable code for the command line.
 <Snippet copy="prevent" text="yarn add @zeit-ui/react" width="300px" />
 `} />
 
+<Playground
+  title="custom symbol"
+  scope={{ Snippet }}
+  code={`
+<Snippet symbol=">" text="yarn add @zeit-ui/react" width="300px" />
+`} />
+
+<Playground
+  title="custom toast"
+  scope={{ Snippet }}
+  code={`
+<Snippet toastText="Code copied!" toastType="secondary" text="yarn add @zeit-ui/react" width="300px" />
+`} />
+
 <Playground
   title="filled"
   scope={{ Snippet, Spacer }}
@@ -85,6 +99,9 @@ Display a snippet of copyable code for the command line.
 | **filled** | filled style | `boolean` | - | `false` |
 | **width** | set CSS string | `string` | - | `initial` |
 | **copy** | function of copy button | `CopyTypes` | `'default', 'slient', 'prevent'` | `default` |
+| **symbol** | symbol snippet | `string` | - | `$` |
+| **toastText** | toast text | `string` | - | `Copied to clipboard!` |
+| **toastType** | toast type | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `success` |
 | ... | native props | `HTMLAttributes` | `'id', 'name', 'className', ...` | - |
 
 <Attributes.Title>SnippetTypes</Attributes.Title>


Test patch:
diff --git a/components/snippet/__tests__/__snapshots__/index.test.tsx.snap b/components/snippet/__tests__/__snapshots__/index.test.tsx.snap
index 5a7ff70..fdd674a 100644
--- a/components/snippet/__tests__/__snapshots__/index.test.tsx.snap
+++ b/components/snippet/__tests__/__snapshots__/index.test.tsx.snap
@@ -58,6 +58,64 @@ exports[`Snippet should render correctly 1`] = `
       </style></div>"
 `;
 
+exports[`Snippet should work with custom symbol 1`] = `
+"<div class=\\"snippet \\"><pre>yarn add @zeit-ui/react</pre><div class=\\"copy\\"><svg viewBox=\\"0 0 24 24\\" width=\\"22\\" height=\\"22\\" stroke=\\"currentColor\\" stroke-width=\\"1.5\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\" style=\\"color: currentcolor;\\"><path d=\\"M8 17.929H6c-1.105 0-2-.912-2-2.036V5.036C4 3.91 4.895 3 6 3h8c1.105 0 2 .911 2 2.036v1.866m-6 .17h8c1.105 0 2 .91 2 2.035v10.857C20 21.09 19.105 22 18 22h-8c-1.105 0-2-.911-2-2.036V9.107c0-1.124.895-2.036 2-2.036z\\"></path></svg></div><style>
+        .snippet {
+          position: relative;
+          width: initial;
+          max-width: 100%;
+          padding: 8pt;
+          padding-right: calc(2 * 16pt);
+          color: #000;
+          background-color: #fff;
+          border: 1px solid #eaeaea;
+          border-radius: 5px;
+        }
+
+        pre {
+          margin: 0;
+          padding: 0;
+          border: none;
+          background-color: transparent;
+          color: #000;
+          font-size: 0.8125rem;
+        }
+
+        pre::before {
+          content: '> ';
+          user-select: none;
+        }
+
+        pre :global(*) {
+          margin: 0;
+          padding: 0;
+          font-size: inherit;
+          color: inherit;
+        }
+
+        .copy {
+          position: absolute;
+          right: 0;
+          top: -2px;
+          transform: translateY(50%);
+          background-color: #fff;
+          display: inline-flex;
+          justify-content: center;
+          align-items: center;
+          width: calc(2 * 16pt);
+          color: inherit;
+          transition: opacity 0.2s ease 0s;
+          border-radius: 5px;
+          cursor: pointer;
+          user-select: none;
+        }
+
+        .copy:hover {
+          opacity: 0.7;
+        }
+      </style></div>"
+`;
+
 exports[`Snippet should work with different styles 1`] = `
 "<div><div class=\\"snippet \\"><pre>yarn add @zeit-ui/react</pre><div class=\\"copy\\"><svg viewBox=\\"0 0 24 24\\" width=\\"22\\" height=\\"22\\" stroke=\\"currentColor\\" stroke-width=\\"1.5\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" fill=\\"none\\" shape-rendering=\\"geometricPrecision\\" style=\\"color: currentcolor;\\"><path d=\\"M8 17.929H6c-1.105 0-2-.912-2-2.036V5.036C4 3.91 4.895 3 6 3h8c1.105 0 2 .911 2 2.036v1.866m-6 .17h8c1.105 0 2 .91 2 2.035v10.857C20 21.09 19.105 22 18 22h-8c-1.105 0-2-.911-2-2.036V9.107c0-1.124.895-2.036 2-2.036z\\"></path></svg></div><style>
         .snippet {
diff --git a/components/snippet/__tests__/index.test.tsx b/components/snippet/__tests__/index.test.tsx
index 7751caa..d3e3066 100644
--- a/components/snippet/__tests__/index.test.tsx
+++ b/components/snippet/__tests__/index.test.tsx
@@ -70,6 +70,20 @@ describe('Snippet', () => {
     expect(wrapper.find('.copy').length).toBe(0)
   })
 
+  it('should work with custom symbol', () => {
+    const wrapper = mount(<Snippet text={command} symbol={'>'} />)
+    expect(wrapper.html()).toMatchSnapshot()
+    expect(() => wrapper.unmount()).not.toThrow()
+  })
+
+  it('should work with custom toast', () => {
+    document.execCommand = jest.fn()
+    const wrapper = mount(<Snippet text={command} toastText="Code copied!" toastType="secondary" />)
+    wrapper.find('.copy').simulate('click')
+    expect(document.execCommand).toHaveBeenCalled()
+    ;(document.execCommand as jest.Mock).mockRestore()
+  })
+
   afterAll(() => {
     ;(window.getSelection as jest.Mock).mockRestore()
     ;(document.createRange as jest.Mock).mockRestore()