update
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
const template = document.createElement('template');
|
||||
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
display: inline-block;
|
||||
direction: ltr;
|
||||
font-family: var(--mdc-icon-font, 'Material Icons');
|
||||
font-feature-settings: 'liga';
|
||||
font-size: var(--mdc-icon-size, 24px);
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
letter-spacing: normal;
|
||||
line-height: 1;
|
||||
text-rendering: optimizeLegibility;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</style>
|
||||
<slot></slot>
|
||||
`;
|
||||
|
||||
class MaterialIconElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' }).appendChild(
|
||||
template.content.cloneNode(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!customElements.get('mwc-icon')) {
|
||||
customElements.define('mwc-icon', MaterialIconElement);
|
||||
}
|
||||
@@ -6,15 +6,45 @@
|
||||
* @throws {Error} unable to copy text to clipboard
|
||||
*/
|
||||
export const copyTextToClipboard = async data => {
|
||||
const text =
|
||||
typeof data === 'object' && data !== null
|
||||
? JSON.stringify(data, null, 2)
|
||||
: String(data ?? '');
|
||||
|
||||
let clipboardError;
|
||||
|
||||
try {
|
||||
const text =
|
||||
typeof data === 'object' && data !== null
|
||||
? JSON.stringify(data, null, 2)
|
||||
: String(data ?? '');
|
||||
if (!navigator.clipboard?.writeText) {
|
||||
throw new Error('Clipboard API is unavailable');
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(text);
|
||||
return;
|
||||
} catch (error) {
|
||||
throw new Error(`Unable to copy text to clipboard: ${error.message}`);
|
||||
clipboardError = error;
|
||||
}
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.setAttribute('readonly', '');
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
textarea.style.pointerEvents = 'none';
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
try {
|
||||
textarea.select();
|
||||
textarea.setSelectionRange(0, textarea.value.length);
|
||||
|
||||
if (!document.execCommand?.('copy')) {
|
||||
throw new Error('Fallback copy command failed');
|
||||
}
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Unable to copy text to clipboard: ${clipboardError.message}`
|
||||
);
|
||||
} finally {
|
||||
textarea.remove();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { copyTextToClipboard, handleOtpPaste } from '../clipboard';
|
||||
|
||||
const mockWriteText = vi.fn();
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
writeText: mockWriteText,
|
||||
},
|
||||
});
|
||||
const mockExecCommand = vi.fn();
|
||||
|
||||
const setClipboard = clipboard => {
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: clipboard,
|
||||
});
|
||||
};
|
||||
|
||||
describe('copyTextToClipboard', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setClipboard({ writeText: mockWriteText });
|
||||
document.execCommand = mockExecCommand;
|
||||
mockExecCommand.mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe('with string input', () => {
|
||||
@@ -133,23 +139,38 @@ describe('copyTextToClipboard', () => {
|
||||
it('throws error when clipboard API fails', async () => {
|
||||
const error = new Error('Clipboard access denied');
|
||||
mockWriteText.mockRejectedValueOnce(error);
|
||||
mockExecCommand.mockReturnValueOnce(false);
|
||||
|
||||
await expect(copyTextToClipboard('test')).rejects.toThrow(
|
||||
'Unable to copy text to clipboard: Clipboard access denied'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles clipboard API not available', async () => {
|
||||
// Temporarily remove clipboard API
|
||||
const originalClipboard = navigator.clipboard;
|
||||
delete navigator.clipboard;
|
||||
it('falls back when clipboard API is not available', async () => {
|
||||
setClipboard(undefined);
|
||||
|
||||
await copyTextToClipboard('test');
|
||||
|
||||
expect(mockExecCommand).toHaveBeenCalledWith('copy');
|
||||
expect(document.querySelector('textarea')).toBeNull();
|
||||
});
|
||||
|
||||
it('falls back when clipboard API rejects the request', async () => {
|
||||
mockWriteText.mockRejectedValueOnce(new Error('Clipboard access denied'));
|
||||
|
||||
await copyTextToClipboard('test');
|
||||
|
||||
expect(mockExecCommand).toHaveBeenCalledWith('copy');
|
||||
expect(document.querySelector('textarea')).toBeNull();
|
||||
});
|
||||
|
||||
it('throws when neither clipboard method is available', async () => {
|
||||
setClipboard(undefined);
|
||||
mockExecCommand.mockReturnValueOnce(false);
|
||||
|
||||
await expect(copyTextToClipboard('test')).rejects.toThrow(
|
||||
'Unable to copy text to clipboard:'
|
||||
'Unable to copy text to clipboard: Clipboard API is unavailable'
|
||||
);
|
||||
|
||||
// Restore clipboard API
|
||||
navigator.clipboard = originalClipboard;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user