Commit a185d1ee authored by Alejandro Celaya's avatar Alejandro Celaya Committed by Alejandro Celaya

Remove deprecated copyText function

parent 1645a344
......@@ -17,7 +17,7 @@ import { isPrivate } from '../../helpers/permissions';
import { withServices } from '../../service-context';
import type { ToastMessengerService } from '../../services/toast-messenger';
import { useSidebarStore } from '../../store';
import { copyText } from '../../util/copy-to-clipboard';
import { copyPlainText } from '../../util/copy-to-clipboard';
import MenuArrow from '../MenuArrow';
import ShareLinks from '../ShareLinks';
......@@ -94,9 +94,9 @@ function AnnotationShareControl({
// bears further discussion.
const showShareLinks = inContextAvailable;
const copyShareLink = () => {
const copyShareLink = async () => {
try {
copyText(shareUri);
await copyPlainText(shareUri);
toastMessenger.success('Copied share link to clipboard');
} catch (err) {
toastMessenger.error('Unable to copy link');
......
......@@ -66,7 +66,7 @@ describe('AnnotationShareControl', () => {
};
fakeCopyToClipboard = {
copyText: sinon.stub(),
copyPlainText: sinon.stub(),
};
fakeToastMessenger = {
success: sinon.stub(),
......@@ -147,16 +147,16 @@ describe('AnnotationShareControl', () => {
getIconButton(wrapper, 'CopyIcon').props().onClick();
assert.calledWith(
fakeCopyToClipboard.copyText,
fakeCopyToClipboard.copyPlainText,
'https://www.example.com',
);
});
it('confirms link copy when successful', () => {
it('confirms link copy when successful', async () => {
const wrapper = createComponent();
openElement(wrapper);
getIconButton(wrapper, 'CopyIcon').props().onClick();
await getIconButton(wrapper, 'CopyIcon').props().onClick();
assert.calledWith(
fakeToastMessenger.success,
......@@ -165,7 +165,7 @@ describe('AnnotationShareControl', () => {
});
it('flashes an error if link copying unsuccessful', () => {
fakeCopyToClipboard.copyText.throws();
fakeCopyToClipboard.copyPlainText.throws();
const wrapper = createComponent();
openElement(wrapper);
......
......@@ -8,7 +8,7 @@ import { withServices } from '../../service-context';
import type { GroupsService } from '../../services/groups';
import type { ToastMessengerService } from '../../services/toast-messenger';
import { useSidebarStore } from '../../store';
import { copyText } from '../../util/copy-to-clipboard';
import { copyPlainText } from '../../util/copy-to-clipboard';
import MenuItem from '../MenuItem';
export type GroupListItemProps = {
......@@ -75,9 +75,9 @@ function GroupListItem({
onExpand(!isExpanded);
};
const copyLink = (url: string) => {
const copyLink = async (url: string) => {
try {
copyText(url);
await copyPlainText(url);
toastMessenger.success(`Copied link for "${group.name}"`);
} catch (err) {
toastMessenger.error('Unable to copy link');
......
......@@ -6,7 +6,7 @@ import GroupListItem, { $imports } from '../GroupListItem';
describe('GroupListItem', () => {
let fakeConfirm;
let fakeCopyText;
let fakeCopyPlainText;
let fakeToastMessenger;
let fakeGroupsService;
let fakeStore;
......@@ -47,7 +47,7 @@ describe('GroupListItem', () => {
leave: sinon.stub(),
};
fakeCopyText = sinon.stub();
fakeCopyPlainText = sinon.stub();
function FakeMenuItem() {
return null;
......@@ -64,7 +64,7 @@ describe('GroupListItem', () => {
$imports.$mock({
'../MenuItem': FakeMenuItem,
'../../util/copy-to-clipboard': {
copyText: fakeCopyText,
copyPlainText: fakeCopyPlainText,
},
'../../helpers/group-list-item-common': fakeGroupListItemCommon,
'../../store': { useSidebarStore: () => fakeStore },
......@@ -87,10 +87,10 @@ describe('GroupListItem', () => {
);
};
function clickMenuItem(wrapper, label) {
act(() => {
wrapper.find(`MenuItem[label="${label}"]`).props().onClick();
});
async function clickMenuItem(wrapper, label) {
await act(() =>
wrapper.find(`MenuItem[label="${label}"]`).props().onClick(),
);
wrapper.update();
}
......@@ -359,22 +359,22 @@ describe('GroupListItem', () => {
});
});
it('copies activity URL if "Copy link" action is clicked', () => {
it('copies activity URL if "Copy link" action is clicked', async () => {
const wrapper = createGroupListItem(fakeGroup, {
isExpanded: true,
});
clickMenuItem(getSubmenu(wrapper), 'Copy invite link');
assert.calledWith(fakeCopyText, 'https://annotate.com/groups/groupid');
await clickMenuItem(getSubmenu(wrapper), 'Copy invite link');
assert.calledWith(fakeCopyPlainText, 'https://annotate.com/groups/groupid');
assert.calledWith(fakeToastMessenger.success, 'Copied link for "Test"');
});
it('reports an error if "Copy link" action fails', () => {
fakeCopyText.throws(new Error('Something went wrong'));
fakeCopyPlainText.throws(new Error('Something went wrong'));
const wrapper = createGroupListItem(fakeGroup, {
isExpanded: true,
});
clickMenuItem(getSubmenu(wrapper), 'Copy invite link');
assert.calledWith(fakeCopyText, 'https://annotate.com/groups/groupid');
assert.calledWith(fakeCopyPlainText, 'https://annotate.com/groups/groupid');
assert.calledWith(fakeToastMessenger.error, 'Unable to copy link');
});
});
......@@ -11,7 +11,7 @@ import { pageSharingLink } from '../../helpers/annotation-sharing';
import { withServices } from '../../service-context';
import type { ToastMessengerService } from '../../services/toast-messenger';
import { useSidebarStore } from '../../store';
import { copyText } from '../../util/copy-to-clipboard';
import { copyPlainText } from '../../util/copy-to-clipboard';
import ShareLinks from '../ShareLinks';
import LoadingSpinner from './LoadingSpinner';
......@@ -32,10 +32,10 @@ function ShareAnnotations({ toastMessenger }: ShareAnnotationsProps) {
const shareURI =
sharingReady && pageSharingLink(mainFrame.uri, focusedGroup.id);
const copyShareLink = useCallback(() => {
const copyShareLink = useCallback(async () => {
try {
if (shareURI) {
copyText(shareURI);
await copyPlainText(shareURI);
toastMessenger.success('Copied share link to clipboard');
}
} catch (err) {
......
......@@ -26,7 +26,7 @@ describe('ShareAnnotations', () => {
beforeEach(() => {
fakeBouncerLink = 'http://hyp.is/go?url=http%3A%2F%2Fwww.example.com';
fakeCopyToClipboard = {
copyText: sinon.stub(),
copyPlainText: sinon.stub(),
};
fakePageSharingLink = sinon.stub().returns(fakeBouncerLink);
......@@ -148,13 +148,13 @@ describe('ShareAnnotations', () => {
wrapper.find('IconButton').props().onClick();
assert.calledWith(fakeCopyToClipboard.copyText, fakeBouncerLink);
assert.calledWith(fakeCopyToClipboard.copyPlainText, fakeBouncerLink);
});
it('confirms link copy when successful', () => {
it('confirms link copy when successful', async () => {
const wrapper = createComponent();
wrapper.find('IconButton').props().onClick();
await wrapper.find('IconButton').props().onClick();
assert.calledWith(
fakeToastMessenger.success,
......@@ -163,7 +163,7 @@ describe('ShareAnnotations', () => {
});
it('flashes an error if link copying unsuccessful', () => {
fakeCopyToClipboard.copyText.throws();
fakeCopyToClipboard.copyPlainText.throws();
const wrapper = createComponent();
wrapper.find('IconButton').props().onClick();
......
......@@ -5,7 +5,7 @@ import type { ComponentChildren } from 'preact';
import type { VersionData } from '../helpers/version-data';
import { withServices } from '../service-context';
import type { ToastMessengerService } from '../services/toast-messenger';
import { copyText } from '../util/copy-to-clipboard';
import { copyPlainText } from '../util/copy-to-clipboard';
type VersionInfoItemProps = {
label: string;
......@@ -37,9 +37,9 @@ export type VersionInfoProps = {
};
function VersionInfo({ toastMessenger, versionData }: VersionInfoProps) {
const copyVersionData = () => {
const copyVersionData = async () => {
try {
copyText(versionData.asFormattedString());
await copyPlainText(versionData.asFormattedString());
toastMessenger.success('Copied version info to clipboard');
} catch (err) {
toastMessenger.error('Unable to copy version info');
......
......@@ -31,7 +31,7 @@ describe('VersionInfo', () => {
beforeEach(() => {
fakeCopyToClipboard = {
copyText: sinon.stub(),
copyPlainText: sinon.stub(),
};
$imports.$mock(mockImportedComponents());
$imports.$mock({
......@@ -82,13 +82,13 @@ describe('VersionInfo', () => {
wrapper.find('Button').props().onClick();
assert.calledWith(fakeCopyToClipboard.copyText, 'fakeString');
assert.calledWith(fakeCopyToClipboard.copyPlainText, 'fakeString');
});
it('confirms info copy when successful', () => {
it('confirms info copy when successful', async () => {
const wrapper = createComponent();
wrapper.find('Button').props().onClick();
await wrapper.find('Button').props().onClick();
assert.calledWith(
fakeToastMessenger.success,
......@@ -97,7 +97,7 @@ describe('VersionInfo', () => {
});
it('flashes an error if info copying unsuccessful', () => {
fakeCopyToClipboard.copyText.throws();
fakeCopyToClipboard.copyPlainText.throws();
const wrapper = createComponent();
wrapper.find('Button').props().onClick();
......
/**
* Copy the string `text` to the clipboard.
*
* In most browsers, this function can only be called in response to a user
* gesture. For example in response to a "click" event.
*
* @throws {Error}
* This function may throw an exception if the browser rejects the attempt
* to copy text.
*
* @deprecated Use copyPlainText instead
*/
export function copyText(text: string) {
const temp = document.createElement('textarea'); // use textarea instead of input to preserve line breaks
temp.value = text;
temp.setAttribute('data-testid', 'copy-text');
// Recipe from https://stackoverflow.com/a/34046084/14463679
temp.contentEditable = 'true';
document.body.appendChild(temp);
temp.focus();
try {
const range = document.createRange();
const selection = document.getSelection()!;
selection.removeAllRanges();
range.selectNodeContents(temp);
selection.addRange(range);
temp.setSelectionRange(0, temp.value.length);
document.execCommand('copy');
} finally {
temp.remove();
}
}
/**
* Copy the string `text` to the clipboard verbatim.
*
......
import { copyPlainText, copyHTML, copyText } from '../copy-to-clipboard';
import { copyPlainText, copyHTML } from '../copy-to-clipboard';
describe('copy-to-clipboard', () => {
const createFakeNavigator = clipboard => ({ clipboard });
describe('copyText', () => {
beforeEach(() => {
sinon.stub(document, 'execCommand');
});
afterEach(() => {
document.execCommand.restore();
});
/**
* Returns the temporary element used to hold text being copied.
*/
function tempSpan() {
return document.querySelector('[data-testid=copy-text]');
}
beforeEach(() => {
// Make no hidden element created for copying text has been left over
// from a previous test.
assert.isNull(tempSpan());
// Make sure there is nothing already selected to copy.
window.getSelection().removeAllRanges();
});
it('copies the passed text to the clipboard', () => {
// We can't actually copy to the clipboard due to security restrictions,
// but we can verify that `execCommand("copy")` was called and that the
// passed text was selected at the time.
document.execCommand.callsFake(() => {
assert.equal(document.getSelection().toString(), 'test string');
});
copyText('test string');
assert.calledWith(document.execCommand, 'copy');
assert.isNull(tempSpan());
});
it('removes temporary span if copying fails', () => {
document.execCommand.callsFake(() => {
assert.ok(tempSpan());
throw new Error('No clipboard access for you!');
});
try {
copyText('fibble-wobble');
} catch (e) {
assert.equal(e.message, 'No clipboard access for you!');
}
assert.isNull(tempSpan());
});
});
describe('copyPlainText', () => {
it('writes provided text to clipboard', async () => {
const text = 'Lorem ipsum dolor sit amet';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment