Skip to content

Commit

Permalink
feat: 馃幐 added deleteNodeAt to TreeStateModifiers
Browse files Browse the repository at this point in the history
added deleteNodeAt, a function that generates a new state after deleting
a node at a supplied index
  • Loading branch information
diogofcunha committed Jan 27, 2019
1 parent d467c8f commit 623818f
Show file tree
Hide file tree
Showing 4 changed files with 695 additions and 5 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export interface TreeState {

export interface TreeStateModifiers {
editNodeAt: (state: State, index: number, setNode: (oldNode: Node) => Node) => State;
deleteNodeAt: (state: State, index: number) => State;
}

export const selectors: Selectors;
37 changes: 32 additions & 5 deletions src/state/TreeStateModifiers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {getFlattenedTreePaths, doesChangeAffectFlattenedTree, isNodeExpanded} from '../selectors/getFlattenedTree';
import TreeState, {validateState, State} from './TreeState';
import {replaceNodeFromTree} from '../selectors/nodes';
import {
getFlattenedTreePaths,
doesChangeAffectFlattenedTree,
isNodeExpanded,
nodeHasChildren,
} from '../selectors/getFlattenedTree';
import TreeState, {State} from './TreeState';
import {replaceNodeFromTree, deleteNodeFromTree} from '../selectors/nodes';

/**
* @callback setNode
Expand All @@ -20,8 +25,6 @@ export default class TreeStateModifiers {
* @return {State} An internal state representation
*/
static editNodeAt = (state, index, setNode) => {
validateState(state);

const node = TreeState.getNodeAt(state, index);
const updatedNode = setNode(node);
const flattenedTree = [...state.flattenedTree];
Expand All @@ -44,4 +47,28 @@ export default class TreeStateModifiers {

return new State(tree, flattenedTree);
};

/**
* Given a state, deletes a node
* @param {State} state - The current state
* @param {number} index - The visible row index
* @return {State} An internal state representation
*/
static deleteNodeAt = (state, index) => {
const node = TreeState.getNodeAt(state, index);

const flattenedTree = [...state.flattenedTree];
const flattenedNodeMap = flattenedTree[index];
const parents = flattenedNodeMap.slice(0, flattenedNodeMap.length - 1);

const numberOfVisibleDescendants = nodeHasChildren(node)
? TreeState.getNumberOfVisibleDescendants(state, index)
: 0;

flattenedTree.splice(index, 1 + numberOfVisibleDescendants);

const tree = deleteNodeFromTree(state.tree, {...node, parents});

return new State(tree, flattenedTree);
};
}
111 changes: 111 additions & 0 deletions src/state/__tests__/TreeStateModifiers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,115 @@ describe('TreeStateModifiers', () => {
});
});
});

describe('deleteNodeAt', () => {
test('should fail when invalid state is supplied', () => {
expect(() => TreeStateModifiers.deleteNodeAt('state', 0)).toThrowError(
'Expected a State instance but got string',
);
expect(() => TreeStateModifiers.deleteNodeAt(1225, 0)).toThrowError('Expected a State instance but got number');
expect(() => TreeStateModifiers.deleteNodeAt([], 0)).toThrowError('Expected a State instance but got object');
expect(() => TreeStateModifiers.deleteNodeAt({}, 0)).toThrowError('Expected a State instance but got object');
expect(() => TreeStateModifiers.deleteNodeAt(true, 0)).toThrowError('Expected a State instance but got boolean');
expect(() => TreeStateModifiers.deleteNodeAt(() => {}, 0)).toThrowError(
'Expected a State instance but got function',
);
});

test('should fail with descriptive error when node at index does not exist', () => {
expect(() => TreeStateModifiers.deleteNodeAt(TreeState.createFromTree(Nodes), 20)).toThrowErrorMatchingSnapshot();
});

describe('flattened tree', () => {
test('should delete a root node with expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {flattenedTree} = TreeStateModifiers.deleteNodeAt(state, 0);

expect(flattenedTree).toMatchSnapshot();
});

test('should delete a root node without expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {flattenedTree} = TreeStateModifiers.deleteNodeAt(state, 5);

expect(flattenedTree).toMatchSnapshot();
});

test('should delete a child node with expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {flattenedTree} = TreeStateModifiers.deleteNodeAt(state, 1);

expect(flattenedTree).toMatchSnapshot();
});

test('should delete a child node without expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {flattenedTree} = TreeStateModifiers.deleteNodeAt(state, 2);

expect(flattenedTree).toMatchSnapshot();
});
});

describe('tree', () => {
test('should delete a root node with expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {tree} = TreeStateModifiers.deleteNodeAt(state, 0);

const changes = diff(state.tree, tree);

expect(changes).toMatchSnapshot();
});

test('should delete a root node without expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {tree} = TreeStateModifiers.deleteNodeAt(state, 6);

const changes = diff(state.tree, tree);

expect(changes).toMatchSnapshot();
});

test('should delete a child node without expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {tree} = TreeStateModifiers.deleteNodeAt(state, 2);

const changes = diff(state.tree, tree);

expect(changes).toMatchSnapshot();
});

test('should delete a child node with expanded children', () => {
const state = TreeState.createFromTree(Nodes);

deepFreeze(state);

const {tree} = TreeStateModifiers.deleteNodeAt(state, 1);

const changes = diff(state.tree, tree);

expect(changes).toMatchSnapshot();
});
});
});
});

0 comments on commit 623818f

Please sign in to comment.