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

Commit

Permalink
Implement onTab in NestedRichTextEditorUtil
Browse files Browse the repository at this point in the history
Summary:
Implement `onTab` for `NestedRichTextEditorUtil`
* Cases 1-5 in the image below
* Decided to postpone case 6 since its not essential for the implementation
* Note that case 1 & 3 are logically equivalent, and the `createNewParent` method implemented in D9624285 handles both.

{F137497472}

Reviewed By: mitermayer

Differential Revision: D9631917

fbshipit-source-id: 9f4298488d8c2a8f172cd82423c30c16d773a8ab
  • Loading branch information
niveditc authored and facebook-github-bot committed Sep 4, 2018
1 parent 6f73657 commit 8d3cfba
Show file tree
Hide file tree
Showing 3 changed files with 1,687 additions and 2 deletions.
46 changes: 45 additions & 1 deletion src/model/modifier/exploration/NestedRichTextEditorUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type SelectionState from 'SelectionState';
import type URI from 'URI';

const DraftModifier = require('DraftModifier');
const DraftTreeOperations = require('DraftTreeOperations');
const EditorState = require('EditorState');
const RichTextEditorUtil = require('RichTextEditorUtil');

Expand Down Expand Up @@ -291,7 +292,7 @@ const NestedRichTextEditorUtil: RichTextUtils = {
return editorState;
}

const content = editorState.getCurrentContent();
let content = editorState.getCurrentContent();
const block = content.getBlockForKey(key);
const type = block.getType();
if (type !== 'unordered-list-item' && type !== 'ordered-list-item') {
Expand All @@ -305,6 +306,49 @@ const NestedRichTextEditorUtil: RichTextUtils = {
return editorState;
}

// implement nested tree behaviour for onTab
if (!event.shiftKey) {
let blockMap = editorState.getCurrentContent().getBlockMap();
const prevSiblingKey = block.getPrevSiblingKey();
const nextSiblingKey = block.getNextSiblingKey();
// if there is no previous sibling, we do nothing
if (prevSiblingKey == null) {
return editorState;
}
// if previous sibling is a non-leaf move node as child of previous sibling
const prevSibling = blockMap.get(prevSiblingKey);
const nextSibling =
nextSiblingKey != null ? blockMap.get(nextSiblingKey) : null;
if (
prevSibling != null &&
prevSibling.getText() === '' &&
prevSibling.getChildKeys().count() > 0
) {
blockMap = DraftTreeOperations.updateAsSiblingsChild(
blockMap,
key,
'previous',
);
// else, if next sibling is a non-leaf move node as child of next sibling
} else if (
nextSibling != null &&
nextSibling.getText() === '' &&
nextSibling.getChildKeys().count() > 0
) {
blockMap = DraftTreeOperations.updateAsSiblingsChild(
blockMap,
key,
'next',
);
// if none of the siblings are non-leaf, we need to create a new parent
} else {
blockMap = DraftTreeOperations.createNewParent(blockMap, key);
}
content = editorState.getCurrentContent().merge({
blockMap: blockMap,
});
}

const withAdjustment = adjustBlockDepthForContentState(
content,
selection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Immutable = require('immutable');
const {List} = Immutable;

const {editorState, contentState, selectionState} = getSampleStateForTesting();
const {onBackspace, onDelete} = NestedRichTextEditorUtil;
const {onBackspace, onDelete, onTab} = NestedRichTextEditorUtil;

const contentBlockNodes = [
new ContentBlockNode({
Expand Down Expand Up @@ -331,6 +331,141 @@ test('onDelete is a no-op the end of a leaf', () => {
});
});

/**
* Tests for onTab
*/
const contentBlockNodes2 = [
new ContentBlockNode({
key: 'A',
nextSibling: 'B',
text: 'Item 1',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'B',
prevSibling: 'A',
nextSibling: 'C',
text: 'Item 2',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'C',
prevSibling: 'B',
nextSibling: 'J',
text: '',
type: 'ordered-list-item',
children: List(['D', 'E', 'F', 'I']),
}),
new ContentBlockNode({
key: 'D',
parent: 'C',
prevSibling: null,
nextSibling: 'E',
text: 'Item 2a',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'E',
parent: 'C',
prevSibling: 'D',
nextSibling: 'F',
text: 'Item 2b',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'F',
parent: 'C',
prevSibling: 'E',
nextSibling: 'I',
text: '',
type: 'ordered-list-item',
children: List(['G', 'H']),
}),
new ContentBlockNode({
key: 'G',
parent: 'F',
prevSibling: null,
nextSibling: 'H',
text: 'Item 2b i',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'H',
parent: 'F',
prevSibling: 'G',
nextSibling: null,
text: 'Item 2b ii',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'I',
parent: 'C',
prevSibling: 'F',
nextSibling: null,
text: 'Item 2c',
type: 'ordered-list-item',
children: List([]),
}),
new ContentBlockNode({
key: 'J',
prevSibling: 'C',
nextSibling: null,
text: 'Item 3',
type: 'ordered-list-item',
children: List([]),
}),
];

test('onTab with leaf as previous block & non-leaf as next block merges to the next block', () => {
assertNestedUtilOperation(
editorState => onTab({preventDefault: () => {}}, editorState, 2),
{
anchorKey: 'E',
focusKey: 'E',
},
contentBlockNodes2,
);
});

test('onTab with non-leaf as previous block merges to the previous block', () => {
assertNestedUtilOperation(
editorState => onTab({preventDefault: () => {}}, editorState, 2),
{
anchorKey: 'I',
focusKey: 'I',
},
contentBlockNodes2,
);
});

test('onTab with no previous block does nothing', () => {
assertNestedUtilOperation(
editorState => onTab({preventDefault: () => {}}, editorState, 1),
{
anchorKey: 'A',
focusKey: 'A',
},
contentBlockNodes2,
);
});

test('onTab when siblings are at the same depth creates a new parent', () => {
assertNestedUtilOperation(
editorState => onTab({preventDefault: () => {}}, editorState, 1),
{
anchorKey: 'H',
focusKey: 'H',
},
contentBlockNodes2,
);
});

// TODO (T32099101)
test('onSplitParent must split a nested block retaining parent', () => {
expect(true).toBe(true);
Expand Down
Loading

0 comments on commit 8d3cfba

Please sign in to comment.