Skip to content

Commit

Permalink
Merge pull request #14 from dkonasov/click-to-copy
Browse files Browse the repository at this point in the history
click-to-copy feature
  • Loading branch information
dkonasov authored May 1, 2024
2 parents 976c58e + b58acc3 commit 2a81b01
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 0 deletions.
41 changes: 41 additions & 0 deletions react-ux-semantics-docs/docs/docs/click-to-copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
sidebar_position: 3
title: Click to copy
---

## General description

Hook that allows you to implement copy-by-click semantic – when user clicks on the interface element and gets some predefined text copied to clipboard.

## Usage example

```jsx
export const ComponentWithCopyableText = () => {
const text = 'This text will be copied!';
const [ clickHandler, copyPromise ] = useClickToCopy(text);

return (
<>
<div>{text}</div>
<button onClick={clickHandler}>Copy text</button>
</>
)
};
```

## API

### useClickToCopy

```ts
declare function useClickToCopy(text: string): [() => void, Promise<void>];
```

#### Arguments

|arg|type|description|
|:--|:---|:----------|
|`text`|`string`|Text to copy|


Returns a tuple with handler for a trigger and promise, which will be resolve when the text will be copied.
16 changes: 16 additions & 0 deletions react-ux-semantics/src/click-to-copy/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useCallback } from "react";

export function useClickToCopy(text: string): [() => void, Promise<void>] {
let resolver: () => void;
const promise = new Promise<void>((resolve) => {
resolver = resolve;
});

const handler = useCallback(async () => {
await navigator.clipboard.writeText(text);

resolver();
}, [text, resolver]);

return [handler, promise];
}
1 change: 1 addition & 0 deletions react-ux-semantics/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { useAutohide } from "./autohide";
export { useClickToCopy } from "./click-to-copy";
export { useShowable } from "./showable";
44 changes: 44 additions & 0 deletions react-ux-semantics/tests/click-to-copy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, it, vi, expect, afterEach } from "vitest";
import { renderHook, act } from "@testing-library/react";

import { useClickToCopy } from "../src/click-to-copy";

type DeepPartial<T> = T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;

const navigatorMock: DeepPartial<Navigator> = {
clipboard: {
writeText: vi.fn(() => Promise.resolve()),
},
};

vi.stubGlobal("navigator", navigatorMock);

describe("click to copy", () => {
afterEach(() => {
vi.resetAllMocks();
});

it("should call clipboard method of we API when handler is called", async () => {
const { result } = renderHook(() => useClickToCopy("henlo, fren!"));
const [handler] = result.current;

await act(() => handler());

expect(navigatorMock.clipboard?.writeText).toHaveBeenCalledWith(
"henlo, fren!",
);
});

it("should resolve promise when text was copied to clipboard", async () => {
const { result } = renderHook(() => useClickToCopy("henlo, fren!"));
const [handler, promise] = result.current;

await act(() => handler());

await expect(promise).resolves.toBeFalsy();
});
});

0 comments on commit 2a81b01

Please sign in to comment.