From 620d42a3dea38ee5b033a27bcf542f37526cba72 Mon Sep 17 00:00:00 2001 From: Cuong Vu Date: Wed, 13 May 2020 19:31:27 +0700 Subject: [PATCH] fix: #1160 strip out HTML when pasting --- .../elements/src/components/Editor/index.tsx | 3 ++ .../TextAreaEditor/__tests__/index.tsx | 54 ++++++++++++++++++- .../src/components/TextAreaEditor/index.tsx | 27 +++++++++- .../src/tests/badges/badge-branches.svg | 2 +- .../src/tests/badges/badge-functions.svg | 2 +- scripts/jest/jest-setup.js | 6 +++ 6 files changed, 90 insertions(+), 4 deletions(-) diff --git a/packages/elements/src/components/Editor/index.tsx b/packages/elements/src/components/Editor/index.tsx index 114d06ee20..96c3570b7f 100644 --- a/packages/elements/src/components/Editor/index.tsx +++ b/packages/elements/src/components/Editor/index.tsx @@ -14,6 +14,7 @@ export interface EditorProps { actions?: Array dataTest?: string onBlur?: () => void + onPaste?: (e: any) => void } const defaultActions = [ @@ -44,6 +45,7 @@ export const Editor = ({ contentClass = 'pell-content', dataTest = '', onBlur, + onPaste, }: EditorProps) => { const containerEl = React.useRef(null) @@ -79,6 +81,7 @@ export const Editor = ({ return (
{ expect(spy).toHaveBeenCalledWith(true) }) }) + + describe('handleTextAreaOnPaste', () => { + afterAll(() => { + Object.defineProperty(document, 'queryCommandSupported', { + value: jest.fn(() => true), + writable: true, + }) + }) + const eventMock = { + stopPropagation: jest.fn(), + preventDefault: jest.fn(), + clipboardData: { getData: jest.fn(() => 'copiedText') }, + } as any + + it('should call execCommand insertText with correct plaintext if supported', () => { + const fn = handleTextAreaOnPaste() + fn(eventMock) + const spy = jest.spyOn(document, 'execCommand') + expect(spy).toHaveBeenCalledWith('insertText', false, 'copiedText') + }) + + it('should call execCommand paste with correct plaintext if not supported', () => { + Object.defineProperty(document, 'queryCommandSupported', { + value: jest.fn(() => false), + writable: true, + }) + const fn = handleTextAreaOnPaste() + fn(eventMock) + const spy = jest.spyOn(document, 'execCommand') + expect(spy).toHaveBeenCalledWith('paste', false, 'copiedText') + }) + it('should fallback to window.clipboardData if not supported', () => { + Object.defineProperty(window, 'clipboardData', { + value: { getData: jest.fn(() => 'copiedText') }, + writable: true, + }) + const eventMock = { + stopPropagation: jest.fn(), + preventDefault: jest.fn(), + } as any + const spy = jest.spyOn((window as any).clipboardData, 'getData') + const fn = handleTextAreaOnPaste() + fn(eventMock) + expect(spy).toHaveBeenCalledWith('Text') + }) + }) }) diff --git a/packages/elements/src/components/TextAreaEditor/index.tsx b/packages/elements/src/components/TextAreaEditor/index.tsx index 0f46522f79..3222e3f1af 100644 --- a/packages/elements/src/components/TextAreaEditor/index.tsx +++ b/packages/elements/src/components/TextAreaEditor/index.tsx @@ -9,6 +9,7 @@ export interface TextAreaEditorProps extends EditorProps { labelText: string name: string dataTest?: string + allowPasteRichText?: boolean } export type HandleTextAreaOnChangeParams = { @@ -27,7 +28,30 @@ export const handleTextAreaOnBlur = ({ helpers }: HandleTextAreaOnBlurParams) => helpers.setTouched(true) } -export const TextAreaEditor = ({ name, labelText, placeholder, id, ...restProps }: TextAreaEditorProps) => { +export const handleTextAreaOnPaste = () => e => { + // Stop data actually being pasted into div + e.stopPropagation() + e.preventDefault() + // Get pasted data via clipboard API + const clipboardData = e.clipboardData || (window as any).clipboardData + const pastedData = clipboardData ? clipboardData.getData('Text') : '' + + // need to check for browser compatible + if (document.queryCommandSupported('insertText')) { + document.execCommand('insertText', false, pastedData) + } else { + document.execCommand('paste', false, pastedData) + } +} + +export const TextAreaEditor = ({ + name, + labelText, + placeholder, + id, + allowPasteRichText = false, + ...restProps +}: TextAreaEditorProps) => { const [field, meta, helpers] = useField(name) const hasError = checkError(meta) @@ -44,6 +68,7 @@ export const TextAreaEditor = ({ name, labelText, placeholder, id, ...restProps placeholder={placeholder} defaultContent={field.value} onChange={handleTextAreaOnChange({ field })} + onPaste={allowPasteRichText ? undefined : handleTextAreaOnPaste()} onBlur={handleTextAreaOnBlur({ helpers })} {...restProps} /> diff --git a/packages/marketplace/src/tests/badges/badge-branches.svg b/packages/marketplace/src/tests/badges/badge-branches.svg index 89b6545df4..f7a5866f5c 100644 --- a/packages/marketplace/src/tests/badges/badge-branches.svg +++ b/packages/marketplace/src/tests/badges/badge-branches.svg @@ -1 +1 @@ -Coverage:branchesCoverage:branches70.14%70.14% +Coverage:branchesCoverage:branches70.14%70.14% \ No newline at end of file diff --git a/packages/marketplace/src/tests/badges/badge-functions.svg b/packages/marketplace/src/tests/badges/badge-functions.svg index fb41f4e420..93982e3810 100644 --- a/packages/marketplace/src/tests/badges/badge-functions.svg +++ b/packages/marketplace/src/tests/badges/badge-functions.svg @@ -1 +1 @@ -Coverage:functionsCoverage:functions75.07%75.07% +Coverage:functionsCoverage:functions75.07%75.07% \ No newline at end of file diff --git a/scripts/jest/jest-setup.js b/scripts/jest/jest-setup.js index 42c3ff8de9..b077a139ce 100644 --- a/scripts/jest/jest-setup.js +++ b/scripts/jest/jest-setup.js @@ -419,6 +419,12 @@ Object.defineProperty(document, 'execCommand', { value: jest.fn(), }) +Object.defineProperty(document, 'queryCommandSupported', { + value: jest.fn(() => true), + writable: true, +}) +window.queryCommandSupported = jest.fn() + Object.defineProperty(window, 'location', { value: { href: '',