Skip to content

Commit a003933

Browse files
authored
Merge pull request #16 from productboard/feature/nested-list-parsing
Traverse nested list items
2 parents 7e471e0 + 7272ef3 commit a003933

File tree

4 files changed

+83
-7
lines changed

4 files changed

+83
-7
lines changed

src/docsSoap.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ const wrapNode = (
2727
result: Node
2828
): Node => {
2929
let newNode = result.cloneNode(true);
30+
if (!inner) {
31+
return newNode;
32+
}
3033
if (inner.style && inner.style.fontWeight === styles.BOLD) {
3134
newNode = wrapNodeInline(newNode, elements.BOLD);
3235
}
@@ -86,13 +89,17 @@ const applyInlineStyles = (
8689
const getCleanNode = (
8790
node: Node
8891
): Array<Node> => {
89-
if (node.childNodes && node.childNodes.length <= 1) {
92+
if (node.childNodes && (node.childNodes.length <= 1 || node.nodeName === 'OL' || node.nodeName === 'UL')) {
9093
let newWrapper = null;
9194
let newNode = document.createTextNode(node.textContent);
92-
if (node.nodeName === 'LI' || node.nodeName === 'UL') {
93-
// TODO: Handle nested bullets
94-
newWrapper = document.createElement('li');
95-
newNode = applyBlockStyles(node.childNodes[0].childNodes[0]);
95+
if (node.nodeName === 'UL' || node.nodeName === 'OL' || node.nodeName === 'LI') {
96+
newWrapper = document.createElement(node.nodeName);
97+
newNode = document.createDocumentFragment();
98+
const items = [];
99+
for (let i = 0; i < node.childNodes.length; i++) {
100+
items.push(...getCleanNode(node.childNodes[i]));
101+
}
102+
items.map(i => newNode.appendChild(i));
96103
} else if (node.nodeName === 'P') {
97104
newWrapper = document.createElement('p');
98105
newNode = applyBlockStyles(node);

test/.eslintrc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"env": {
3+
"mocha": true
4+
}
5+
}
6+

test/docsSoapSpec.js

+62-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use es6';
22

3-
import { elements } from '../src/constants'
3+
import { elements } from '../src/constants';
44
import documents from './fixtures/documents';
55
import docsSoap from '../src/index';
66
import expect from 'expect';
@@ -10,6 +10,13 @@ import parseHTML from '../src/parseHTML';
1010
describe('Google Docs Converter', () => {
1111
jsdom();
1212

13+
it('converts plain doc', () => {
14+
const doc = docsSoap(documents.plain);
15+
expect(doc).toExist();
16+
const parsed = parseHTML(doc);
17+
expect(parsed.textContent).toBe('Hello world.');
18+
});
19+
1320
it('converts inline styles from google docs properly', () => {
1421
const rawContents = parseHTML(documents.inlineStyles);
1522
expect(rawContents.querySelectorAll(elements.BOLD).length).toBe(0);
@@ -37,4 +44,58 @@ describe('Google Docs Converter', () => {
3744
expect(doc.querySelectorAll(elements.ITALIC).length).toBe(2);
3845
expect(doc.querySelectorAll(elements.ANCHOR).length).toBe(4);
3946
});
47+
48+
it('converts nested list from google docs properly', () => {
49+
const doc = parseHTML(docsSoap(documents.nestedList));
50+
// ol
51+
// - li
52+
// - ol
53+
// - li
54+
// - ol
55+
// - li
56+
// - li
57+
// - ol
58+
// ....
59+
// ul
60+
// - li
61+
// - li
62+
// - ul
63+
// - li
64+
// - ul
65+
// - li
66+
expect(doc.childNodes[0].nodeName).toBe('OL');
67+
expect(doc.childNodes[0].childNodes.length).toBe(4);
68+
expect(doc.childNodes[0].childNodes[0].nodeName).toBe('LI');
69+
// jsdom bug: should actually include a line break
70+
expect(doc.childNodes[0].childNodes[0].textContent).toBe('Abcd1234');
71+
expect(doc.childNodes[0].childNodes[1].nodeName).toBe('OL');
72+
expect(doc.childNodes[0].childNodes[1].childNodes.length).toBe(2);
73+
expect(doc.childNodes[0].childNodes[1].childNodes[0].nodeName).toBe('LI');
74+
expect(doc.childNodes[0].childNodes[1].childNodes[1].nodeName).toBe('OL');
75+
expect(doc.childNodes[0].childNodes[1].childNodes[1].childNodes.length).toBe(1);
76+
expect(doc.childNodes[0].childNodes[1].childNodes[1].childNodes[0].nodeName).toBe('LI');
77+
expect(doc.childNodes[0].childNodes[2].nodeName).toBe('LI');
78+
expect(doc.childNodes[0].childNodes[2].childNodes.length).toBe(1);
79+
expect(doc.childNodes[0].childNodes[3].nodeName).toBe('OL');
80+
81+
expect(doc.childNodes[1].nodeName).toBe('UL');
82+
expect(doc.childNodes[1].childNodes.length).toBe(3);
83+
expect(doc.childNodes[1].childNodes[0].nodeName).toBe('LI');
84+
expect(doc.childNodes[1].childNodes[1].nodeName).toBe('LI');
85+
expect(doc.childNodes[1].childNodes[2].nodeName).toBe('UL');
86+
expect(doc.childNodes[1].childNodes[2].childNodes[0].nodeName).toBe('LI');
87+
expect(doc.childNodes[1].childNodes[2].childNodes[1].nodeName).toBe('UL');
88+
expect(doc.childNodes[1].childNodes[2].childNodes[1].childNodes[0].nodeName).toBe('LI');
89+
expect(doc.childNodes[1].childNodes[2].childNodes[1].childNodes[0].childNodes.length).toBe(1);
90+
});
91+
92+
it('converts deep nested styles from gdocs properly', () => {
93+
const doc = parseHTML(docsSoap(documents.nestedList));
94+
expect(doc.childNodes[0].childNodes[0].querySelectorAll(elements.BOLD).length).toBe(3);
95+
expect(doc.childNodes[0].childNodes[0].querySelectorAll(elements.UNDERLINE).length).toBe(3);
96+
expect(doc.childNodes[0].childNodes[1].childNodes[1].childNodes[0].querySelectorAll(elements.ITALIC).length).toBe(1);
97+
expect(doc.childNodes[0].childNodes[1].childNodes[1].childNodes[0].querySelector(elements.ITALIC).textContent).toBe('italics');
98+
expect(doc.childNodes[0].childNodes[1].childNodes[1].childNodes[0].querySelectorAll(elements.UNDERLINE).length).toBe(1);
99+
expect(doc.childNodes[0].childNodes[1].childNodes[1].childNodes[0].querySelector(elements.UNDERLINE).textContent).toBe('underline');
100+
});
40101
});

0 commit comments

Comments
 (0)