-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make CopyToClipboard work for text with newlines [#154782444] (#555)
- Loading branch information
Showing
4 changed files
with
105 additions
and
149 deletions.
There are no files selected for viewing
72 changes: 34 additions & 38 deletions
72
spec/pivotal-ui-react/copy-to-clipboard/clipboard-helper_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,48 @@ | ||
import '../spec_helper' ; | ||
import ClipboardHelper from '../../../src/react/copy-to-clipboard/clipboard-helper'; | ||
import {copy} from '../../../src/react/copy-to-clipboard/clipboard-helper'; | ||
|
||
describe('ClipboardHelper', () => { | ||
let subject; | ||
|
||
beforeEach(() => { | ||
subject = ClipboardHelper; | ||
}); | ||
let document, copyText, textarea; | ||
|
||
describe('copy', () => { | ||
const element = 'mock element'; | ||
let window, document, range, selection; | ||
beforeEach(() => { | ||
range = jasmine.createSpyObj('range', ['selectNode']); | ||
selection = jasmine.createSpyObj('selection', ['removeAllRanges', 'addRange']); | ||
window = jasmine.createSpyObj('window', ['getSelection']); | ||
window.getSelection.and.returnValue(selection); | ||
document = jasmine.createSpyObj('document', ['createRange', 'execCommand']); | ||
document.createRange.and.returnValue(range); | ||
document = jasmine.createSpyObj('document', [ | ||
'execCommand', | ||
'createElement' | ||
]); | ||
|
||
document.body = jasmine.createSpyObj('body', [ | ||
'appendChild', | ||
'removeChild' | ||
]); | ||
|
||
textarea = jasmine.createSpyObj('textarea', ['select']); | ||
document.createElement.and.returnValue(textarea); | ||
|
||
copyText = 'Text to be copied'; | ||
copy(document, copyText); | ||
}); | ||
|
||
it('does some useful things', () => { | ||
subject.copy(window, document, element); | ||
expect(selection.removeAllRanges).toHaveBeenCalled(); | ||
expect(selection.addRange).toHaveBeenCalledWith(range); | ||
expect(range.selectNode).toHaveBeenCalledWith(element); | ||
expect(document.execCommand).toHaveBeenCalledWith('copy'); | ||
expect(selection.removeAllRanges.calls.count()).toBe(2); | ||
it('creates a textarea and appends it to the body', () => { | ||
expect(document.createElement).toHaveBeenCalledWith('textarea'); | ||
expect(document.body.appendChild).toHaveBeenCalledWith(textarea); | ||
}); | ||
}); | ||
|
||
describe('select', () => { | ||
const element = 'mock element'; | ||
let window, document, range, selection; | ||
beforeEach(() => { | ||
range = jasmine.createSpyObj('range', ['selectNode']); | ||
selection = jasmine.createSpyObj('selection', ['removeAllRanges', 'addRange']); | ||
window = jasmine.createSpyObj('window', ['getSelection']); | ||
window.getSelection.and.returnValue(selection); | ||
document = jasmine.createSpyObj('document', ['createRange']); | ||
document.createRange.and.returnValue(range); | ||
it('sets the correct value and className on the textarea', () => { | ||
expect(textarea.value).toBe(copyText); | ||
expect(textarea.className).toBe('sr-only'); | ||
}); | ||
|
||
it('selects the textarea content', () => { | ||
expect(textarea.select).toHaveBeenCalled(); | ||
}); | ||
|
||
it('executes a copy command', () => { | ||
expect(document.execCommand).toHaveBeenCalledWith('copy'); | ||
}); | ||
|
||
it('does some useful things', () => { | ||
subject.select(window, document, element); | ||
expect(selection.removeAllRanges).toHaveBeenCalled(); | ||
expect(selection.addRange).toHaveBeenCalledWith(range); | ||
expect(range.selectNode).toHaveBeenCalledWith(element); | ||
it('removes the textarea from the body', () => { | ||
expect(document.body.removeChild).toHaveBeenCalledWith(textarea); | ||
}); | ||
}); | ||
}); | ||
}); |
131 changes: 51 additions & 80 deletions
131
spec/pivotal-ui-react/copy-to-clipboard/copy-to-clipboard_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,104 +1,75 @@ | ||
import '../spec_helper'; | ||
|
||
import {CopyToClipboard} from '../../../src/react/copy-to-clipboard'; | ||
import ClipboardHelper from '../../../src/react/copy-to-clipboard/clipboard-helper'; | ||
|
||
describe('CopyToClipboard', () => { | ||
const text = 'some copy text'; | ||
let onClick, getWindow, window, document, range, selection, subject; | ||
let text, onClick, subject; | ||
|
||
beforeEach(() => { | ||
text = 'some copy text'; | ||
onClick = jasmine.createSpy('onClick'); | ||
|
||
range = jasmine.createSpyObj('range', ['selectNode']); | ||
selection = jasmine.createSpyObj('selection', ['removeAllRanges', 'addRange']); | ||
window = jasmine.createSpyObj('window', ['getSelection']); | ||
getWindow = jasmine.createSpy('getWindow').and.returnValue(window); | ||
document = jasmine.createSpyObj('document', ['createRange', 'execCommand']); | ||
spyOn(ClipboardHelper, 'copy'); | ||
|
||
document.createRange.and.returnValue(range); | ||
window.document = document; | ||
window.getSelection.and.returnValue(selection); | ||
subject = ReactDOM.render(<CopyToClipboard {...{ | ||
text, | ||
onClick | ||
}}/>, root); | ||
}); | ||
|
||
describe('CopyToClipboard (basic)', () => { | ||
const renderComponent = props => ReactDOM.render(<CopyToClipboard {...props}/>, root); | ||
|
||
it('renders the text', () => { | ||
subject = renderComponent({text, onClick, className: 'test-class', id: 'test-id', style: {opacity: '0.5'}}); | ||
expect('.sr-only').toHaveText(text); | ||
}); | ||
it('renders an anchor', () => { | ||
expect('a.pui-copy-to-clipboard').toExist(); | ||
expect('a.pui-copy-to-clipboard').toHaveAttr('role', 'button'); | ||
}); | ||
|
||
it('propagates attributes', () => { | ||
subject = renderComponent({text, onClick, className: 'test-class', id: 'test-id', style: {opacity: '0.5'}}); | ||
it('renders a hidden tooltip with default text', () => { | ||
expect('.tooltip-container').toHaveClass('tooltip-container-hidden'); | ||
expect('.tooltip-content').toHaveText('Copied'); | ||
}); | ||
|
||
expect('.copy-to-clipboard').toHaveClass('test-class'); | ||
expect('.copy-to-clipboard').toHaveAttr('id', 'test-id'); | ||
expect('.copy-to-clipboard').toHaveCss({opacity: '0.5'}); | ||
describe('when given tooltip text', () => { | ||
beforeEach(() => { | ||
subject::setProps({tooltip: 'Copied successfully!'}); | ||
}); | ||
|
||
describe('clicking on the element', () => { | ||
it('renders a tooltip that says "Copied in"', () => { | ||
subject = renderComponent({ | ||
getWindow, | ||
text, | ||
onClick, | ||
className: 'test-class', | ||
id: 'test-id', | ||
style: {opacity: '0.5'}, | ||
tooltip: 'Copied in' | ||
}); | ||
|
||
$('.copy-to-clipboard .tooltip').simulate('click'); | ||
|
||
expect('.tooltip-container').toHaveClass('tooltip-container-visible'); | ||
expect('.tooltip-content').toHaveText('Copied in'); | ||
}); | ||
|
||
it('hides tooltip after 1 seconds', () => { | ||
subject = renderComponent({ | ||
getWindow, | ||
text, | ||
onClick, | ||
className: 'test-class', | ||
id: 'test-id', | ||
style: {opacity: '0.5'} | ||
}); | ||
|
||
$('.copy-to-clipboard').simulate('click'); | ||
jasmine.clock().tick(2000); | ||
it('uses the custom tooltip text', () => { | ||
expect('.tooltip-content').toHaveText('Copied successfully!'); | ||
}); | ||
}); | ||
|
||
expect('.tooltip-container').not.toHaveClass('tooltip-container-visible'); | ||
}); | ||
describe('when given additional props', () => { | ||
beforeEach(() => { | ||
subject::setProps({className: 'test-class', id: 'test-id', style: {opacity: '0.5'}}); | ||
}); | ||
|
||
it('copies the text to the clipboard', () => { | ||
subject = renderComponent({ | ||
getWindow, | ||
text, | ||
onClick, | ||
className: 'test-class', | ||
id: 'test-id', | ||
style: {opacity: '0.5'} | ||
}); | ||
it('passes the props to the anchor', () => { | ||
expect('a.pui-copy-to-clipboard').toHaveClass('test-class'); | ||
expect('a.pui-copy-to-clipboard').toHaveAttr('id', 'test-id'); | ||
expect('a.pui-copy-to-clipboard').toHaveCss({opacity: '0.5'}); | ||
}); | ||
}); | ||
|
||
$('.copy-to-clipboard').simulate('click'); | ||
describe('clicking on the element', () => { | ||
beforeEach(() => { | ||
$('.pui-copy-to-clipboard .tooltip').click(); | ||
$('.pui-copy-to-clipboard').click(); | ||
}); | ||
|
||
expect(document.execCommand).toHaveBeenCalledWith('copy'); | ||
}); | ||
it('makes tooltip visible', () => { | ||
expect('.tooltip-container').toHaveClass('tooltip-container-visible'); | ||
}); | ||
|
||
it('calls the provided callback', () => { | ||
subject = renderComponent({ | ||
getWindow, | ||
text, | ||
onClick, | ||
className: 'test-class', | ||
id: 'test-id', | ||
style: {opacity: '0.5'} | ||
}); | ||
it('hides tooltip after 1 seconds', () => { | ||
jasmine.clock().tick(2000); | ||
expect('.tooltip-container').not.toHaveClass('tooltip-container-visible'); | ||
}); | ||
|
||
$('.copy-to-clipboard').simulate('click'); | ||
it('copies the text to the clipboard', () => { | ||
expect(ClipboardHelper.copy).toHaveBeenCalledWith(document, text); | ||
}); | ||
|
||
expect(onClick).toHaveBeenCalled(); | ||
}); | ||
it('calls the provided callback', () => { | ||
expect(onClick).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,16 @@ | ||
const select = (window, document, element) => { | ||
window.getSelection().removeAllRanges(); | ||
const range = document.createRange(); | ||
range.selectNode(element); | ||
window.getSelection().addRange(range); | ||
}; | ||
const copy = (document, text) => { | ||
const textarea = document.createElement('textarea'); | ||
textarea.className = 'sr-only'; | ||
textarea.value = text; | ||
document.body.appendChild(textarea); | ||
|
||
const copy = (window, document, element) => { | ||
select(window, document, element); | ||
try { | ||
textarea.select(); | ||
document.execCommand('copy'); | ||
} catch (e) { | ||
} finally { | ||
window.getSelection().removeAllRanges(); | ||
document.body.removeChild(textarea); | ||
} | ||
}; | ||
|
||
export default {select, copy}; | ||
export default {copy}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters