diff --git a/demos/xychart.html b/demos/xychart.html index 4017cc86f..bfa7f12b1 100644 --- a/demos/xychart.html +++ b/demos/xychart.html @@ -65,18 +65,18 @@
       xychart-beta
       title "Basic xychart with multiple datasets"
-      x-axis "Relevant categories" [category1, "category 2", category3, category4, category5, category6, category7]
-      y-axis Animals 10 --> 200
-      bar [["dogs" [52, 96, 35, 10, 87, 34, 67, 99]],["cats" [15, 7, 23, 55, 11, 41, 26, 3]]]
+      x-axis "Relevant categories" [category1, "category 2", category3, category4]
+      y-axis Animals 0 --> 160
+      bar [["dogs" [40, 20, 40, 30]],["cats" [20, 40, 50, 30]],["birds" [30, 60, 50, 30]]]
     

XY Charts bar horizontal with multiple datasets

       xychart-beta horizontal
       title "Basic xychart with multiple datasets"
-      x-axis "Relevant categories" [category1, "category 2", category3, category4, category5, category6, category7]
-      y-axis Animals 10 --> 200
-      bar [["dogs" [52, 96, 35, 10, 87, 34, 67, 99]],["cats" [15, 7, 23, 55, 11, 41, 26, 3]]]
+      x-axis "Relevant categories" [category1, "category 2", category3, category4]
+      y-axis Animals 0 --> 160
+      bar [["dogs" [40, 20, 40, 30]],["cats" [20, 40, 50, 30]],["birds" [30, 60, 50, 30]]]
     

XY Charts line single dataset

diff --git a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts index 753d1d345..93f5eb585 100644 --- a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts +++ b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts @@ -32,7 +32,10 @@ export class BarPlot { type: 'rect', data: finalData.map((data, index) => { const x = offset[index] + this.boundingRect.x; - const width = data[1] - this.boundingRect.x; + const width = + data[1] - + this.boundingRect.x - + (dataIndex > 0 ? this.yAxis.getAxisOuterPadding() : 0); offset[index] += width; return { x, @@ -50,8 +53,10 @@ export class BarPlot { groupTexts: ['plot', `bar-plot-${this.plotIndex}-${dataIndex}`], type: 'rect', data: finalData.map((data, index) => { - const y = data[1] - offset[index]; - const height = this.boundingRect.y + this.boundingRect.height - data[1]; + const adjustForAxisOuterPadding = dataIndex > 0 ? this.yAxis.getAxisOuterPadding() : 0; + const y = data[1] - offset[index] + adjustForAxisOuterPadding; + const height = + this.boundingRect.y + this.boundingRect.height - data[1] - adjustForAxisOuterPadding; offset[index] += height; return { x: data[0] - barWidthHalf, diff --git a/packages/mermaid/src/diagrams/xychart/parser/xychart.jison b/packages/mermaid/src/diagrams/xychart/parser/xychart.jison index d44dbd170..a52c2ebe0 100644 --- a/packages/mermaid/src/diagrams/xychart/parser/xychart.jison +++ b/packages/mermaid/src/diagrams/xychart/parser/xychart.jison @@ -109,14 +109,14 @@ statement ; datasets - : SQUARE_BRACES_START datasetBraced COMMA datasets SQUARE_BRACES_END { $$ = [$datasetBraced, ...$datasets] } - | SQUARE_BRACES_START datasetBraced SQUARE_BRACES_END { $$ = [$datasetBraced] } + : SQUARE_BRACES_START datasetBraced SQUARE_BRACES_END { $$ = [$datasetBraced] } | datasetBraced { $$ = [$datasetBraced] } | dataset { $$ = [$dataset] } ; datasetBraced - : SQUARE_BRACES_START dataset SQUARE_BRACES_END { $$ = $dataset } + : SQUARE_BRACES_START dataset SQUARE_BRACES_END COMMA datasetBraced { $$ = [$dataset, ...$datasetBraced] } + | SQUARE_BRACES_START dataset SQUARE_BRACES_END { $$ = [$dataset] } ; dataset diff --git a/packages/mermaid/src/diagrams/xychart/parser/xychart.jison.spec.ts b/packages/mermaid/src/diagrams/xychart/parser/xychart.jison.spec.ts index d79e20f87..cca21720e 100644 --- a/packages/mermaid/src/diagrams/xychart/parser/xychart.jison.spec.ts +++ b/packages/mermaid/src/diagrams/xychart/parser/xychart.jison.spec.ts @@ -33,380 +33,384 @@ describe('Testing xychart jison file', () => { clearMocks(); }); - it('should throw error if xychart-beta text is not there', () => { - const str = 'xychart-beta-1'; - expect(parserFnConstructor(str)).toThrow(); - }); - - it('should not throw error if only xychart is there', () => { - const str = 'xychart-beta'; - expect(parserFnConstructor(str)).not.toThrow(); - }); - - it('parse title of the chart within "', () => { - const str = 'xychart-beta \n title "This is a title"'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('This is a title'); - }); - it('parse title of the chart without "', () => { - const str = 'xychart-beta \n title oneLinertitle'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('oneLinertitle'); - }); - - it('parse chart orientation', () => { - const str = 'xychart-beta vertical'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setOrientation).toHaveBeenCalledWith('vertical'); - }); - - it('parse chart orientation with spaces', () => { - let str = 'xychart-beta horizontal '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setOrientation).toHaveBeenCalledWith('horizontal'); - - str = 'xychart-beta abc'; - expect(parserFnConstructor(str)).toThrow(); - }); - - it('parse x-axis', () => { - const str = 'xychart-beta \nx-axis xAxisName\n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: 'xAxisName', - type: 'text', + describe('single dataset', () => { + it('should throw error if xychart-beta text is not there', () => { + const str = 'xychart-beta-1'; + expect(parserFnConstructor(str)).toThrow(); }); - }); - it('parse x-axis with axis name without "', () => { - const str = 'xychart-beta \nx-axis xAxisName \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: 'xAxisName', - type: 'text', + it('should not throw error if only xychart is there', () => { + const str = 'xychart-beta'; + expect(parserFnConstructor(str)).not.toThrow(); }); - }); - it('parse x-axis with axis name with "', () => { - const str = 'xychart-beta \n x-axis "xAxisName has space"\n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: 'xAxisName has space', - type: 'text', + it('parse title of the chart within "', () => { + const str = 'xychart-beta \n title "This is a title"'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('This is a title'); + }); + it('parse title of the chart without "', () => { + const str = 'xychart-beta \n title oneLinertitle'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('oneLinertitle'); }); - }); - it('parse x-axis with axis name with " with spaces', () => { - const str = 'xychart-beta \n x-axis " xAxisName has space " \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: ' xAxisName has space ', - type: 'text', + it('parse chart orientation', () => { + const str = 'xychart-beta vertical'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setOrientation).toHaveBeenCalledWith('vertical'); }); - }); - it('parse x-axis with axis name and range data', () => { - const str = 'xychart-beta \nx-axis xAxisName 45.5 --> 33 \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: 'xAxisName', - type: 'text', - }); - expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 33); - }); - it('parse x-axis throw error for invalid range data', () => { - const str = 'xychart-beta \nx-axis xAxisName aaa --> 33 \n'; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse x-axis with axis name and range data with only decimal part', () => { - const str = 'xychart-beta \nx-axis xAxisName 45.5 --> .34 \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: 'xAxisName', - type: 'text', - }); - expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 0.34); - }); + it('parse chart orientation with spaces', () => { + let str = 'xychart-beta horizontal '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setOrientation).toHaveBeenCalledWith('horizontal'); - it('parse x-axis without axisname and range data', () => { - const str = 'xychart-beta \nx-axis 45.5 --> 1.34 \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: '', - type: 'text', + str = 'xychart-beta abc'; + expect(parserFnConstructor(str)).toThrow(); }); - expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 1.34); - }); - it('parse x-axis with axis name and category data', () => { - const str = 'xychart-beta \nx-axis xAxisName [ "cat1" , cat2a ] \n '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: 'xAxisName', - type: 'text', - }); - expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ - { - text: 'cat1', + it('parse x-axis', () => { + const str = 'xychart-beta \nx-axis xAxisName\n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: 'xAxisName', type: 'text', - }, - { text: 'cat2a', type: 'text' }, - ]); - }); - - it('parse x-axis without axisname and category data', () => { - const str = 'xychart-beta \nx-axis [ "cat1" , cat2a ] \n '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ - text: '', - type: 'text', + }); }); - expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ - { - text: 'cat1', + + it('parse x-axis with axis name without "', () => { + const str = 'xychart-beta \nx-axis xAxisName \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: 'xAxisName', type: 'text', - }, - { text: 'cat2a', type: 'text' }, - ]); - }); - - it('parse x-axis throw error if unbalanced bracket', () => { - let str = 'xychart-beta \nx-axis xAxisName [ "cat1" [ cat2a ] \n '; - expect(parserFnConstructor(str)).toThrow(); - str = 'xychart-beta \nx-axis xAxisName [ "cat1" , cat2a ] ] \n '; - expect(parserFnConstructor(str)).toThrow(); - }); - - it('parse x-axis complete variant 1', () => { - const str = `xychart-beta \n x-axis "this is x axis" [category1, "category 2", category3]\n`; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'this is x axis', type: 'text' }); - expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ - { text: 'category1', type: 'text' }, - { text: 'category 2', type: 'text' }, - { text: 'category3', type: 'text' }, - ]); - }); - - it('parse x-axis complete variant 2', () => { - const str = - 'xychart-beta \nx-axis xAxisName [ "cat1 with space" , cat2 , cat3] \n '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ - { text: 'cat1 with space', type: 'text' }, - { text: 'cat2', type: 'text' }, - { text: 'cat3', type: 'text' }, - ]); - }); - - it('parse x-axis complete variant 3', () => { - const str = - 'xychart-beta \nx-axis xAxisName [ "cat1 with space" , cat2 asdf , cat3] \n '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ - { text: 'cat1 with space', type: 'text' }, - { text: 'cat2asdf', type: 'text' }, - { text: 'cat3', type: 'text' }, - ]); - }); - - it('parse y-axis with axis name', () => { - const str = 'xychart-beta \ny-axis yAxisName\n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - }); - it('parse y-axis with axis name with spaces', () => { - const str = 'xychart-beta \ny-axis yAxisName \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - }); - it('parse y-axis with axis name with "', () => { - const str = 'xychart-beta \n y-axis "yAxisName has space"\n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ - text: 'yAxisName has space', - type: 'text', + }); }); - }); - it('parse y-axis with axis name with " and spaces', () => { - const str = 'xychart-beta \n y-axis " yAxisName has space " \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ - text: ' yAxisName has space ', - type: 'text', + + it('parse x-axis with axis name with "', () => { + const str = 'xychart-beta \n x-axis "xAxisName has space"\n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: 'xAxisName has space', + type: 'text', + }); }); - }); - it('parse y-axis with axis name with range data', () => { - const str = 'xychart-beta \ny-axis yAxisName 45.5 --> 33 \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 33); - }); - it('parse y-axis without axisname with range data', () => { - const str = 'xychart-beta \ny-axis 45.5 --> 33 \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: '', type: 'text' }); - expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 33); - }); - it('parse y-axis with axis name with range data with only decimal part', () => { - const str = 'xychart-beta \ny-axis yAxisName 45.5 --> .33 \n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 0.33); - }); - it('parse y-axis throw error for invalid number in range data', () => { - const str = 'xychart-beta \ny-axis yAxisName 45.5 --> abc \n'; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse y-axis throws error if range data is passed', () => { - const str = 'xychart-beta \ny-axis yAxisName [ 45.3, 33 ] \n'; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse both axis at once', () => { - const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - }); - it('parse line Data', () => { - const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line lineTitle [23, 45, 56.6]'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: 'lineTitle', type: 'text' }, - [23, 45, 56.6] - ); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - }); - it('parse line Data with spaces and +,- symbols', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 , 56.6 ] '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: 'lineTitle with space', type: 'text' }, - [23, -45, 56.6] - ); - }); - it('parse line Data without title', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line [ +23 , -45 , 56.6 , .33] '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: '', type: 'text' }, - [23, -45, 56.6, 0.33] - ); - }); - it('parse line Data throws error unbalanced brackets', () => { - let str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 [ -45 , 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 ] 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse line Data throws error if data is not provided', () => { - const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse line Data throws error if data is empty', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse line Data throws error if , is not in proper', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , , -45 , 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse line Data throws error if not number', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -4aa5 , 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse bar Data', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle [23, 45, 56.6, .22]'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setBarData).toHaveBeenCalledWith([ - [{ text: 'barTitle', type: 'text' }, [23, 45, 56.6, 0.22]], - ]); - }); - it('parse bar Data spaces and +,- symbol', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 , 56.6 ] '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setBarData).toHaveBeenCalledWith([ - [{ text: 'barTitle with space', type: 'text' }, [23, -45, 56.6]], - ]); - }); - it('parse bar Data without plot title', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar [ +23 , -45 , 56.6 ] '; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setBarData).toHaveBeenCalledWith([['', [23, -45, 56.6]]]); - }); - it('parse bar should throw for unbalanced brackets', () => { - let str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 [ -45 , 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 ] 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse bar should throw error if data is not provided', () => { - const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse bar should throw error if data is empty', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse bar should throw error if comma is not proper', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , , -45 , 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse bar should throw error if number is not passed', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -4aa5 , 56.6 ] '; - expect(parserFnConstructor(str)).toThrow(); - }); - it('parse multiple bar and line variant 1', () => { - const str = - 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle1 [23, 45, 56.6] \n line lineTitle1 [11, 45.5, 67, 23] \n bar barTitle2 [13, 42, 56.89] \n line lineTitle2 [45, 99, 012]'; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); - expect(mockDB.setBarData).toHaveBeenCalledWith([ - [{ text: 'barTitle1', type: 'text' }, [23, 45, 56.6]], - ]); - expect(mockDB.setBarData).toHaveBeenCalledWith([ - [{ text: 'barTitle2', type: 'text' }, [13, 42, 56.89]], - ]); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: 'lineTitle1', type: 'text' }, - [11, 45.5, 67, 23] - ); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: 'lineTitle2', type: 'text' }, - [45, 99, 12] - ); - }); - it('parse multiple bar and line variant 2', () => { - const str = ` + + it('parse x-axis with axis name with " with spaces', () => { + const str = 'xychart-beta \n x-axis " xAxisName has space " \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: ' xAxisName has space ', + type: 'text', + }); + }); + + it('parse x-axis with axis name and range data', () => { + const str = 'xychart-beta \nx-axis xAxisName 45.5 --> 33 \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: 'xAxisName', + type: 'text', + }); + expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 33); + }); + it('parse x-axis throw error for invalid range data', () => { + const str = 'xychart-beta \nx-axis xAxisName aaa --> 33 \n'; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse x-axis with axis name and range data with only decimal part', () => { + const str = 'xychart-beta \nx-axis xAxisName 45.5 --> .34 \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: 'xAxisName', + type: 'text', + }); + expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 0.34); + }); + + it('parse x-axis without axisname and range data', () => { + const str = 'xychart-beta \nx-axis 45.5 --> 1.34 \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: '', + type: 'text', + }); + expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 1.34); + }); + + it('parse x-axis with axis name and category data', () => { + const str = 'xychart-beta \nx-axis xAxisName [ "cat1" , cat2a ] \n '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: 'xAxisName', + type: 'text', + }); + expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ + { + text: 'cat1', + type: 'text', + }, + { text: 'cat2a', type: 'text' }, + ]); + }); + + it('parse x-axis without axisname and category data', () => { + const str = 'xychart-beta \nx-axis [ "cat1" , cat2a ] \n '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ + text: '', + type: 'text', + }); + expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ + { + text: 'cat1', + type: 'text', + }, + { text: 'cat2a', type: 'text' }, + ]); + }); + + it('parse x-axis throw error if unbalanced bracket', () => { + let str = 'xychart-beta \nx-axis xAxisName [ "cat1" [ cat2a ] \n '; + expect(parserFnConstructor(str)).toThrow(); + str = 'xychart-beta \nx-axis xAxisName [ "cat1" , cat2a ] ] \n '; + expect(parserFnConstructor(str)).toThrow(); + }); + + it('parse x-axis complete variant 1', () => { + const str = `xychart-beta \n x-axis "this is x axis" [category1, "category 2", category3]\n`; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'this is x axis', type: 'text' }); + expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ + { text: 'category1', type: 'text' }, + { text: 'category 2', type: 'text' }, + { text: 'category3', type: 'text' }, + ]); + }); + + it('parse x-axis complete variant 2', () => { + const str = + 'xychart-beta \nx-axis xAxisName [ "cat1 with space" , cat2 , cat3] \n '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ + { text: 'cat1 with space', type: 'text' }, + { text: 'cat2', type: 'text' }, + { text: 'cat3', type: 'text' }, + ]); + }); + + it('parse x-axis complete variant 3', () => { + const str = + 'xychart-beta \nx-axis xAxisName [ "cat1 with space" , cat2 asdf , cat3] \n '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ + { text: 'cat1 with space', type: 'text' }, + { text: 'cat2asdf', type: 'text' }, + { text: 'cat3', type: 'text' }, + ]); + }); + + it('parse y-axis with axis name', () => { + const str = 'xychart-beta \ny-axis yAxisName\n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + }); + it('parse y-axis with axis name with spaces', () => { + const str = 'xychart-beta \ny-axis yAxisName \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + }); + it('parse y-axis with axis name with "', () => { + const str = 'xychart-beta \n y-axis "yAxisName has space"\n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ + text: 'yAxisName has space', + type: 'text', + }); + }); + it('parse y-axis with axis name with " and spaces', () => { + const str = 'xychart-beta \n y-axis " yAxisName has space " \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ + text: ' yAxisName has space ', + type: 'text', + }); + }); + it('parse y-axis with axis name with range data', () => { + const str = 'xychart-beta \ny-axis yAxisName 45.5 --> 33 \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 33); + }); + it('parse y-axis without axisname with range data', () => { + const str = 'xychart-beta \ny-axis 45.5 --> 33 \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: '', type: 'text' }); + expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 33); + }); + it('parse y-axis with axis name with range data with only decimal part', () => { + const str = 'xychart-beta \ny-axis yAxisName 45.5 --> .33 \n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 0.33); + }); + it('parse y-axis throw error for invalid number in range data', () => { + const str = 'xychart-beta \ny-axis yAxisName 45.5 --> abc \n'; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse y-axis throws error if range data is passed', () => { + const str = 'xychart-beta \ny-axis yAxisName [ 45.3, 33 ] \n'; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse both axis at once', () => { + const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + }); + it('parse line Data', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line lineTitle [23, 45, 56.6]'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: 'lineTitle', type: 'text' }, + [23, 45, 56.6] + ); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + }); + it('parse line Data with spaces and +,- symbols', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 , 56.6 ] '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: 'lineTitle with space', type: 'text' }, + [23, -45, 56.6] + ); + }); + it('parse line Data without title', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line [ +23 , -45 , 56.6 , .33] '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: '', type: 'text' }, + [23, -45, 56.6, 0.33] + ); + }); + it('parse line Data throws error unbalanced brackets', () => { + let str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 [ -45 , 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 ] 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse line Data throws error if data is not provided', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse line Data throws error if data is empty', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse line Data throws error if , is not in proper', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , , -45 , 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse line Data throws error if not number', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -4aa5 , 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse bar Data', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle [23, 45, 56.6, .22]'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [{ text: 'barTitle', type: 'text' }, [23, 45, 56.6, 0.22]], + ]); + }); + it('parse bar Data spaces and +,- symbol', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 , 56.6 ] '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [{ text: 'barTitle with space', type: 'text' }, [23, -45, 56.6]], + ]); + }); + it('parse bar Data without plot title', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar [ +23 , -45 , 56.6 ] '; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setBarData).toHaveBeenCalledWith([['', [23, -45, 56.6]]]); + }); + it('parse bar should throw for unbalanced brackets', () => { + let str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 [ -45 , 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 ] 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse bar should throw error if data is not provided', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse bar should throw error if data is empty', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse bar should throw error if comma is not proper', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , , -45 , 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse bar should throw error if number is not passed', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -4aa5 , 56.6 ] '; + expect(parserFnConstructor(str)).toThrow(); + }); + it('parse multiple bar and line variant 1', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle1 [23, 45, 56.6] \n line lineTitle1 [11, 45.5, 67, 23] \n bar barTitle2 [13, 42, 56.89] \n line lineTitle2 [45, 99, 012]'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [{ text: 'barTitle1', type: 'text' }, [23, 45, 56.6]], + ]); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [{ text: 'barTitle2', type: 'text' }, [13, 42, 56.89]], + ]); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: 'lineTitle1', type: 'text' }, + [11, 45.5, 67, 23] + ); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: 'lineTitle2', type: 'text' }, + [45, 99, 12] + ); + }); + it('parse multiple bar and line variant 2', () => { + const str = ` xychart-beta horizontal title Basic xychart x-axis "this is x axis" [category1, "category 2", category3] @@ -415,28 +419,60 @@ describe('Testing xychart jison file', () => { line lineTitle1 [11, 45.5, 67, 23] bar barTitle2 [13, 42, 56.89] line lineTitle2 [45, 99, 012]`; - expect(parserFnConstructor(str)).not.toThrow(); - expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yaxisText', type: 'text' }); - expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(10, 150); - expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'this is x axis', type: 'text' }); - expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ - { text: 'category1', type: 'text' }, - { text: 'category 2', type: 'text' }, - { text: 'category3', type: 'text' }, - ]); - expect(mockDB.setBarData).toHaveBeenCalledWith([ - [{ text: 'barTitle1', type: 'text' }, [23, 45, 56.6]], - ]); - expect(mockDB.setBarData).toHaveBeenCalledWith([ - [{ text: 'barTitle2', type: 'text' }, [13, 42, 56.89]], - ]); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: 'lineTitle1', type: 'text' }, - [11, 45.5, 67, 23] - ); - expect(mockDB.setLineData).toHaveBeenCalledWith( - { text: 'lineTitle2', type: 'text' }, - [45, 99, 12] - ); + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yaxisText', type: 'text' }); + expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(10, 150); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'this is x axis', type: 'text' }); + expect(mockDB.setXAxisBand).toHaveBeenCalledWith([ + { text: 'category1', type: 'text' }, + { text: 'category 2', type: 'text' }, + { text: 'category3', type: 'text' }, + ]); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [{ text: 'barTitle1', type: 'text' }, [23, 45, 56.6]], + ]); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [{ text: 'barTitle2', type: 'text' }, [13, 42, 56.89]], + ]); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: 'lineTitle1', type: 'text' }, + [11, 45.5, 67, 23] + ); + expect(mockDB.setLineData).toHaveBeenCalledWith( + { text: 'lineTitle2', type: 'text' }, + [45, 99, 12] + ); + }); + }); + + describe('multiple datasets', () => { + it('parse 2 datasets', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\nbar [["barTitle1" [23, 45, 56.6]],["barTitle2" [13, 42, 56.89]]]'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [ + [{ text: 'barTitle1', type: 'text' }, [23, 45, 56.6]], + [{ text: 'barTitle2', type: 'text' }, [13, 42, 56.89]], + ], + ]); + }); + + it('parse 3 datasets', () => { + const str = + 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\nbar [["barTitle1" [23, 45, 56.6]],["barTitle2" [13, 42, 56.89]],["barTitle3" [18, 37, 56.1]]]'; + expect(parserFnConstructor(str)).not.toThrow(); + expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' }); + expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' }); + expect(mockDB.setBarData).toHaveBeenCalledWith([ + [ + [{ text: 'barTitle1', type: 'text' }, [23, 45, 56.6]], + [{ text: 'barTitle2', type: 'text' }, [13, 42, 56.89]], + [{ text: 'barTitle3', type: 'text' }, [18, 37, 56.1]], + ], + ]); + }); }); }); diff --git a/packages/mermaid/src/diagrams/xychart/xychartDb.ts b/packages/mermaid/src/diagrams/xychart/xychartDb.ts index 218085769..595c9c6a6 100644 --- a/packages/mermaid/src/diagrams/xychart/xychartDb.ts +++ b/packages/mermaid/src/diagrams/xychart/xychartDb.ts @@ -172,15 +172,20 @@ function setLineData(title: NormalTextType, data: number[]) { type NamedDataset = [title: NormalTextType, data: number[]]; function setBarData(datasets: NamedDataset[]) { - datasets.forEach((dataset) => { - const plotData = transformDataWithoutCategory(dataset[1]); - xyChartData.plots.push({ - type: 'bar', - fill: getPlotColorFromPalette(plotIndex), - data: plotData, + datasets[0] + .filter((dataset) => Array.isArray(dataset)) + .forEach((dataset) => { + const data = dataset as any as NamedDataset; + const plotData = transformDataWithoutCategory( + Array.isArray(data[1]) ? data[1] : (dataset as any as number[]) + ); + xyChartData.plots.push({ + type: 'bar', + fill: getPlotColorFromPalette(plotIndex), + data: plotData, + }); + plotIndex++; }); - plotIndex++; - }); } function getDrawableElem(): DrawableElem[] {