diff --git a/src/utils/__tests__/constructTree.js b/src/utils/__tests__/constructTree.js index b589ee6..e8a999f 100644 --- a/src/utils/__tests__/constructTree.js +++ b/src/utils/__tests__/constructTree.js @@ -42,7 +42,7 @@ describe('constructTree', () => { }, ] const nodes = { - 0: {type: 'normal', level: 0, children: [1, 2, 3], depth: 3}, + 0: {type: 'normal', level: 0, children: [1, 2], depth: 3}, 1: {type: 'normal', level: 1, children: [], depth: 3}, 2: {type: 'normal', level: 1, children: [3], depth: 3}, 3: {type: 'normal', level: 2, children: [], depth: 3}, @@ -79,6 +79,35 @@ describe('constructTree', () => { 2: {type: 'equal', level: 2, children: [], depth: 3}, }) }) + test('with the same events, children of another one', () => { + // Naively, if B == C, and both B and C are children of A, A will pick them as children, + // circumventing the type "equal" process, so we test against that + const events = [ + { + id: 0, + allDay: false, + start: new Date(2017, 9, 7, 12, 0, 0), + end: new Date(2017, 9, 7, 14, 30, 0), + }, + { + id: 1, + allDay: false, + start: new Date(2017, 9, 7, 13, 0, 0), + end: new Date(2017, 9, 7, 14, 30, 0), + }, + { + id: 3, + allDay: false, + start: new Date(2017, 9, 7, 13, 0, 0), + end: new Date(2017, 9, 7, 14, 30, 0), + }, + ] + expect(constructTree(events)).toEqual({ + 0: {type: 'normal', level: 0, children: [1], depth: 3}, + 1: {type: 'equal', level: 1, children: [2], depth: 3}, + 2: {type: 'equal', level: 2, children: [], depth: 3}, + }) + }) test('subsequent children, 2 child of 1, 3 child of 2 but not of 1', () => { const events = [ { diff --git a/src/utils/__tests__/parseData.js b/src/utils/__tests__/parseData.js index 064c7f4..e09fb87 100644 --- a/src/utils/__tests__/parseData.js +++ b/src/utils/__tests__/parseData.js @@ -71,7 +71,7 @@ describe('parseData', () => { }, ] const nodes = { - 0: {type: 'normal', level: 0, children: [1, 2, 3], depth: 3}, + 0: {type: 'normal', level: 0, children: [1, 2], depth: 3}, 1: {type: 'normal', level: 1, children: [], depth: 3}, 2: {type: 'normal', level: 1, children: [3], depth: 3}, 3: {type: 'normal', level: 2, children: [], depth: 3}, diff --git a/src/utils/constructTree.js b/src/utils/constructTree.js index 0578df0..30d5daf 100644 --- a/src/utils/constructTree.js +++ b/src/utils/constructTree.js @@ -6,50 +6,52 @@ import isEqual from 'date-fns/fp/isEqual' import {isSameEvent} from './isSameEvent' import type {Event, Nodes, Node} from '../types' -// Creates the tree used for the daily "stairs" layout algorithm, i.e. specify which nodes are child -// of which node -// It also assigns for each node it's level (position on each branch) and depth (length of the -// branch) +/* + Creates the tree used for the daily "stairs" layout algorithm, i.e. specify which nodes are child + of which node + It also assigns for each node it's level (position on each branch) and depth (length of the + branch) +*/ // Invariant: the data is always sorted first by start, then by duration export function constructTree(data: $ReadOnlyArray): Nodes { // Tag nodes picked in the tree so that they are only used once const pickedNodes = {} - let nodes = {} + const nodes = {} // Finds a node direct children. If more than one event is identical to the node, picks only the // first one (if a == b == c, it'll be c child of b, and b child of a) - const findChildren = id => { + const findChildren = (id: number, level: number) => { + const createChildrenNodes = n => { + if (typeof nodes[n] === 'undefined') { + nodes[n] = makeNode(level + 1, n) + pickedNodes[n] = true + } + } + const children = [] const currentNode = data[id] let alreadySameEvent = false for (let i = 0; i < data.length; i++) { const targetNode = data[i] if ( + pickedNodes[i] !== true && !alreadySameEvent && areIntervalsOverlapping(currentNode, targetNode) && // no intersection, no children (isAfter(currentNode.start)(targetNode.start) || - (isEqual(currentNode.start)(targetNode.start) && - currentNode.id !== targetNode.id && - pickedNodes[i] !== true)) + (isEqual(currentNode.start)(targetNode.start) && currentNode.id !== targetNode.id)) ) { if (isSameEvent(currentNode, targetNode)) alreadySameEvent = true pickedNodes[i] = true children.push(i) + createChildrenNodes(i) } } return children } const makeNode = (level, id): Node => { - const children = findChildren(id) - children.map(n => { - if (typeof nodes[n] === 'undefined') { - nodes[n] = makeNode(level + 1, n) - } else { - if (nodes[n].level < level + 1) nodes[n].level = level + 1 - } - }) + const children = findChildren(id, level) const currentNode = data[id] // Since the events are sorted (and children too) we can only check the next event const nextNode = children.length > 0 ? data[children[0]] : null