Merged in recent updates

This commit is contained in:
Knut Sveidqvist
2019-06-09 10:53:46 -07:00
6 changed files with 175 additions and 220 deletions

View File

@@ -8,6 +8,7 @@ let title = ''
let sections = []
let tasks = []
let currentSection = ''
const tags = ['active', 'done', 'crit', 'milestone']
export const clear = function () {
sections = []
@@ -199,26 +200,9 @@ const compileData = function (prevTask, dataStr) {
const task = {}
// Get tags like active, done cand crit
let matchFound = true
while (matchFound) {
matchFound = false
if (data[0].match(/^\s*active\s*$/)) {
task.active = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*done\s*$/)) {
task.done = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*crit\s*$/)) {
task.crit = true
data.shift(1)
matchFound = true
}
}
// Get tags like active, done, crit and milestone
getTaskTags(data, task, tags)
for (let i = 0; i < data.length; i++) {
data[i] = data[i].trim()
}
@@ -264,26 +248,9 @@ const parseData = function (prevTaskId, dataStr) {
const task = {}
// Get tags like active, done cand crit
let matchFound = true
while (matchFound) {
matchFound = false
if (data[0].match(/^\s*active\s*$/)) {
task.active = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*done\s*$/)) {
task.done = true
data.shift(1)
matchFound = true
}
if (data[0].match(/^\s*crit\s*$/)) {
task.crit = true
data.shift(1)
matchFound = true
}
}
// Get tags like active, done, crit and milestone
getTaskTags(data, task, tags)
for (let i = 0; i < data.length; i++) {
data[i] = data[i].trim()
}
@@ -332,6 +299,7 @@ export const addTask = function (descr, data) {
rawTask.active = taskInfo.active
rawTask.done = taskInfo.done
rawTask.crit = taskInfo.crit
rawTask.milestone = taskInfo.milestone
const pos = rawTasks.push(rawTask)
@@ -359,6 +327,7 @@ export const addTaskOrg = function (descr, data) {
newTask.active = taskInfo.active
newTask.done = taskInfo.done
newTask.crit = taskInfo.crit
newTask.milestone = taskInfo.milestone
lastTask = newTask
tasks.push(newTask)
}
@@ -415,3 +384,19 @@ export default {
addTaskOrg,
setExcludes
}
function getTaskTags (data, task, tags) {
let matchFound = true
while (matchFound) {
matchFound = false
tags.forEach(function (t) {
const pattern = '^\\s*' + t + '\\s*$'
const regex = new RegExp(pattern)
if (data[0].match(regex)) {
task[t] = true
data.shift(1)
matchFound = true
}
})
}
}

View File

@@ -7,151 +7,48 @@ describe('when using the ganttDb', function () {
ganttDb.clear()
})
it('should handle an fixed dates', function () {
it.each`
testName | section | taskName | taskData | expStartDate | expEndDate | expId | expTask
${'should handle fixed dates'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'id1'} | ${'test1'}
${'should handle duration (days) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 3)} | ${'id1'} | ${'test1'}
${'should handle duration (hours) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2h'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 2)} | ${'id1'} | ${'test1'}
${'should handle duration (minutes) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2m'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 2)} | ${'id1'} | ${'test1'}
${'should handle duration (seconds) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2s'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 1, 0, 0, 2)} | ${'id1'} | ${'test1'}
${'should handle duration (weeks) instead of fixed date to determine end date'} | ${'testa1'} | ${'test1'} | ${'id1,2013-01-01,2w'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 15)} | ${'id1'} | ${'test1'}
${'should handle fixed dates without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,2013-01-12'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 12)} | ${'task1'} | ${'test1'}
${'should handle duration instead of a fixed date to determine end date without id'} | ${'testa1'} | ${'test1'} | ${'2013-01-01,4d'} | ${new Date(2013, 0, 1)} | ${new Date(2013, 0, 5)} | ${'task1'} | ${'test1'}
`('$testName', ({ section, taskName, taskData, expStartDate, expEndDate, expId, expTask }) => {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2013-01-12')
ganttDb.addSection(section)
ganttDb.addTask(taskName, taskData)
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle duration (days) instead of fixed date to determine end date', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2d')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-03', 'YYYY-MM-DD').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle duration (hours) instead of fixed date to determine end date', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2h')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-01 2:00', 'YYYY-MM-DD hh:mm').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle duration (minutes) instead of fixed date to determine end date', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2m')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:02', 'YYYY-MM-DD hh:mm').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle duration (seconds) instead of fixed date to determine end date', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2s')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:00:02', 'YYYY-MM-DD hh:mm:ss').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle duration (weeks) instead of fixed date to determine end date', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
expect(tasks[0].id).toEqual('id1')
expect(tasks[0].task).toEqual('test1')
expect(tasks[0].startTime).toEqual(expStartDate)
expect(tasks[0].endTime).toEqual(expEndDate)
expect(tasks[0].id).toEqual(expId)
expect(tasks[0].task).toEqual(expTask)
})
it('should handle relative start date based on id', function () {
it.each`
section | taskName1 | taskName2 | taskData1 | taskData2 | expStartDate2 | expEndDate2 | expId2 | expTask2
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'id2'} | ${'test2'}
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'id2,after id3,1d'} | ${new Date((new Date()).setHours(0, 0, 0, 0))} | ${undefined} | ${'id2'} | ${'test2'}
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'after id1,1d'} | ${new Date(2013, 0, 15)} | ${undefined} | ${'task1'} | ${'test2'}
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2013-01-26'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 26)} | ${'task1'} | ${'test2'}
${'testa1'} | ${'test1'} | ${'test2'} | ${'id1,2013-01-01,2w'} | ${'2d'} | ${new Date(2013, 0, 15)} | ${new Date(2013, 0, 17)} | ${'task1'} | ${'test2'}
`('$testName', ({ section, taskName1, taskName2, taskData1, taskData2, expStartDate2, expEndDate2, expId2, expTask2 }) => {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
ganttDb.addTask('test2', 'id2,after id1,1d')
ganttDb.addSection(section)
ganttDb.addTask(taskName1, taskData1)
ganttDb.addTask(taskName2, taskData2)
const tasks = ganttDb.getTasks()
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
expect(tasks[1].id).toEqual('id2')
expect(tasks[1].task).toEqual('test2')
expect(tasks[1].startTime).toEqual(expStartDate2)
if (!expEndDate2 === undefined) {
expect(tasks[1].endTime).toEqual(expEndDate2)
}
expect(tasks[1].id).toEqual(expId2)
expect(tasks[1].task).toEqual(expTask2)
})
it('should handle relative start date based on id when id is invalid', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
ganttDb.addTask('test2', 'id2,after id3,1d')
const tasks = ganttDb.getTasks()
expect(tasks[1].startTime).toEqual(new Date((new Date()).setHours(0, 0, 0, 0)))
expect(tasks[1].id).toEqual('id2')
expect(tasks[1].task).toEqual('test2')
})
it('should handle fixed dates without id', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', '2013-01-01,2013-01-12')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
expect(tasks[0].id).toEqual('task1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle duration instead of a fixed date to determine end date without id', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', '2013-01-01,4d')
const tasks = ganttDb.getTasks()
expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
expect(tasks[0].endTime).toEqual(moment('2013-01-05', 'YYYY-MM-DD').toDate())
expect(tasks[0].id).toEqual('task1')
expect(tasks[0].task).toEqual('test1')
})
it('should handle relative start date of a fixed date to determine end date without id', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
ganttDb.addTask('test2', 'after id1,1d')
const tasks = ganttDb.getTasks()
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
expect(tasks[1].id).toEqual('task1')
expect(tasks[1].task).toEqual('test2')
})
it('should handle a new task with only an end date as definition', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
ganttDb.addTask('test2', '2013-01-26')
const tasks = ganttDb.getTasks()
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
expect(tasks[1].endTime).toEqual(moment('2013-01-26', 'YYYY-MM-DD').toDate())
expect(tasks[1].id).toEqual('task1')
expect(tasks[1].task).toEqual('test2')
})
it('should handle a new task with only an end date as definition', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
ganttDb.addTask('test1', 'id1,2013-01-01,2w')
ganttDb.addTask('test2', '2d')
const tasks = ganttDb.getTasks()
expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
expect(tasks[1].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
expect(tasks[1].id).toEqual('task1')
expect(tasks[1].task).toEqual('test2')
})
it('should handle relative start date based on id regardless of sections', function () {
ganttDb.setDateFormat('YYYY-MM-DD')
ganttDb.addSection('testa1')
@@ -162,15 +59,15 @@ describe('when using the ganttDb', function () {
const tasks = ganttDb.getTasks()
expect(tasks[1].startTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
expect(tasks[1].endTime).toEqual(moment('2013-01-18', 'YYYY-MM-DD').toDate())
expect(tasks[1].startTime).toEqual(new Date(2013, 0, 17))
expect(tasks[1].endTime).toEqual(new Date(2013, 0, 18))
expect(tasks[1].id).toEqual('id2')
expect(tasks[1].task).toEqual('test2')
expect(tasks[2].id).toEqual('id3')
expect(tasks[2].task).toEqual('test3')
expect(tasks[2].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
expect(tasks[2].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
expect(tasks[2].startTime).toEqual(new Date(2013, 0, 15))
expect(tasks[2].endTime).toEqual(new Date(2013, 0, 17))
})
it('should ignore weekends', function () {
ganttDb.setDateFormat('YYYY-MM-DD')

View File

@@ -98,6 +98,7 @@ export const draw = function (text, id) {
}
function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
// Draw background rects covering the entire width of the graph, these form the section rows.
svg.append('g')
.selectAll('rect')
.data(theArray)
@@ -120,6 +121,7 @@ export const draw = function (text, id) {
return 'section section0'
})
// Draw the rects representing the tasks
const rectangles = svg.append('g')
.selectAll('rect')
.data(theArray)
@@ -129,15 +131,24 @@ export const draw = function (text, id) {
.attr('rx', 3)
.attr('ry', 3)
.attr('x', function (d) {
if (d.milestone) {
return timeScale(d.startTime) + theSidePad + (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
}
return timeScale(d.startTime) + theSidePad
})
.attr('y', function (d, i) {
return i * theGap + theTopPad
})
.attr('width', function (d) {
if (d.milestone) {
return theBarHeight
}
return (timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime))
})
.attr('height', theBarHeight)
.attr('transform-origin', function (d, i) {
return (timeScale(d.startTime) + theSidePad + 0.5 * (timeScale(d.endTime) - timeScale(d.startTime))).toString() + 'px ' + (i * theGap + theTopPad + 0.5 * theBarHeight).toString() + 'px'
})
.attr('class', function (d) {
const res = 'task'
@@ -148,37 +159,48 @@ export const draw = function (text, id) {
}
}
let taskClass = ''
if (d.active) {
taskClass = ' active'
} else if (d.done) {
taskClass = ' done'
}
if (d.crit) {
return res + ' activeCrit' + secNum
if (taskClass.length > 0) {
taskClass += 'Crit'
} else {
return res + ' active' + secNum
taskClass = ' crit'
}
}
if (d.done) {
if (d.crit) {
return res + ' doneCrit' + secNum
} else {
return res + ' done' + secNum
}
if (taskClass.length === 0) {
taskClass = ' task'
}
if (d.crit) {
return res + ' crit' + secNum
if (d.milestone) {
taskClass = ' milestone' + taskClass
}
return res + ' task' + secNum
taskClass += secNum
return res + taskClass
})
// Append task labels
rectangles.append('text')
.text(function (d) {
return d.task
})
.attr('font-size', conf.fontSize)
.attr('x', function (d) {
const startX = timeScale(d.startTime)
const endX = timeScale(d.renderEndTime || d.endTime)
let startX = timeScale(d.startTime)
let endX = timeScale(d.renderEndTime || d.endTime)
if (d.milestone) {
startX += (0.5 * (timeScale(d.endTime) - timeScale(d.startTime))) - (0.5 * theBarHeight)
}
if (d.milestone) {
endX = startX + theBarHeight
}
const textWidth = this.getBBox().width
// Check id text width > width of rectangle
@@ -198,7 +220,10 @@ export const draw = function (text, id) {
.attr('text-height', theBarHeight)
.attr('class', function (d) {
const startX = timeScale(d.startTime)
const endX = timeScale(d.endTime)
let endX = timeScale(d.endTime)
if (d.milestone) {
endX = startX + theBarHeight
}
const textWidth = this.getBBox().width
let secNum = 0
for (let i = 0; i < categories.length; i++) {
@@ -228,6 +253,10 @@ export const draw = function (text, id) {
}
}
if (d.milestone) {
taskType += ' milestoneText'
}
// Check id text width > width of rectangle
if (textWidth > (endX - startX)) {
if (endX + textWidth + 1.5 * conf.leftPadding > w) {

View File

@@ -1,10 +1,12 @@
/* eslint-env jasmine */
import { parser } from './parser/gantt'
import ganttDb from './ganttDb'
/* eslint-disable no-eval */
import { parser } from './gantt'
import ganttDb from '../ganttDb'
describe('when parsing a gantt diagram it', function () {
beforeEach(function () {
parser.yy = ganttDb
parser.yy.clear()
})
it('should handle a dateFormat definition', function () {
@@ -44,11 +46,46 @@ describe('when parsing a gantt diagram it', function () {
*/
it('should handle a task definition', function () {
const str = 'gantt\n' +
'dateFormat yyyy-mm-dd\n' +
'dateFormat YYYY-MM-DD\n' +
'title Adding gantt diagram functionality to mermaid\n' +
'section Documentation\n' +
'Design jison grammar:des1, 2014-01-01, 2014-01-04'
parser.parse(str)
const tasks = parser.yy.getTasks()
expect(tasks[0].startTime).toEqual(new Date(2014, 0, 1))
expect(tasks[0].endTime).toEqual(new Date(2014, 0, 4))
expect(tasks[0].id).toEqual('des1')
expect(tasks[0].task).toEqual('Design jison grammar')
})
it.each`
tags | milestone | done | crit | active
${'milestone'} | ${true} | ${false} | ${false} | ${false}
${'done'} | ${false} | ${true} | ${false} | ${false}
${'crit'} | ${false} | ${false} | ${true} | ${false}
${'active'} | ${false} | ${false} | ${false} | ${true}
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
`('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
const str = 'gantt\n' +
'dateFormat YYYY-MM-DD\n' +
'title Adding gantt diagram functionality to mermaid\n' +
'section Documentation\n' +
'test task:' + tags + ', 2014-01-01, 2014-01-04'
const allowedTags = ['active', 'done', 'crit', 'milestone']
parser.parse(str)
const tasks = parser.yy.getTasks()
allowedTags.forEach(function (t) {
if (eval(t)) {
expect(tasks[0][t]).toBeTruthy()
} else {
expect(tasks[0][t]).toBeFalsy()
}
})
})
})

View File

@@ -188,6 +188,13 @@
shape-rendering: crispEdges;
}
.milestone {
transform: rotate(45deg) scale(0.8,0.8);
}
.milestoneText {
font-style: italic;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,