Skip to content

Commit

Permalink
Add Feature Flags in Draft, and add Entity Deletion change under GK
Browse files Browse the repository at this point in the history
We use feature flags in the FB version of Draft to test new and
potentially unstable changes. This mirrors out the flags we currently
have and sets the stage for keeping things in sync going forward.

Now internal use of feature flags will not block syncing and releasing
Draft.

This is part of an effort to make releases simpler and more frequent,
and keep the internal and external versions of Draft in sync.

As part of matching up the internal and external Draft flags we added
the changes from PR facebookarchive#1065 by @mmmoussa, with the main change under a
flag that is set to false.

---Test Plan
Manually tested the examples in Chrome, and we will do the full test
plan before releasing v0.10.1.
  • Loading branch information
flarnie committed Mar 29, 2017
1 parent eac6e2e commit a9c3bef
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 24 deletions.
16 changes: 13 additions & 3 deletions src/component/handlers/edit/editOnInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

'use strict';

const DraftFeatureFlags = require('DraftFeatureFlags');
var DraftModifier = require('DraftModifier');
var DraftOffsetKey = require('DraftOffsetKey');
var EditorState = require('EditorState');
Expand Down Expand Up @@ -47,11 +48,20 @@ function editOnInput(editor: DraftEditor): void {
var domSelection = global.getSelection();

var {anchorNode, isCollapsed} = domSelection;

const isNotTextNode =
anchorNode.nodeType !== Node.TEXT_NODE;
const isNotTextOrElementNode = anchorNode.nodeType !== Node.TEXT_NODE
&& anchorNode.nodeType !== Node.ELEMENT_NODE;
if (isNotTextOrElementNode) {
return;

if (DraftFeatureFlags.draft_killswitch_allow_nontextnodes) {
if (isNotTextNode) {
return;
}
} else {
if (isNotTextOrElementNode) {
// TODO: (t16149272) figure out context for this change
return;
}
}

var domText = anchorNode.textContent;
Expand Down
20 changes: 20 additions & 0 deletions src/component/utils/DraftFeatureFlags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule DraftFeatureFlags
* @flow
*/

'use strict';

var DraftFeatureFlags = {
draft_killswitch_allow_nontextnodes: false,
draft_segmented_entities_behavior: false,
};

module.exports = DraftFeatureFlags;
53 changes: 39 additions & 14 deletions src/model/modifier/DraftModifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

var CharacterMetadata = require('CharacterMetadata');
var ContentStateInlineStyle = require('ContentStateInlineStyle');
const DraftFeatureFlags = require('DraftFeatureFlags');
var Immutable = require('immutable');

var applyEntityToContentState = require('applyEntityToContentState');
Expand Down Expand Up @@ -138,29 +139,53 @@ var DraftModifier = {
rangeToRemove: SelectionState,
removalDirection: DraftRemovalDirection
): ContentState {
let startKey, endKey, startBlock, endBlock;
startKey = removalDirection === 'forward'
? rangeToRemove.getAnchorKey()
: rangeToRemove.getFocusKey();
endKey = removalDirection === 'forward'
? rangeToRemove.getFocusKey()
: rangeToRemove.getAnchorKey();
startBlock = contentState.getBlockForKey(startKey);
endBlock = contentState.getBlockForKey(endKey);
const startOffset = rangeToRemove.getStartOffset();
const endOffset = rangeToRemove.getEndOffset();

const startEntityKey = startBlock.getEntityAt(startOffset);
const endEntityKey = endBlock.getEntityAt(endOffset - 1);

// Check whether the selection state overlaps with a single entity.
// If so, try to remove the appropriate substring of the entity text.
if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) {
var key = rangeToRemove.getAnchorKey();
var startOffset = rangeToRemove.getStartOffset();
var endOffset = rangeToRemove.getEndOffset();
var block = contentState.getBlockForKey(key);

var startEntity = block.getEntityAt(startOffset);
var endEntity = block.getEntityAt(endOffset - 1);
if (startEntity && startEntity === endEntity) {
var adjustedRemovalRange = getCharacterRemovalRange(
if (startKey === endKey) {
if (startEntityKey && startEntityKey === endEntityKey) {
const adjustedRemovalRange = getCharacterRemovalRange(
contentState.getEntityMap(),
block,
startBlock,
endBlock,
rangeToRemove,
removalDirection
removalDirection,
);
return removeRangeFromContentState(contentState, adjustedRemovalRange);
}
}
let adjustedRemovalRange = rangeToRemove;
if (DraftFeatureFlags.draft_segmented_entities_behavior) {
// Adjust the selection to properly delete segemented and immutable
// entities
adjustedRemovalRange = getCharacterRemovalRange(
contentState.getEntityMap(),
startBlock,
endBlock,
rangeToRemove,
removalDirection,
);
}

var withoutEntities = removeEntitiesAtEdges(contentState, rangeToRemove);
return removeRangeFromContentState(withoutEntities, rangeToRemove);
var withoutEntities = removeEntitiesAtEdges(
contentState,
adjustedRemovalRange,
);
return removeRangeFromContentState(withoutEntities, adjustedRemovalRange);
},

splitBlock: function(
Expand Down
90 changes: 83 additions & 7 deletions src/model/modifier/getCharacterRemovalRange.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,93 @@ import type {EntityMap} from 'EntityMap';
*/
function getCharacterRemovalRange(
entityMap: EntityMap,
block: ContentBlock,
startBlock: ContentBlock,
endBlock: ContentBlock,
selectionState: SelectionState,
direction: DraftRemovalDirection
direction: DraftRemovalDirection,
): SelectionState {
var start = selectionState.getStartOffset();
var end = selectionState.getEndOffset();
var entityKey = block.getEntityAt(start);
if (!entityKey) {
var startEntityKey = startBlock.getEntityAt(start);
var endEntityKey = endBlock.getEntityAt(end - 1);
if (!startEntityKey && !endEntityKey) {
return selectionState;
}
var newSelectionState = selectionState;
if (selectionState.getIsBackward()) {
newSelectionState = selectionState.merge({
anchorKey: selectionState.getFocusKey(),
anchorOffset: selectionState.getFocusOffset(),
focusKey: selectionState.getAnchorKey(),
focusOffset: selectionState.getAnchorOffset(),
isBackward: false,
});
}
if (startEntityKey && (startEntityKey === endEntityKey)) {
newSelectionState = getEntityRemovalRange(
entityMap,
startBlock,
newSelectionState,
direction,
startEntityKey,
);
} else if (startEntityKey && endEntityKey) {
const startSelectionState = getEntityRemovalRange(
entityMap,
startBlock,
newSelectionState,
direction,
startEntityKey,
);
const endSelectionState = getEntityRemovalRange(
entityMap,
endBlock,
newSelectionState,
direction,
endEntityKey,
);
newSelectionState = newSelectionState.merge({
anchorOffset: startSelectionState.getAnchorOffset(),
focusOffset: endSelectionState.getFocusOffset(),
isBackward: false,
});
} else if (startEntityKey) {
const startSelectionState = getEntityRemovalRange(
entityMap,
startBlock,
newSelectionState,
direction,
startEntityKey,
);
newSelectionState = newSelectionState.merge({
anchorOffset: startSelectionState.getStartOffset(),
isBackward: false,
});
} else if (endEntityKey) {
const endSelectionState = getEntityRemovalRange(
entityMap,
endBlock,
newSelectionState,
direction,
endEntityKey,
);
newSelectionState = newSelectionState.merge({
focusOffset: endSelectionState.getEndOffset(),
isBackward: false,
});
}
return newSelectionState;
}

function getEntityRemovalRange(
entityMap: EntityMap,
block: ContentBlock,
selectionState: SelectionState,
direction: DraftRemovalDirection,
entityKey: string,
): SelectionState {
var start = selectionState.getStartOffset();
var end = selectionState.getEndOffset();
var entity = entityMap.__get(entityKey);
var mutability = entity.getMutability();

Expand All @@ -56,12 +132,12 @@ function getCharacterRemovalRange(

// Find the entity range that overlaps with our removal range.
var entityRanges = getRangesForDraftEntity(block, entityKey).filter(
(range) => start < range.end && end > range.start
(range) => start < range.end && end > range.start,
);

invariant(
entityRanges.length == 1,
'There should only be one entity range within this removal range.'
'There should only be one entity range within this removal range.',
);

var entityRange = entityRanges[0];
Expand All @@ -82,7 +158,7 @@ function getCharacterRemovalRange(
end,
block.getText().slice(entityRange.start, entityRange.end),
entityRange.start,
direction
direction,
);

return selectionState.merge({
Expand Down

0 comments on commit a9c3bef

Please sign in to comment.