Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use editor3 internal clipboard to copy entities from open editors #2509

Merged
merged 11 commits into from
Sep 6, 2018

Conversation

pablopunk
Copy link
Contributor

@pablopunk pablopunk commented Aug 29, 2018

SDESK-2996

The problem was that tables could not be pasted between editors because draft-js doesn't handle very well pasting custom entities to another editor. This attempts to use the first editor content (via internal clipboard) and insert it directly into the second editor.

If you copy something from an editor with images/tables/etc, refresh the page, and then paste it to another editor this fix won't work as it has no reference to the other editor. I'm thinking of another way of handling this situation, like copying the actual contentState instead of the html but that can be messy and I think it's not possible right now in draft

*My first approach to this issue was handling all paste by our custom html parser. I did implement this and it worked for tables but images/media were kind of tricky so I went for the second fix.

@pablopunk pablopunk added the wip label Aug 29, 2018
tsconfig.json Outdated
@@ -11,8 +11,8 @@
"target": "es5",
"lib": [
"dom",
"es2015"
"es2017"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: I needed this to get Object.entries recognized by typescript but I ended up dropping that code. I still think we'll need this in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomaskikutis maybe you know, could this commit cause us any problem regarding typescript compilation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for me

"lib": [
    "dom",
    "es2015",
    "es2016"
]

I think you might need to still include es2015 and es2016 even if you have es2017 included.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomaskikutis are you sure? That's weird, I would assume the es2017 standard includes both es2015 and es2016

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try and see, but I think it might not automatically include the features of previous editions of the spec

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it is working right now just with es2017, the compiler doesn't complain about anything

@petrjasek petrjasek added this to the 1.25 milestone Aug 30, 2018
function processPastedHtml(props, html) {
const {onChange, editorState, editorFormat} = props;
let pastedContent = getContentStateFromHtml(html);
function insertContentInState(props: any, _pastedContent: ContentState) : DraftHandleValue {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of props, take the actual arguments you need: editorState, editorFormat, onChange. This way function signature becomes cleaner and you can add type annotations without needing to add it for the entire object.

It also makes it easier to understand and refactor the code if you take fewer arguments. For example pasteContentFromOpenEditor which calls this function(insertContentInState) takes props as an argument, and while I'm looking at pasteContentFromOpenEditor I'm thinking "why does it take all props as an argument? And then I see: oh, it passes the entire object to another function, it must require it then I think. In this case, it's only two levels, but when you keep doing it, you end up with 6 levels of passing the entire object when you only need a few params from it which obfuscates your intention and creates dependencies(on the entire object) which should not be there.

Then comes a time when you need to call that function and you have those 3 required values(editorState, editorFormat, onChange), but not the entire object and you think: "I'll just create an empty object with these 3 values and it will work for that function". And then someone modifying the function thinks "I've got the entire object, I can start using another property" - and the errors start coming.

That's probably more context than required, but I saw these things happen in the code and it's quite simple to avoid it.

By the way, if you use props outside a React component, specify what kind props is that. It's obvious while in a React component, but otherwise it looks like _this as an argument which doesn't really mean anything outside of the class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right. I didn't think about it as I didn't write insertContentInState (just renamed it) but I'm working on it right now while I fix other types. Thanks

@pablopunk pablopunk removed the wip label Sep 4, 2018
package.json Outdated
@@ -28,6 +28,7 @@
],
"main": "scripts/index.js",
"dependencies": {
"@types/draft-js": "^0.10.24",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might not cause any issues, but I'd lock the version.

@@ -25,7 +20,8 @@ function removeMediaFromHtml(htmlString) : string {
return element.innerHTML;
}

function pasteContentFromOpenEditor(props: any, html: string) : DraftHandleValue {
function pasteContentFromOpenEditor(
html: string, editorState: EditorState, onChange: Function, editorFormat: Array<string>) : DraftHandleValue {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's alright for now, but try avoiding the use of Function type, since it's very generic. You can define

interface IEditor3props {
    onChange(editorState: EditorState): void;
}

and then access onChange like a object property, so you have the whole function signature, but don't need to typed it entirely.

function functionWhichTakesOnChangeAsAnArgument(onChange: IEditor3props['onChange']) {
    // ...
}

@@ -34,15 +30,15 @@ function pasteContentFromOpenEditor(props: any, html: string) : DraftHandleValue
if (internalClipboard) {
const blocksArray = [];

internalClipboard.map((b) => blocksArray.push(b));
internalClipboard.forEach((b) => blocksArray.push(b));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't you passing internalClipboard to createFromBlockArray and instead are making a shallow copy?

}
}
}

return DraftHandleValue.NOT_HANDLED;
return 'not-handled';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's wrong with DraftHandleValue?

@@ -6,6 +6,7 @@ import {getDraftSelectionForEntireContent} from './getDraftSelectionForEntireCon
import {resizeDraftSelection} from './resizeDraftSelection';
import {clearInlineStyles} from './clearInlineStyles';
import {changeSuggestionsTypes, blockStylesDescription, paragraphSuggestionTypes} from '../highlightsConfig';
import _ from 'lodash';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import {onlyWhatYouNeed} from 'lodash', so we can have a smaller bundle size.

@@ -1,4 +1,6 @@
/* eslint-disable no-unused-vars */ // eslint complains about EditorChangeType not being used
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use eslint-disable-next-line to avoid having to re-enable rules.

// eslint-disable-next-line no-unused-vars
import {EditorChangeType} from 'draft-js';

import {SelectionState, Modifier, EditorState} from 'draft-js';

@@ -0,0 +1,19 @@
patch-package
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ioanpocol I added a new dependency: patch-package. With it we can modify any package under node_modules and make a patch like this one to fix a bug without waiting for a PR to be merged. This will patch the module after an npm install automatically

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@pablopunk pablopunk merged commit d22909d into superdesk:master Sep 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants