Skip to content
This repository was archived by the owner on Feb 6, 2023. It is now read-only.

Commit 0f5427a

Browse files
Daniel Quadros de Mirandafacebook-github-bot
Daniel Quadros de Miranda
authored andcommitted
draft-js: clean up useless divs from HTML when pasting content
Summary: When pasting rich (HTML) content into a Draft editor, the generated blocks are flattened such that they can be represented in the internal state model. Until a change to the tree-based model is fully implemented, that may cause formatting to be lost if the content is wrapped in multiple levels of divs, as the parsing does not traverse them deeply. Improve the amount of formatting that can be preserved in the current model by stripping out divs considered 'useless' (with no text or styling), such that more important elements can be 'brought to the surface' and parsed in a more pleasant manner. Reviewed By: claudiopro Differential Revision: D15923965 fbshipit-source-id: 72824ddba69b5d08374f505187ed4a6ea7c4d573
1 parent ceaeebf commit 0f5427a

File tree

3 files changed

+104
-4
lines changed

3 files changed

+104
-4
lines changed

src/model/encoding/__tests__/__snapshots__/convertFromHTMLToContentBlocks2-test.js.snap

+65
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,71 @@ lorem ipsum
29672967
]
29682968
`;
29692969

2970+
exports[`eliminates useless blocks when experimentalTreeDataSupport is disabled 1`] = `false`;
2971+
2972+
exports[`eliminates useless blocks when experimentalTreeDataSupport is disabled 2`] = `
2973+
Array [
2974+
Object {
2975+
"characterList": Array [
2976+
Object {
2977+
"entity": null,
2978+
"style": Array [],
2979+
},
2980+
Object {
2981+
"entity": null,
2982+
"style": Array [],
2983+
},
2984+
Object {
2985+
"entity": null,
2986+
"style": Array [],
2987+
},
2988+
Object {
2989+
"entity": null,
2990+
"style": Array [],
2991+
},
2992+
Object {
2993+
"entity": null,
2994+
"style": Array [],
2995+
},
2996+
],
2997+
"data": Object {},
2998+
"depth": 0,
2999+
"key": "key2",
3000+
"text": "Hello",
3001+
"type": "unstyled",
3002+
},
3003+
Object {
3004+
"characterList": Array [
3005+
Object {
3006+
"entity": null,
3007+
"style": Array [],
3008+
},
3009+
Object {
3010+
"entity": null,
3011+
"style": Array [],
3012+
},
3013+
Object {
3014+
"entity": null,
3015+
"style": Array [],
3016+
},
3017+
Object {
3018+
"entity": null,
3019+
"style": Array [],
3020+
},
3021+
Object {
3022+
"entity": null,
3023+
"style": Array [],
3024+
},
3025+
],
3026+
"data": Object {},
3027+
"depth": 0,
3028+
"key": "key3",
3029+
"text": "World",
3030+
"type": "unstyled",
3031+
},
3032+
]
3033+
`;
3034+
29703035
exports[`highlighted text should be recognized and considered styled characters 1`] = `
29713036
Array [
29723037
Immutable.Record {

src/model/encoding/__tests__/convertFromHTMLToContentBlocks2-test.js

+16
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,22 @@ test('does not convert deeply nested html blocks when experimentalTreeDataSuppor
254254
});
255255
});
256256

257+
test('eliminates useless blocks when experimentalTreeDataSupport is disabled', () => {
258+
const html_string = `
259+
<div>
260+
<div>
261+
<div>Hello</div>
262+
</div>
263+
<div>World</div>
264+
</div>
265+
`;
266+
267+
expect(AreTreeBlockNodesEquivalent(html_string)).toMatchSnapshot();
268+
assertConvertFromHTMLToContentBlocks(html_string, {
269+
experimentalTreeDataSupport: false,
270+
});
271+
});
272+
257273
SUPPORTED_TAGS.forEach(tag =>
258274
testConvertingAdjacentHtmlElementsToContentBlocks(tag, true),
259275
);

src/model/encoding/convertFromHTMLToContentBlocks2.js

+23-4
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,26 @@ class ContentBlocksBuilder {
658658
}
659659
}
660660

661+
/**
662+
* Remove 'useless' container nodes from the block config hierarchy, by
663+
* replacing them with their children.
664+
*/
665+
666+
_hoistContainersInBlockConfigs(
667+
blockConfigs: Array<ContentBlockConfig>,
668+
): List<ContentBlockConfig> {
669+
const hoisted = List(blockConfigs).flatMap(blockConfig => {
670+
// Don't mess with useful blocks
671+
if (blockConfig.type !== 'unstyled' || blockConfig.text !== '') {
672+
return [blockConfig];
673+
}
674+
675+
return this._hoistContainersInBlockConfigs(blockConfig.childConfigs);
676+
});
677+
678+
return hoisted;
679+
}
680+
661681
// ***********************************************************************
662682
// The two methods below are used for backward compatibility when
663683
// experimentalTreeDataSupport is disabled.
@@ -667,9 +687,8 @@ class ContentBlocksBuilder {
667687
* text content.
668688
*/
669689
_toFlatContentBlocks(blockConfigs: Array<ContentBlockConfig>) {
670-
const l = blockConfigs.length - 1;
671-
for (let i = 0; i <= l; i++) {
672-
const config = blockConfigs[i];
690+
const cleanConfigs = this._hoistContainersInBlockConfigs(blockConfigs);
691+
cleanConfigs.forEach(config => {
673692
const {text, characterList} = this._extractTextFromBlockConfigs(
674693
config.childConfigs,
675694
);
@@ -680,7 +699,7 @@ class ContentBlocksBuilder {
680699
characterList: config.characterList.concat(characterList),
681700
}),
682701
);
683-
}
702+
});
684703
}
685704

686705
/**

0 commit comments

Comments
 (0)