Skip to content

Commit

Permalink
refactor(DynamicEditField): conserve typical paste features when sani…
Browse files Browse the repository at this point in the history
…tizing paste input

We get the range that the user has selected (aka where the selection starts and ends, or, if there is no selection but just a caret, we get the location of the caret), after sanitizing the pasted text (striping all html and new lines), insert the pasted text over/into the selection.

Afterwards, adjust the selection to now select the pasted text.
  • Loading branch information
ramfox committed Sep 21, 2020
1 parent deee663 commit b6807bc
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 15 deletions.
61 changes: 50 additions & 11 deletions app/components/form/DynamicEditField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,59 @@ const DynamicEditField: React.FunctionComponent<DynamicEditFieldProps> = ({
data-placeholder={placeholder}
onFocus={onFocus}
onBlur={onBlur}
// TODO (ramfox): refactor as a util `onPasteSanitized` func
onPaste={(e) => {
e.preventDefault()
e.stopPropagation()
var str = stripHtml(e.clipboardData.getData('text')).result

/**
* TODO (ramfox): this replaces the div text with the pasted text
* instead it should:
* 1) find the cursor position
* 2) insert the text at the cursor position
* 3) move the cursor to after the pasted text
* 4) trigger an update (maybe just force the 'handleChange' event?)
*/
e.currentTarget.innerHTML = str
var paste = stripHtml(e.clipboardData.getData('text')).result

// we currently don't allow new lines in the description
paste = paste.replace(/[\r\n]/gm, " ")

// the selection is where we want to paste over/into
var sel = window.getSelection()
if (!sel) {
return
}
// get the start of the selection
// get the end of the selection
var text = e.currentTarget.innerText
var start = sel.anchorOffset
var end = sel.focusOffset
if (!text) {
text = ''
start = 0
end = 0
}

// if start is after end, switch them
if (start - end > 0) {
var temp = start
start = end
end = temp
}

// slice the pasted content
text = text.slice(0, start) + paste + text.slice(end)
// set the text to be the new content
e.currentTarget.innerText = text
// force an update!
handleChange(e)

// adjust the selection range to cover the pasted content
var range = sel.getRangeAt(0)

if (!e.currentTarget.firstChild) {
return
}
// determine where the range should end
// and adjust the range to start and end over the pasted content
end = start + paste.length

// because we striped html & line breaks we can trust
// there is only one node in the current target
range.setStart(e.currentTarget.firstChild, start)
range.setEnd(e.currentTarget.firstChild, end)
}}
>{value}</div>
</div>
Expand Down
5 changes: 1 addition & 4 deletions test/app/utils/formValidation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ describe('formValidation', () => {
const datasetNameGoodCases = [
'a',
'hello_world',
'hello-world',
'pmfqbx5bhe7w4nbonqj6zu2abb15txq7vc5yfgysawjbdiqaxghvt4iy3rdyhvxg2v52mcsqeh1yymxe6ciz1lsxwmfsqyzdkhfjdksajfiejfijeilsdjafijdsilafjdkmciejntiesail'
]

Expand All @@ -120,10 +121,6 @@ describe('formValidation', () => {
})

const datasetNameBadCases = [
{
string: 'hello-world',
err: ERR_INVALID_DATASETNAME_CHARACTERS
},
{
string: 'hi👋🏻',
err: ERR_INVALID_DATASETNAME_CHARACTERS
Expand Down

0 comments on commit b6807bc

Please sign in to comment.