-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added TreeState, an immutable strucuture that represents the tree state
- Loading branch information
1 parent
a941029
commit 33933d0
Showing
4 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import {getFlattenedTreePaths} from '../selectors/getFlattenedTree'; | ||
import {getNodeFromPath} from '../selectors/nodes'; | ||
|
||
class State { | ||
flattenedTree = null; | ||
tree = null; | ||
|
||
constructor(tree, flattenedTree) { | ||
this.tree = tree; | ||
this.flattenedTree = flattenedTree || getFlattenedTreePaths(tree); | ||
} | ||
} | ||
|
||
export const validateState = state => { | ||
if (!(state instanceof State)) { | ||
throw new Error(`Expected a State instance but got ${typeof state}`); | ||
} | ||
}; | ||
|
||
/** | ||
* Immutable structure that represents the TreeState. | ||
*/ | ||
export default class TreeState { | ||
/** | ||
* Given a state, finds a node at a certain row index. | ||
* @param {State} state - The current state | ||
* @param {number} index - The visible row index | ||
* @return {State} An internal state representation | ||
*/ | ||
static getNodeAt = (state, index) => { | ||
validateState(state); | ||
|
||
const rowPath = state.flattenedTree[index]; | ||
|
||
if (!rowPath) { | ||
throw Error( | ||
`Tried to get node at row "${index}" but got nothing, the tree are ${state.flattenedTree.length} visible rows`, | ||
); | ||
} | ||
|
||
return getNodeFromPath(rowPath, state.tree); | ||
}; | ||
|
||
/** | ||
* Given a state, gets the tree | ||
* @param {State} state - The current state | ||
* @return {Node[]} The tree | ||
*/ | ||
static getTree = state => { | ||
validateState(state); | ||
|
||
return state.tree; | ||
}; | ||
|
||
/** | ||
* Creates an instance of state. | ||
* @param {Node[]} tree - The original tree | ||
* @return {State} An internal state representation | ||
*/ | ||
static createFromTree = tree => { | ||
if (!tree) { | ||
throw Error('A falsy tree was supplied in tree creation'); | ||
} | ||
|
||
if (!Array.isArray(tree)) { | ||
throw Error('An invalid tree was supplied in creation'); | ||
} | ||
|
||
return new State(tree); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import TreeState from '../TreeState'; | ||
import {Nodes} from '../../../testData/sampleTree'; | ||
|
||
describe('TreeState', () => { | ||
describe('createFromTree', () => { | ||
test('should fail when falsy value is supplied', () => { | ||
expect(() => TreeState.createFromTree()).toThrowError('A falsy tree was supplied in tree creation'); | ||
expect(() => TreeState.createFromTree('')).toThrowError('A falsy tree was supplied in tree creation'); | ||
expect(() => TreeState.createFromTree(0)).toThrowError('A falsy tree was supplied in tree creation'); | ||
expect(() => TreeState.createFromTree(false)).toThrowError('A falsy tree was supplied in tree creation'); | ||
}); | ||
|
||
test('should fail when an invalid value is supplied', () => { | ||
expect(() => TreeState.createFromTree({})).toThrowError('An invalid tree was supplied in creation'); | ||
expect(() => TreeState.createFromTree('tree')).toThrowError('An invalid tree was supplied in creation'); | ||
expect(() => TreeState.createFromTree(1234)).toThrowError('An invalid tree was supplied in creation'); | ||
expect(() => TreeState.createFromTree(true)).toThrowError('An invalid tree was supplied in creation'); | ||
}); | ||
|
||
test('should create state when a valid tree is supplied', () => { | ||
const {tree, flattenedTree} = TreeState.createFromTree(Nodes); | ||
|
||
expect(tree).toEqual(Nodes); | ||
expect(flattenedTree).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe('getNodeAt', () => { | ||
test('should get a for an existing rowId', () => { | ||
expect(TreeState.getNodeAt(TreeState.createFromTree(Nodes), 0)).toBe(Nodes[0]); | ||
expect(TreeState.getNodeAt(TreeState.createFromTree(Nodes), 1)).toMatchSnapshot('2nd row'); | ||
expect(TreeState.getNodeAt(TreeState.createFromTree(Nodes), 2)).toMatchSnapshot('3rd row'); | ||
expect(TreeState.getNodeAt(TreeState.createFromTree(Nodes), 6)).toMatchSnapshot('7th row'); | ||
}); | ||
|
||
test('should fail with a custom error when supplied rowId does not exist', () => { | ||
expect(() => TreeState.getNodeAt(TreeState.createFromTree(Nodes), 25)).toThrowErrorMatchingSnapshot(); | ||
}); | ||
|
||
test('should fail for when invalid state is supplied', () => { | ||
expect(() => TreeState.getNodeAt('state', 0)).toThrowError('Expected a State instance but got string'); | ||
expect(() => TreeState.getNodeAt(1225, 0)).toThrowError('Expected a State instance but got number'); | ||
expect(() => TreeState.getNodeAt([], 0)).toThrowError('Expected a State instance but got object'); | ||
expect(() => TreeState.getNodeAt({}, 0)).toThrowError('Expected a State instance but got object'); | ||
expect(() => TreeState.getNodeAt(true, 0)).toThrowError('Expected a State instance but got boolean'); | ||
expect(() => TreeState.getNodeAt(() => {}, 0)).toThrowError('Expected a State instance but got function'); | ||
}); | ||
}); | ||
|
||
describe('getTree', () => { | ||
test('should get a tree', () => { | ||
expect(TreeState.getTree(TreeState.createFromTree(Nodes))).toEqual(Nodes); | ||
}); | ||
|
||
test('should fail for when invalid state is supplied', () => { | ||
expect(() => TreeState.getTree('state')).toThrowError('Expected a State instance but got string'); | ||
expect(() => TreeState.getTree(1225)).toThrowError('Expected a State instance but got number'); | ||
expect(() => TreeState.getTree([])).toThrowError('Expected a State instance but got object'); | ||
expect(() => TreeState.getTree({})).toThrowError('Expected a State instance but got object'); | ||
expect(() => TreeState.getTree(true)).toThrowError('Expected a State instance but got boolean'); | ||
expect(() => TreeState.getTree(() => {})).toThrowError('Expected a State instance but got function'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`TreeState createFromTree should create state when a valid tree is supplied 1`] = ` | ||
Array [ | ||
Array [ | ||
0, | ||
], | ||
Array [ | ||
0, | ||
2, | ||
], | ||
Array [ | ||
0, | ||
2, | ||
3, | ||
], | ||
Array [ | ||
0, | ||
2, | ||
4, | ||
], | ||
Array [ | ||
0, | ||
5, | ||
], | ||
Array [ | ||
1, | ||
], | ||
Array [ | ||
"z", | ||
], | ||
] | ||
`; | ||
|
||
exports[`TreeState getNodeAt should fail with a custom error when supplied rowId does not exist 1`] = `"Tried to get node at row \\"25\\" but got nothing, the tree are 7 visible rows"`; | ||
|
||
exports[`TreeState getNodeAt should get a for an existing rowId: 2nd row 1`] = ` | ||
Object { | ||
"children": Array [ | ||
Object { | ||
"id": 3, | ||
"name": "Leaf 3", | ||
"state": Object { | ||
"deletable": true, | ||
"favorite": true, | ||
}, | ||
}, | ||
Object { | ||
"id": 4, | ||
"name": "Leaf 4", | ||
}, | ||
], | ||
"id": 2, | ||
"name": "Leaf 2", | ||
"state": Object { | ||
"deletable": true, | ||
"expanded": true, | ||
}, | ||
} | ||
`; | ||
|
||
exports[`TreeState getNodeAt should get a for an existing rowId: 3rd row 1`] = ` | ||
Object { | ||
"id": 3, | ||
"name": "Leaf 3", | ||
"state": Object { | ||
"deletable": true, | ||
"favorite": true, | ||
}, | ||
} | ||
`; | ||
|
||
exports[`TreeState getNodeAt should get a for an existing rowId: 7th row 1`] = ` | ||
Object { | ||
"id": "z", | ||
"name": "Leaf z", | ||
"state": Object { | ||
"deletable": true, | ||
"favorite": true, | ||
}, | ||
} | ||
`; |