Skip to content

Commit

Permalink
refactor(www): use new clipboard.writeText API if it exists (gatsbyjs…
Browse files Browse the repository at this point in the history
  • Loading branch information
DSchau authored and wardpeet committed Jul 23, 2019
1 parent 045a70e commit ac75767
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 62 deletions.
16 changes: 16 additions & 0 deletions www/src/components/__tests__/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react"
import { render } from "react-testing-library"

import Copy from "../copy"

test(`it renders Copy by default`, () => {
const { queryByText } = render(<Copy content="1234" />)

expect(queryByText(`Copy`)).toBeInTheDocument()
})

test(`it renders screen-reader text`, () => {
const { container } = render(<Copy content="1234" />)

expect(container.querySelector(`[aria-roledescription]`)).toBeInTheDocument()
})
27 changes: 2 additions & 25 deletions www/src/components/code-block/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import React from "react"
import CodeBlock from ".."
import { fireEvent, render } from "react-testing-library"
import { render } from "react-testing-library"

beforeEach(() => {
document.createRange = jest.fn()
document.execCommand = jest.fn()
window.getSelection = jest.fn().mockImplementation(() => {
return {
addRange: jest.fn(),
removeAllRanges: jest.fn(),
}
})
})
import CodeBlock from ".."

describe(`basic functionality`, () => {
describe(`copy`, () => {
Expand All @@ -22,19 +12,6 @@ describe(`basic functionality`, () => {

expect(queryByText(`copy`)).toBeDefined()
})

it(`copies text to clipboard`, () => {
const text = `alert('hello world')`
const { queryByText } = render(
<CodeBlock language="jsx">{text}</CodeBlock>
)

const copyButton = queryByText(`Copy`)

fireEvent.click(copyButton)

expect(document.execCommand).toHaveBeenCalledWith(`copy`)
})
})

describe(`highlighting`, () => {
Expand Down
21 changes: 2 additions & 19 deletions www/src/components/copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,7 @@ import {
shadows,
transition,
} from "../utils/presets"

const copyToClipboard = content => {
const textarea = document.createElement(`textarea`)
textarea.value = content
textarea.setAttribute(`readonly`, true)
textarea.setAttribute(`contenteditable`, true)
textarea.style.position = `absolute`
textarea.style.left = `-9999px`
document.body.appendChild(textarea)
textarea.select()
const range = document.createRange()
const sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
textarea.setSelectionRange(0, textarea.value.length)
document.execCommand(`copy`)
document.body.removeChild(textarea)
}
import copyToClipboard from "../utils/copy-to-clipboard"

const delay = duration => new Promise(resolve => setTimeout(resolve, duration))

Expand Down Expand Up @@ -67,7 +50,7 @@ function Copy({ className, content, duration, fileName, trim = false }) {
},
}}
onClick={async () => {
copyToClipboard(trim ? content.trim() : content)
await copyToClipboard(trim ? content.trim() : content)

setCopied(true)

Expand Down
32 changes: 32 additions & 0 deletions www/src/utils/__tests__/copy-to-clipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import copyToClipboard from "../copy-to-clipboard"

beforeEach(() => {
window.navigator.clipboard = {
writeText: jest.fn(),
}

document.execCommand = jest.fn()
document.createRange = jest.fn()
window.getSelection = jest.fn(() => {
return {
removeAllRanges: jest.fn(),
addRange: jest.fn(),
}
})
})

test(`it uses writeText API, by default`, async () => {
const str = `waddup`
await copyToClipboard(str)

expect(window.navigator.clipboard.writeText).toHaveBeenCalledWith(str)
})

test(`it falls back to execCommand, if writeText is not defined`, async () => {
window.navigator.clipboard = {}

await copyToClipboard(`sup`)

expect(document.execCommand).toHaveBeenCalledWith(`copy`)
expect(document.execCommand).toHaveBeenCalledTimes(1)
})
44 changes: 26 additions & 18 deletions www/src/utils/copy-to-clipboard.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
// https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f
const copyToClipboard = str => {
const el = document.createElement(`textarea`) // Create a <textarea> element
el.value = str // Set its value to the string that you want copied
el.setAttribute(`readonly`, ``) // Make it readonly to be tamper-proof
el.style.position = `absolute`
el.style.left = `-9999px` // Move outside the screen to make it invisible
document.body.appendChild(el) // Append the <textarea> element to the HTML document
const selected =
document.getSelection().rangeCount > 0 // Check if there is any content selected previously
? document.getSelection().getRangeAt(0) // Store selection if found
: false // Mark as false to know no selection existed before
el.select() // Select the <textarea> content
document.execCommand(`copy`) // Copy - only works as a result of a user action (e.g. click events)
document.body.removeChild(el) // Remove the <textarea> element
if (selected) {
// If a selection existed before copying
document.getSelection().removeAllRanges() // Unselect everything on the HTML document
document.getSelection().addRange(selected) // Restore the original selection
const clipboard = window.navigator.clipboard
/*
* fallback to older browsers (including Safari)
* if clipboard API not supported
*/
if (!clipboard || typeof clipboard.writeText !== `function`) {
const textarea = document.createElement(`textarea`)
textarea.value = str
textarea.setAttribute(`readonly`, true)
textarea.setAttribute(`contenteditable`, true)
textarea.style.position = `absolute`
textarea.style.left = `-9999px`
document.body.appendChild(textarea)
textarea.select()
const range = document.createRange()
const sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
textarea.setSelectionRange(0, textarea.value.length)
document.execCommand(`copy`)
document.body.removeChild(textarea)

return Promise.resolve(true)
}

return clipboard.writeText(str)
}

export default copyToClipboard

0 comments on commit ac75767

Please sign in to comment.