diff --git a/src/diagrams/gantt/ganttDb.js b/src/diagrams/gantt/ganttDb.js index 557754378..77946deeb 100644 --- a/src/diagrams/gantt/ganttDb.js +++ b/src/diagrams/gantt/ganttDb.js @@ -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 + } + }) + } +} diff --git a/src/diagrams/gantt/ganttDb.spec.js b/src/diagrams/gantt/ganttDb.spec.js index bf2ed9c8d..12e644a0d 100644 --- a/src/diagrams/gantt/ganttDb.spec.js +++ b/src/diagrams/gantt/ganttDb.spec.js @@ -7,151 +7,48 @@ describe('when using the ganttDb', function () { ganttDb.clear() }) - it('should handle an fixed dates', function () { - ganttDb.setDateFormat('YYYY-MM-DD') - ganttDb.addSection('testa1') - ganttDb.addTask('test1', 'id1,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('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') - }) + 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(section) + ganttDb.addTask(taskName, taskData) + const tasks = ganttDb.getTasks() + 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 () { - ganttDb.setDateFormat('YYYY-MM-DD') - ganttDb.addSection('testa1') - ganttDb.addTask('test1', 'id1,2013-01-01,2w') - ganttDb.addTask('test2', 'id2,after id1,1d') + 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(section) + ganttDb.addTask(taskName1, taskData1) + ganttDb.addTask(taskName2, taskData2) + const tasks = ganttDb.getTasks() + 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) +}) - 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') - }) - - 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') diff --git a/src/diagrams/gantt/ganttRenderer.js b/src/diagrams/gantt/ganttRenderer.js index 633d4ae84..42396fe22 100644 --- a/src/diagrams/gantt/ganttRenderer.js +++ b/src/diagrams/gantt/ganttRenderer.js @@ -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,17 +131,26 @@ 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 ' + const res = 'task' let secNum = 0 for (let i = 0; i < categories.length; i++) { @@ -148,37 +159,48 @@ export const draw = function (text, id) { } } + let taskClass = '' if (d.active) { - if (d.crit) { - return res + ' activeCrit' + secNum - } else { - return res + ' active' + secNum - } + taskClass = ' active' + } else if (d.done) { + taskClass = ' done' } - - if (d.done) { - if (d.crit) { - return res + ' doneCrit' + secNum - } else { - return res + ' done' + secNum - } - } - if (d.crit) { - return res + ' crit' + secNum + if (taskClass.length > 0) { + taskClass += 'Crit' + } else { + taskClass = ' crit' + } } - return res + ' task' + secNum + if (taskClass.length === 0) { + taskClass = ' task' + } + + if (d.milestone) { + taskClass = ' milestone' + taskClass + } + + 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) { diff --git a/src/diagrams/gantt/parser/gantt.js b/src/diagrams/gantt/parser/gantt.js index cd3384a2d..ea3aa0e17 100644 --- a/src/diagrams/gantt/parser/gantt.js +++ b/src/diagrams/gantt/parser/gantt.js @@ -73,7 +73,7 @@ */ var parser = (function(){ var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15,16],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13],$V6=[1,14]; -var parser = {trace: function trace() { }, +var parser = {trace: function trace () { }, yy: {}, symbols_: {"error":2,"start":3,"gantt":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"dateFormat":11,"axisFormat":12,"excludes":13,"title":14,"section":15,"taskTxt":16,"taskData":17,"$accept":0,"$end":1}, terminals_: {2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",11:"dateFormat",12:"axisFormat",13:"excludes",14:"title",15:"section",16:"taskTxt",17:"taskData"}, @@ -120,7 +120,7 @@ break; }, table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:$V1,12:$V2,13:$V3,14:$V4,15:$V5,16:$V6},o($V0,[2,7],{1:[2,1]}),o($V0,[2,3]),{9:15,11:$V1,12:$V2,13:$V3,14:$V4,15:$V5,16:$V6},o($V0,[2,5]),o($V0,[2,6]),o($V0,[2,8]),o($V0,[2,9]),o($V0,[2,10]),o($V0,[2,11]),o($V0,[2,12]),{17:[1,16]},o($V0,[2,4]),o($V0,[2,13])], defaultActions: {}, -parseError: function parseError(str, hash) { +parseError: function parseError (str, hash) { if (hash.recoverable) { this.trace(str); } else { @@ -412,7 +412,7 @@ showPosition:function () { }, // test the lexed token: return FALSE when not a match, otherwise return token -test_match:function (match, indexed_rule) { +test_match:function(match, indexed_rule) { var token, lines, backup; @@ -542,7 +542,7 @@ next:function () { }, // return next match that has a token -lex:function lex() { +lex:function lex () { var r = this.next(); if (r) { return r; @@ -552,12 +552,12 @@ lex:function lex() { }, // activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) -begin:function begin(condition) { +begin:function begin (condition) { this.conditionStack.push(condition); }, // pop the previously active lexer condition state off the condition stack -popState:function popState() { +popState:function popState () { var n = this.conditionStack.length - 1; if (n > 0) { return this.conditionStack.pop(); @@ -567,7 +567,7 @@ popState:function popState() { }, // produce the lexer rule set which is active for the currently active lexer condition state -_currentRules:function _currentRules() { +_currentRules:function _currentRules () { if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; } else { @@ -576,7 +576,7 @@ _currentRules:function _currentRules() { }, // return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available -topState:function topState(n) { +topState:function topState (n) { n = this.conditionStack.length - 1 - Math.abs(n || 0); if (n >= 0) { return this.conditionStack[n]; @@ -586,7 +586,7 @@ topState:function topState(n) { }, // alias for begin(condition) -pushState:function pushState(condition) { +pushState:function pushState (condition) { this.begin(condition); }, @@ -652,7 +652,7 @@ if (typeof require !== 'undefined' && typeof exports !== 'undefined') { exports.parser = parser; exports.Parser = parser.Parser; exports.parse = function () { return parser.parse.apply(parser, arguments); }; -exports.main = function commonjsMain(args) { +exports.main = function commonjsMain (args) { if (!args[1]) { console.log('Usage: '+args[0]+' FILE'); process.exit(1); diff --git a/src/diagrams/gantt/gantt.spec.js b/src/diagrams/gantt/parser/gantt.spec.js similarity index 50% rename from src/diagrams/gantt/gantt.spec.js rename to src/diagrams/gantt/parser/gantt.spec.js index 54b74edc4..ad6295180 100644 --- a/src/diagrams/gantt/gantt.spec.js +++ b/src/diagrams/gantt/parser/gantt.spec.js @@ -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() + } }) }) +}) diff --git a/src/themes/gantt.scss b/src/themes/gantt.scss index 6793135ec..2d1182cc5 100644 --- a/src/themes/gantt.scss +++ b/src/themes/gantt.scss @@ -188,6 +188,13 @@ shape-rendering: crispEdges; } +.milestone { + transform: rotate(45deg) scale(0.8,0.8); +} + +.milestoneText { + font-style: italic; +} .doneCritText0, .doneCritText1, .doneCritText2,