initial converting pie files

This commit is contained in:
Yokozuna59
2023-06-16 23:05:06 +03:00
parent afea3e8f37
commit ea3fbbd58d
14 changed files with 383 additions and 307 deletions

View File

@@ -1,13 +0,0 @@
/**
* Mocked pie (picChart) diagram renderer
*/
import { vi } from 'vitest';
export const draw = vi.fn().mockImplementation(() => {
return '';
});
export default {
draw,
};

8
__mocks__/pieRenderer.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* Mocked pie (picChart) diagram renderer
*/
import { vi } from 'vitest';
const draw = vi.fn().mockImplementation(() => '');
export const renderer = { draw };

View File

@@ -3,9 +3,11 @@ import { imgSnapshotTest } from '../../helpers/util.js';
describe('info diagram', () => {
it('should handle an info definition', () => {
imgSnapshotTest(`info`);
cy.get('svg');
});
it('should handle an info definition with showInfo', () => {
imgSnapshotTest(`info showInfo`);
cy.get('svg');
});
});

View File

@@ -5,7 +5,7 @@ import er from '../diagrams/er/erDetector.js';
import git from '../diagrams/git/gitGraphDetector.js';
import gantt from '../diagrams/gantt/ganttDetector.js';
import { info } from '../diagrams/info/infoDetector.js';
import pie from '../diagrams/pie/pieDetector.js';
import { pie } from '../diagrams/pie/pieDetector.js';
import quadrantChart from '../diagrams/quadrant-chart/quadrantDetector.js';
import requirement from '../diagrams/requirement/requirementDetector.js';
import sequence from '../diagrams/sequence/sequenceDetector.js';

View File

@@ -1,10 +0,0 @@
name,amounts
Foo, 33
Rishab, 12
Alexis, 41
Tom, 16
Courtney, 59
Christina, 38
Jack, 21
Mickey, 25
Paul, 30
1 name amounts
2 Foo 33
3 Rishab 12
4 Alexis 41
5 Tom 16
6 Courtney 59
7 Christina 38
8 Jack 21
9 Mickey 25
10 Paul 30

View File

@@ -1,132 +0,0 @@
import pieDb from '../pieDb.js';
import pie from './pie.jison';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('when parsing pie', function () {
beforeEach(function () {
pie.parser.yy = pieDb;
pie.parser.yy.clear();
});
it('should handle very simple pie', function () {
const res = pie.parser.parse(`pie
"ash" : 100
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(100);
});
it('should handle simple pie', function () {
const res = pie.parser.parse(`pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with comments', function () {
const res = pie.parser.parse(`pie
%% comments
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with a directive', function () {
const res = pie.parser.parse(`%%{init: {'logLevel':0}}%%
pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with a title', function () {
const res = pie.parser.parse(`pie title a 60/40 pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a 60/40 pie');
});
it('should handle simple pie without an acc description (accDescr)', function () {
const res = pie.parser.parse(`pie title a neat chart
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const description = pieDb.getAccDescription();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a neat chart');
expect(description).toBe('');
});
it('should handle simple pie with an acc description (accDescr)', function () {
const res = pie.parser.parse(`pie title a neat chart
accDescr: a neat description
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const description = pieDb.getAccDescription();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a neat chart');
expect(description).toBe('a neat description');
});
it('should handle simple pie with a multiline acc description (accDescr)', function () {
const res = pie.parser.parse(`pie title a neat chart
accDescr {
a neat description
on multiple lines
}
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const description = pieDb.getAccDescription();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a neat chart');
expect(description).toBe('a neat description\non multiple lines');
});
it('should handle simple pie with positive decimal', function () {
const res = pie.parser.parse(`pie
"ash" : 60.67
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60.67);
});
it('should handle simple pie with negative decimal', function () {
expect(() => {
pie.parser.parse(`pie
"ash" : 60.67
"bat" : 40..12
`);
}).toThrowError();
});
});

View File

@@ -0,0 +1,148 @@
// @ts-ignore - jison doesn't export types
import { parser } from './parser/pie.jison';
import { db } from './pieDb.js';
import { setConfig } from '../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('pie chart', () => {
beforeEach(() => {
parser.yy = db;
parser.yy.clear();
});
it('should handle very simple pie', () => {
parser.parse(`pie
"ash": 100
`);
const sections = db.getSections();
expect(sections['ash']).toBe(100);
});
it('should handle simple pie', () => {
parser.parse(`pie
"ash" : 60
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with comments', () => {
parser.parse(`pie
%% comments
"ash" : 60
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with a directive', () => {
parser.parse(`%%{init: {'logLevel':0}}%%
pie
"ash" : 60
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with a title', () => {
parser.parse(`pie title a 60/40 pie
"ash" : 60
"bat" : 40
`);
const title = db.getDiagramTitle();
expect(title).toBe('a 60/40 pie');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with an acc title (accTitle)', () => {
parser.parse(`pie title a neat chart
accTitle: a neat acc title
"ash" : 60
"bat" : 40
`);
const title = db.getDiagramTitle();
expect(title).toBe('a neat chart');
const accTitle = db.getAccTitle();
expect(accTitle).toBe('a neat acc title');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with an acc description (accDescr)', () => {
parser.parse(`pie title a neat chart
accDescr: a neat description
"ash" : 60
"bat" : 40
`);
const title = db.getDiagramTitle();
expect(title).toBe('a neat chart');
const description = db.getAccDescription();
expect(description).toBe('a neat description');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with a multiline acc description (accDescr)', () => {
parser.parse(`pie title a neat chart
accDescr {
a neat description
on multiple lines
}
"ash" : 60
"bat" : 40
`);
const title = db.getDiagramTitle();
expect(title).toBe('a neat chart');
const description = db.getAccDescription();
expect(description).toBe('a neat description\non multiple lines');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with positive decimal', () => {
parser.parse(`pie
"ash" : 60.67
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60.67);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with negative decimal', () => {
expect(() => {
parser.parse(`pie
"ash" : -60.67
"bat" : 40.12
`);
}).toThrowError();
});
});

View File

@@ -1,69 +0,0 @@
import { log } from '../../logger.js';
import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
import {
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
getAccDescription,
setAccDescription,
clear as commonClear,
} from '../../commonDb.js';
let sections = {};
let showData = false;
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const addSection = function (id, value) {
id = common.sanitizeText(id, configApi.getConfig());
if (sections[id] === undefined) {
sections[id] = value;
log.debug('Added new section :', id);
}
};
const getSections = () => sections;
const setShowData = function (toggle) {
showData = toggle;
};
const getShowData = function () {
return showData;
};
const cleanupValue = function (value) {
if (value.substring(0, 1) === ':') {
value = value.substring(1).trim();
return Number(value.trim());
} else {
return Number(value.trim());
}
};
const clear = function () {
sections = {};
showData = false;
commonClear();
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().pie,
addSection,
getSections,
cleanupValue,
clear,
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
setShowData,
getShowData,
getAccDescription,
setAccDescription,
};

View File

@@ -0,0 +1,88 @@
import { log } from '../../logger.js';
import { parseDirective as _parseDirective } from '../../directiveUtils.js';
import { getConfig } from '../../config.js';
import { sanitizeText } from '../common/common.js';
import {
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
getAccDescription,
setAccDescription,
clear as commonClear,
} from '../../commonDb.js';
import type { ParseDirectiveDefinition } from '../../diagram-api/types.js';
import type { PieFields, PieDb, Sections } from './pieTypes.js';
import type { PieDiagramConfig } from '../../config.type.js';
export const DEFAULT_PIE_CONFIG: PieDiagramConfig = {
useMaxWidth: true,
useWidth: 1200,
textPosition: 0.75,
} as const;
export const DEFAULT_PIE_DB: PieFields = {
sections: {},
showData: false,
config: DEFAULT_PIE_CONFIG,
} as const;
let sections: Sections = DEFAULT_PIE_DB.sections;
let showData: boolean = DEFAULT_PIE_DB.showData;
const config: PieDiagramConfig = {
useWidth: DEFAULT_PIE_DB.config.useWidth,
useMaxWidth: DEFAULT_PIE_DB.config.useMaxWidth,
textPosition: DEFAULT_PIE_DB.config.textPosition,
};
export const parseDirective: ParseDirectiveDefinition = (statement, context, type) => {
_parseDirective(this, statement, context, type);
};
const addSection = (label: string, value: number): void => {
label = sanitizeText(label, getConfig());
if (sections[label] === undefined) {
sections[label] = value;
log.debug(`added new section: ${label}, with value: ${value}`);
}
};
const getSections = (): Sections => sections;
const setShowData = (toggle: boolean): void => {
showData = toggle;
};
const getShowData = (): boolean => showData;
const cleanupValue = (value: string): number => {
if (value.substring(0, 1) === ':') {
value = value.substring(1).trim();
return Number(value.trim());
} else {
return Number(value.trim());
}
};
const clear = (): void => {
sections = DEFAULT_PIE_DB.sections;
showData = DEFAULT_PIE_DB.showData;
commonClear();
};
export const db: PieDb = {
clear,
getConfig: () => getConfig().pie,
parseDirective,
setDiagramTitle,
getDiagramTitle,
setAccTitle,
getAccTitle,
setAccDescription,
getAccDescription,
addSection,
getSections,
cleanupValue,
setShowData,
getShowData,
};

View File

@@ -15,10 +15,8 @@ const loader: DiagramLoader = async () => {
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
export const pie: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@@ -1,7 +1,7 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: TODO Fix ts errors
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore - jison doesn't export types
import parser from './parser/pie.jison';
import db from './pieDb.js';
import { db } from './pieDb.js';
import styles from './styles.js';
import renderer from './pieRenderer.js';

View File

@@ -1,80 +1,71 @@
/** Created by AshishJ on 11-09-2019. */
// @ts-nocheck - placeholder to be handled
import { select, scaleOrdinal, pie as d3pie, arc } from 'd3';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import * as configApi from '../../config.js';
import { getConfig } from '../../config.js';
import { parseFontSize } from '../../utils.js';
let conf = configApi.getConfig();
import type { DrawDefinition, HTML, SVG } from '../../diagram-api/types.js';
import type { PieDb, Sections } from './pieTypes.js';
/**
* Draws a Pie Chart with the data given in text.
*
* @param text
* @param id
* @param text - pie chart code
* @param id - diagram id
*/
let width;
const height = 450;
export const draw = (txt, id, _version, diagObj) => {
const draw: DrawDefinition = (txt, id, _version, diagObj) => {
try {
conf = configApi.getConfig();
log.debug('Rendering info diagram\n' + txt);
log.debug('rendering pie chart\n' + txt);
const securityLevel = configApi.getConfig().securityLevel;
// Handle root and Document for when rendering in sandbox mode
let sandboxElement;
const config = getConfig();
let width: number | undefined = config.pie?.useWidth;
const height = 450;
const { securityLevel } = config;
// handle root and document for when rendering in sandbox mode
let sandboxElement: HTML | undefined;
let document: Document | null | undefined;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
document = sandboxElement.nodes()[0].contentDocument;
width = document?.parentElement?.offsetWidth;
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the Pie Chart definition
diagObj.db.clear();
// @ts-ignore - figure out how to assign HTML to document type
const root: HTML =
sandboxElement !== undefined && document !== undefined && document !== null
? select(document)
: select('body');
// parse the Pie Chart definition
const db = diagObj.db as PieDb;
db.clear();
log.debug('parsing pie chart');
diagObj.parser.parse(txt);
log.debug('Parsed info diagram');
const elem = doc.getElementById(id);
width = elem.parentElement.offsetWidth;
if (width === undefined) {
width = 1200;
}
if (conf.useWidth !== undefined) {
width = conf.useWidth;
}
if (conf.pie.useWidth !== undefined) {
width = conf.pie.useWidth;
}
const diagram: SVG = root.select('#' + id);
configureSvgSize(diagram, height, width, config.pie?.useMaxWidth || true);
const diagram = root.select('#' + id);
configureSvgSize(diagram, height, width, conf.pie.useMaxWidth);
// set viewBox
document?.parentElement?.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
// fetch the default direction, use TD if none was found
const margin = 40;
const legendRectSize = 18;
const legendSpacing = 4;
// Fetch the default direction, use TD if none was found
var margin = 40;
var legendRectSize = 18;
var legendSpacing = 4;
const radius = Math.min(width, height) / 2 - margin;
var radius = Math.min(width, height) / 2 - margin;
var svg = diagram
const svg = diagram
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var data = diagObj.db.getSections();
var sum = 0;
Object.keys(data).forEach(function (key) {
sum += data[key];
});
const themeVariables = conf.themeVariables;
var myGeneratedColors = [
const themeVariables = config.themeVariables;
const myGeneratedColors = [
themeVariables.pie1,
themeVariables.pie2,
themeVariables.pie3,
@@ -89,34 +80,41 @@ export const draw = (txt, id, _version, diagObj) => {
themeVariables.pie12,
];
const textPosition = conf.pie?.textPosition ?? 0.75;
const textPosition = config.pie?.textPosition ?? 0.75;
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
outerStrokeWidth ??= 2;
// Set the color scale
var color = scaleOrdinal().range(myGeneratedColors);
const color = scaleOrdinal().range(myGeneratedColors);
// Compute the position of each group on the pie:
var pieData = Object.entries(data).map(function (el, idx) {
const sections: Sections = db.getSections();
let sum = 0;
Object.keys(sections).forEach((key: string) => {
sum += sections[key];
});
// compute the position of each group on the pie:
const pieData = Object.entries(sections).map((el: [string, number], index: number) => {
return {
order: idx,
order: index,
name: el[0],
value: el[1],
};
});
var pie = d3pie()
.value(function (d) {
return d.value;
const pie = d3pie()
// @ts-ignore: TODO Fix ts errors
.value((d) => {
return d;
})
.sort(function (a, b) {
.sort((a, b) => {
// Sort slices in clockwise direction
return a.order - b.order;
});
var dataReady = pie(pieData);
const dataReady = pie(pieData);
// Shape helper to build arcs:
var arcGenerator = arc().innerRadius(0).outerRadius(radius);
var labelArcGenerator = arc()
const arcGenerator = arc().innerRadius(0).outerRadius(radius);
const labelArcGenerator = arc()
.innerRadius(radius * textPosition)
.outerRadius(radius * textPosition);
@@ -134,7 +132,8 @@ export const draw = (txt, id, _version, diagObj) => {
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', function (d) {
// @ts-ignore: TODO Fix ts errors
.attr('fill', (d) => {
return color(d.data.name);
})
.attr('class', 'pieCircle');
@@ -146,10 +145,12 @@ export const draw = (txt, id, _version, diagObj) => {
.data(dataReady)
.enter()
.append('text')
.text(function (d) {
// @ts-ignore: TODO Fix ts errors
.text((d) => {
return ((d.data.value / sum) * 100).toFixed(0) + '%';
})
.attr('transform', function (d) {
// @ts-ignore: TODO Fix ts errors
.attr('transform', (d) => {
return 'translate(' + labelArcGenerator.centroid(d) + ')';
})
.style('text-anchor', 'middle')
@@ -157,23 +158,24 @@ export const draw = (txt, id, _version, diagObj) => {
svg
.append('text')
.text(diagObj.db.getDiagramTitle())
.text(db.getDiagramTitle())
.attr('x', 0)
.attr('y', -(height - 50) / 2)
.attr('class', 'pieTitleText');
// Add the legends/annotations for each section
var legend = svg
const legend = svg
.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function (d, i) {
// @ts-ignore: TODO Fix ts errors
.attr('transform', (d, index: number) => {
const height = legendRectSize + legendSpacing;
const offset = (height * color.domain().length) / 2;
const horizontal = 12 * legendRectSize;
const vertical = i * height - offset;
const vertical = index * height - offset;
return 'translate(' + horizontal + ',' + vertical + ')';
});
@@ -189,19 +191,17 @@ export const draw = (txt, id, _version, diagObj) => {
.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function (d) {
if (diagObj.db.getShowData() || conf.showData || conf.pie.showData) {
// @ts-ignore: TODO Fix ts errors
.text((d) => {
if (db.getShowData()) {
return d.data.name + ' [' + d.data.value + ']';
} else {
return d.data.name;
}
});
} catch (e) {
log.error('Error while rendering info diagram');
log.error(e);
log.error('error while rendering pie chart', e);
}
};
export default {
draw,
};
export const renderer = { draw };

View File

@@ -0,0 +1,54 @@
import type { PieDiagramConfig } from '../../config.type.js';
import type { DiagramDB, ParseDirectiveDefinition } from '../../diagram-api/types.js';
export interface PieFields {
sections: Sections;
showData: boolean;
config: PieDiagramConfig;
}
export interface PieStyleOptions {
fontFamily: string;
pie1: string;
pie2: string;
pie3: string;
pie4: string;
pie5: string;
pie6: string;
pie7: string;
pie8: string;
pie9: string;
pie10: string;
pie11: string;
pie12: string;
pieTitleTextSize: string;
pieTitleTextColor: string;
pieSectionTextSize: string;
pieSectionTextColor: string;
pieLegendTextSize: string;
pieLegendTextColor: string;
pieStrokeColor: string;
pieStrokeWidth: string;
pieOuterStrokeWidth: string;
pieOuterStrokeColor: string;
pieOpacity: string;
}
export type Sections = Record<string, number>;
export interface PieDb extends DiagramDB {
clear: () => void;
getConfig: () => PieDiagramConfig | undefined;
parseDirective: ParseDirectiveDefinition;
setDiagramTitle: (title: string) => void;
getDiagramTitle: () => string;
setAccTitle: (title: string) => void;
getAccTitle: () => string;
setAccDescription: (describetion: string) => void;
getAccDescription: () => string;
addSection: (label: string, value: number) => void;
cleanupValue: (value: string) => number;
getSections: () => Sections;
setShowData: (toggle: boolean) => void;
getShowData: () => boolean;
}

View File

@@ -1,4 +1,6 @@
const getStyles = (options) =>
import { PieStyleOptions } from './pieTypes.js';
const getStyles = (options: PieStyleOptions) =>
`
.pieCircle{
stroke: ${options.pieStrokeColor};