Skip to content

Commit

Permalink
Merge pull request #1006 from hashicorp/rn.code-block-modern-clipboar…
Browse files Browse the repository at this point in the history
…d-api

Use the modern DOM clipboard API
  • Loading branch information
RubenSandwich committed Dec 19, 2023
2 parents 038cd7f + 0977c32 commit ca647a0
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 69 deletions.
5 changes: 5 additions & 0 deletions .changeset/seven-clocks-jog.md
@@ -0,0 +1,5 @@
---
'@hashicorp/react-code-block': minor
---

Update to modern clipboard API
39 changes: 23 additions & 16 deletions packages/code-block/index.test.js
Expand Up @@ -12,16 +12,21 @@ import {
} from '@testing-library/react'
import CodeBlock from './'
import { heapAttributes } from './analytics'
// Mock copy-to-clipboard
import copyToClipboard from './partials/clipboard-button/copy-to-clipboard'
jest.mock('./partials/clipboard-button/copy-to-clipboard', () =>
jest.fn().mockImplementation(() => true)
)
// We want to make sure copied code is passed through processSnippet,
// we import it so that we don't have to manually recreate its output
import processSnippet from './utils/process-snippet'

afterEach(cleanup)
// mock the clipboard API
Object.assign(navigator, {
clipboard: {
writeText: jest.fn(),
},
})

afterEach(() => {
cleanup()
jest.clearAllMocks()
})

it('should render a root element with a `g-code-block` class', () => {
const { container } = render(<CodeBlock code="some-example-code" />)
Expand Down Expand Up @@ -108,9 +113,10 @@ it('should use the `Copy` button to copy code to the clipboard', async () => {
fireEvent.click(buttonElem)
// Expect copyToClipboard to have been called with our code snippet
// (note: this function is mocked at the top of this test file)
await waitFor(() => expect(copyToClipboard).toHaveBeenCalledTimes(1))
expect(copyToClipboard).toHaveBeenCalledWith(codeString)
copyToClipboard.mockClear()
await waitFor(() =>
expect(navigator.clipboard.writeText).toHaveBeenCalledTimes(1)
)
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(codeString)
})

it('should track a "Copy" event when the "Copy" button is clicked', async () => {
Expand All @@ -136,7 +142,6 @@ it('should track a "Copy" event when the "Copy" button is clicked', async () =>
})
// Cleanup
window.analytics = forMockRestore
copyToClipboard.mockClear()
})

it('should call "onCopyCallback" when the "Copy" button is clicked', async () => {
Expand All @@ -156,9 +161,10 @@ it('should call "onCopyCallback" when the "Copy" button is clicked', async () =>
expect(buttonElem).toBeInTheDocument()
fireEvent.click(buttonElem)
// Expect onCopyCallback to have been called
await waitFor(() => expect(onCopyCallback).toHaveBeenCalledTimes(1))
await waitFor(() =>
expect(navigator.clipboard.writeText).toHaveBeenCalledTimes(1)
)
expect(onCopyCallback).toBeCalledWith(true)
copyToClipboard.mockClear()
})

it('should track a "Click" event when the root element is clicked', async () => {
Expand Down Expand Up @@ -192,11 +198,12 @@ it('should use process-snippet to strip the leading $ from shell snippets', asyn
const buttonElem = screen.getByText('Copy')
expect(buttonElem).toBeInTheDocument()
fireEvent.click(buttonElem)
// Expect copyToClipboard to have been called with our code snippet
// Expect navigator.clipboard.writeText to have been called with our code snippet
// (note: this function is mocked at the top of this test file)
// We also expect the code to have been modified by processSnippet
const expectedCode = processSnippet(codeString)
await waitFor(() => expect(copyToClipboard).toHaveBeenCalledTimes(1))
expect(copyToClipboard).toHaveBeenCalledWith(expectedCode)
copyToClipboard.mockClear()
await waitFor(() =>
expect(navigator.clipboard.writeText).toHaveBeenCalledTimes(1)
)
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedCode)
})
44 changes: 0 additions & 44 deletions packages/code-block/partials/clipboard-button/copy-to-clipboard.js

This file was deleted.

11 changes: 8 additions & 3 deletions packages/code-block/partials/clipboard-button/index.tsx
Expand Up @@ -8,7 +8,6 @@ import classnames from 'classnames'
import { IconCheckSquare16 } from '@hashicorp/flight-icons/svg-react/check-square-16'
import { IconDuplicate16 } from '@hashicorp/flight-icons/svg-react/duplicate-16'
import { IconXSquare16 } from '@hashicorp/flight-icons/svg-react/x-square-16'
import copyToClipboard from './copy-to-clipboard'
import analytics, { heapAttributes } from '../../analytics'
import s from './style.module.css'

Expand Down Expand Up @@ -43,8 +42,14 @@ function ClipboardButton({
}

// Otherwise, continue on...
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const isCopied = copyToClipboard(text!)
let isCopied = false
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await navigator.clipboard.writeText(text!)
isCopied = true
} catch (err) {
// noop
}

// If there's an internal failure copying text, exit early to handle the error
if (!isCopied) {
Expand Down
8 changes: 2 additions & 6 deletions packages/code-block/provider/use-indexed-tabs.test.js
Expand Up @@ -17,12 +17,8 @@ afterEach(() => {
* useIndexedTabs hook works as expected.
*/
function TestComponent({ tabGroupIds, defaultTabIdx }) {
const [
localTabIdx,
setActiveTabIdx,
activeTabGroup,
setActiveTabGroup,
] = useIndexedTabs(tabGroupIds, defaultTabIdx)
const [localTabIdx, setActiveTabIdx, activeTabGroup, setActiveTabGroup] =
useIndexedTabs(tabGroupIds, defaultTabIdx)
return (
<div>
<p data-value={localTabIdx}>localTabIdx</p>
Expand Down

1 comment on commit ca647a0

@vercel
Copy link

@vercel vercel bot commented on ca647a0 Dec 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.