Merge pull request #5173 from ilyes-ced/feature/add-point-styling-quadrant-to-charts

feat: Add point styling for quadrant chart
This commit is contained in:
Sidharth Vinod 2024-04-16 09:17:07 +05:30 committed by GitHub
commit 3809732e48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 540 additions and 17 deletions

View File

@ -1,4 +1,4 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
import { imgSnapshotTest } from '../../helpers/util.ts';
describe('Quadrant Chart', () => {
it('should render if only chart type is provided', () => {
@ -226,4 +226,52 @@ describe('Quadrant Chart', () => {
);
cy.get('svg');
});
it('it should render data points with styles', () => {
imgSnapshotTest(
`
quadrantChart
title Reach and engagement of campaigns
x-axis Reach -->
y-axis Engagement -->
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.3, 0.6] radius: 20
Campaign B: [0.45, 0.23] color: #ff0000
Campaign C: [0.57, 0.69] stroke-color: #ff00ff
Campaign D: [0.78, 0.34] stroke-width: 3px
Campaign E: [0.40, 0.34] radius: 20, color: #ff0000 , stroke-color : #ff00ff, stroke-width : 3px
Campaign F: [0.35, 0.78] stroke-width: 3px , color: #ff0000, radius: 20, stroke-color: #ff00ff
Campaign G: [0.22, 0.22] stroke-width: 3px , color: #309708 , radius : 20 , stroke-color: #5060ff
Campaign H: [0.22, 0.44]
`,
{}
);
cy.get('svg');
});
it('it should render data points with styles + classes', () => {
imgSnapshotTest(
`
quadrantChart
title Reach and engagement of campaigns
x-axis Reach -->
y-axis Engagement -->
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A:::class1: [0.3, 0.6] radius: 20
Campaign B: [0.45, 0.23] color: #ff0000
Campaign C: [0.57, 0.69] stroke-color: #ff00ff
Campaign D:::class2: [0.78, 0.34] stroke-width: 3px
Campaign E:::class2: [0.40, 0.34] radius: 20, color: #ff0000, stroke-color: #ff00ff, stroke-width: 3px
Campaign F:::class1: [0.35, 0.78]
classDef class1 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
classDef class2 color: #f00fff, radius : 10
`
);
});
});

View File

@ -168,3 +168,86 @@ quadrantChart
quadrant-3 Delegate
quadrant-4 Delete
```
### Point styling
Points can either be styled directly or with defined shared classes
1. Direct styling
```md
Point A: [0.9, 0.0] radius: 12
Point B: [0.8, 0.1] color: #ff3300, radius: 10
Point C: [0.7, 0.2] radius: 25, color: #00ff33, stroke-color: #10f0f0
Point D: [0.6, 0.3] radius: 15, stroke-color: #00ff0f, stroke-width: 5px ,color: #ff33f0
```
2. Classes styling
```md
Point A:::class1: [0.9, 0.0]
Point B:::class2: [0.8, 0.1]
Point C:::class3: [0.7, 0.2]
Point D:::class3: [0.7, 0.2]
classDef class1 color: #109060
classDef class2 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
classDef class3 color: #f00fff, radius : 10
```
#### Available styles:
| Parameter | Description |
| ------------ | ---------------------------------------------------------------------- |
| color | Fill color of the point |
| radius | Radius of the point |
| stroke-width | Border width of the point |
| stroke-color | Border color of the point (useless when stroke-width is not specified) |
> **Note**
> Order of preference:
>
> 1. Direct styles
> 2. Class styles
> 3. Theme styles
## Example on styling
```mermaid-example
quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.9, 0.0] radius: 12
Campaign B:::class1: [0.8, 0.1] color: #ff3300, radius: 10
Campaign C: [0.7, 0.2] radius: 25, color: #00ff33, stroke-color: #10f0f0
Campaign D: [0.6, 0.3] radius: 15, stroke-color: #00ff0f, stroke-width: 5px ,color: #ff33f0
Campaign E:::class2: [0.5, 0.4]
Campaign F:::class3: [0.4, 0.5] color: #0000ff
classDef class1 color: #109060
classDef class2 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
classDef class3 color: #f00fff, radius : 10
```
```mermaid
quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.9, 0.0] radius: 12
Campaign B:::class1: [0.8, 0.1] color: #ff3300, radius: 10
Campaign C: [0.7, 0.2] radius: 25, color: #00ff33, stroke-color: #10f0f0
Campaign D: [0.6, 0.3] radius: 15, stroke-color: #00ff0f, stroke-width: 5px ,color: #ff33f0
Campaign E:::class2: [0.5, 0.4]
Campaign F:::class3: [0.4, 0.5] color: #0000ff
classDef class1 color: #109060
classDef class2 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
classDef class3 color: #f00fff, radius : 10
```

View File

@ -11,6 +11,7 @@
%x point_start
%x point_x
%x point_y
%x class_name
%%
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
@ -35,6 +36,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
" "*"quadrant-2"" "* return 'QUADRANT_2';
" "*"quadrant-3"" "* return 'QUADRANT_3';
" "*"quadrant-4"" "* return 'QUADRANT_4';
"classDef" return 'CLASSDEF';
["][`] { this.begin("md_string");}
<md_string>[^`"]+ { return "MD_STR";}
@ -43,6 +45,9 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<string>["] this.popState();
<string>[^"]* return "STR";
\:\:\: {this.begin('class_name')}
<class_name>^\w+ {this.popState(); return 'class_name';}
\s*\:\s*\[\s* {this.begin("point_start"); return 'point_start';}
<point_start>(1)|(0(.\d+)?) {this.begin('point_x'); return 'point_x';}
<point_start>\s*\]" "* {this.popState();}
@ -75,6 +80,31 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
%% /* language grammar */
idStringToken : ALPHA | NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT | UNICODE_TEXT;
styleComponent: ALPHA | NUM | NODE_STRING | COLON | UNIT | SPACE | BRKT | STYLE | PCT | MINUS ;
idString
:idStringToken
{$$=$idStringToken}
| idString idStringToken
{$$=$idString+''+$idStringToken}
;
style: styleComponent
|style styleComponent
{$$ = $style + $styleComponent;}
;
stylesOpt: style
{$$ = [$style.trim()]}
| stylesOpt COMMA style
{$stylesOpt.push($style.trim());$$ = $stylesOpt;}
;
classDefStatement
: CLASSDEF SPACE idString SPACE stylesOpt {$$ = $CLASSDEF;yy.addClass($idString,$stylesOpt);}
;
start
: eol start
| SPACE start
@ -92,6 +122,7 @@ line
statement
:
| classDefStatement {$$=[];}
| SPACE statement
| axisDetails
| quadrantDetails
@ -103,7 +134,10 @@ statement
;
points
: text point_start point_x point_y {yy.addPoint($1, $3, $4);}
: text point_start point_x point_y {yy.addPoint($1, "", $3, $4, []);}
| text class_name point_start point_x point_y {yy.addPoint($1, $2, $4, $5, []);}
| text point_start point_x point_y stylesOpt {yy.addPoint($1, "", $3, $4, $stylesOpt);}
| text class_name point_start point_x point_y stylesOpt {yy.addPoint($1, $2, $4, $5, $stylesOpt);}
;
axisDetails

View File

@ -212,20 +212,34 @@ describe('Testing quadrantChart jison file', () => {
it('should be able to parse points', () => {
let str = 'quadrantChart\npoint1: [0.1, 0.4]';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'point1', type: 'text' }, '0.1', '0.4');
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'point1', type: 'text' },
'',
'0.1',
'0.4',
[]
);
clearMocks();
str = 'QuadRantChart \n Point1 : [0.1, 0.4] ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'Point1', type: 'text' }, '0.1', '0.4');
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Point1', type: 'text' },
'',
'0.1',
'0.4',
[]
);
clearMocks();
str = 'QuadRantChart \n "Point1 : (* +=[❤": [1, 0] ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Point1 : (* +=[❤', type: 'text' },
'',
'1',
'0'
'0',
[]
);
clearMocks();
@ -264,15 +278,149 @@ describe('Testing quadrantChart jison file', () => {
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Visionaries', type: 'text' });
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Microsoft', type: 'text' },
'',
'0.75',
'0.75'
'0.75',
[]
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Salesforce', type: 'text' },
'',
'0.55',
'0.60'
'0.60',
[]
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'IBM', type: 'text' },
'',
'0.51',
'0.40',
[]
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Incorta', type: 'text' },
'',
'0.20',
'0.30',
[]
);
});
it('should be able to parse the whole chart with point styling with all params or some params', () => {
const str = `quadrantChart
title Analytics and Business Intelligence Platforms
x-axis "Completeness of Vision ❤" --> "x-axis-2"
y-axis Ability to Execute --> "y-axis-2"
quadrant-1 Leaders
quadrant-2 Challengers
quadrant-3 Niche
quadrant-4 Visionaries
Microsoft: [0.75, 0.75] radius: 10
Salesforce: [0.55, 0.60] radius: 10, color: #ff0000
IBM: [0.51, 0.40] radius: 10, color: #ff0000, stroke-color: #ff00ff
Incorta: [0.20, 0.30] radius: 10 ,color: #ff0000 ,stroke-color: #ff00ff ,stroke-width: 10px`;
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
text: 'Completeness of Vision ❤',
type: 'text',
});
expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'x-axis-2', type: 'text' });
expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'y-axis-2', type: 'text' });
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
text: 'Ability to Execute',
type: 'text',
});
expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Leaders', type: 'text' });
expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Challengers', type: 'text' });
expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Niche', type: 'text' });
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Visionaries', type: 'text' });
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Microsoft', type: 'text' },
'',
'0.75',
'0.75',
['radius: 10']
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Salesforce', type: 'text' },
'',
'0.55',
'0.60',
['radius: 10', 'color: #ff0000']
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'IBM', type: 'text' },
'',
'0.51',
'0.40',
['radius: 10', 'color: #ff0000', 'stroke-color: #ff00ff']
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Incorta', type: 'text' },
'',
'0.20',
'0.30',
['radius: 10', 'color: #ff0000', 'stroke-color: #ff00ff', 'stroke-width: 10px']
);
});
it('should be able to parse the whole chart with point styling with params in a random order + class names', () => {
const str = `quadrantChart
title Analytics and Business Intelligence Platforms
x-axis "Completeness of Vision ❤" --> "x-axis-2"
y-axis Ability to Execute --> "y-axis-2"
quadrant-1 Leaders
quadrant-2 Challengers
quadrant-3 Niche
quadrant-4 Visionaries
Microsoft: [0.75, 0.75] stroke-color: #ff00ff ,stroke-width: 10px, color: #ff0000, radius: 10
Salesforce:::class1: [0.55, 0.60] radius: 10, color: #ff0000
IBM: [0.51, 0.40] stroke-color: #ff00ff ,stroke-width: 10px
Incorta: [0.20, 0.30] stroke-width: 10px`;
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
text: 'Completeness of Vision ❤',
type: 'text',
});
expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'x-axis-2', type: 'text' });
expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'y-axis-2', type: 'text' });
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
text: 'Ability to Execute',
type: 'text',
});
expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Leaders', type: 'text' });
expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Challengers', type: 'text' });
expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Niche', type: 'text' });
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Visionaries', type: 'text' });
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Microsoft', type: 'text' },
'',
'0.75',
'0.75',
['stroke-color: #ff00ff', 'stroke-width: 10px', 'color: #ff0000', 'radius: 10']
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Salesforce', type: 'text' },
'class1',
'0.55',
'0.60',
['radius: 10', 'color: #ff0000']
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'IBM', type: 'text' },
'',
'0.51',
'0.40',
['stroke-color: #ff00ff', 'stroke-width: 10px']
);
expect(mockDB.addPoint).toHaveBeenCalledWith(
{ text: 'Incorta', type: 'text' },
'',
'0.20',
'0.30',
['stroke-width: 10px']
);
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'IBM', type: 'text' }, '0.51', '0.40');
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'Incorta', type: 'text' }, '0.20', '0.30');
});
});

View File

@ -1,7 +1,7 @@
import { scaleLinear } from 'd3';
import { log } from '../../logger.js';
import type { BaseDiagramConfig, QuadrantChartConfig } from '../../config.type.js';
import defaultConfig from '../../defaultConfig.js';
import { log } from '../../logger.js';
import { getThemeVariables } from '../../themes/theme-default.js';
import type { Point } from '../../types.js';
@ -10,7 +10,15 @@ const defaultThemeVariables = getThemeVariables();
export type TextVerticalPos = 'left' | 'center' | 'right';
export type TextHorizontalPos = 'top' | 'middle' | 'bottom';
export interface QuadrantPointInputType extends Point {
export interface StylesObject {
className?: string;
radius?: number;
color?: string;
strokeColor?: string;
strokeWidth?: string;
}
export interface QuadrantPointInputType extends Point, StylesObject {
text: string;
}
@ -23,7 +31,9 @@ export interface QuadrantTextType extends Point {
rotation: number;
}
export interface QuadrantPointType extends Point {
export interface QuadrantPointType
extends Point,
Pick<StylesObject, 'strokeColor' | 'strokeWidth'> {
fill: string;
radius: number;
text: QuadrantTextType;
@ -117,6 +127,7 @@ export class QuadrantBuilder {
private config: QuadrantBuilderConfig;
private themeConfig: QuadrantBuilderThemeConfig;
private data: QuadrantBuilderData;
private classes: Record<string, StylesObject> = {};
constructor() {
this.config = this.getDefaultConfig();
@ -191,6 +202,7 @@ export class QuadrantBuilder {
this.config = this.getDefaultConfig();
this.themeConfig = this.getDefaultThemeConfig();
this.data = this.getDefaultData();
this.classes = {};
log.info('clear called');
}
@ -202,6 +214,10 @@ export class QuadrantBuilder {
this.data.points = [...points, ...this.data.points];
}
addClass(className: string, styles: StylesObject) {
this.classes[className] = styles;
}
setConfig(config: Partial<QuadrantBuilderConfig>) {
log.trace('setConfig called with: ', config);
this.config = { ...this.config, ...config };
@ -470,11 +486,15 @@ export class QuadrantBuilder {
.range([quadrantHeight + quadrantTop, quadrantTop]);
const points: QuadrantPointType[] = this.data.points.map((point) => {
const classStyles = this.classes[point.className as keyof typeof this.classes];
if (classStyles) {
point = { ...classStyles, ...point };
}
const props: QuadrantPointType = {
x: xAxis(point.x),
y: yAxis(point.y),
fill: this.themeConfig.quadrantPointFill,
radius: this.config.pointRadius,
fill: point.color || this.themeConfig.quadrantPointFill,
radius: point.radius || this.config.pointRadius,
text: {
text: point.text,
fill: this.themeConfig.quadrantPointTextFill,
@ -485,6 +505,8 @@ export class QuadrantBuilder {
fontSize: this.config.pointLabelFontSize,
rotation: 0,
},
strokeColor: point.strokeColor || this.themeConfig.quadrantPointFill,
strokeWidth: point.strokeWidth || '0px',
};
return props;
});

View File

@ -0,0 +1,50 @@
import quadrantDb from './quadrantDb.js';
describe('quadrant unit tests', () => {
it('should parse the styles array and return a StylesObject', () => {
const styles = ['radius: 10', 'color: #ff0000', 'stroke-color: #ff00ff', 'stroke-width: 10px'];
const result = quadrantDb.parseStyles(styles);
expect(result).toEqual({
radius: 10,
color: '#ff0000',
strokeColor: '#ff00ff',
strokeWidth: '10px',
});
});
it('should throw an error for non supported style name', () => {
const styles: string[] = ['test_name: value'];
expect(() => quadrantDb.parseStyles(styles)).toThrowError(
'style named test_name is not supported.'
);
});
it('should return an empty StylesObject for an empty input array', () => {
const styles: string[] = [];
const result = quadrantDb.parseStyles(styles);
expect(result).toEqual({});
});
it('should throw an error for non supported style value', () => {
let styles: string[] = ['radius: f'];
expect(() => quadrantDb.parseStyles(styles)).toThrowError(
'value for radius f is invalid, please use a valid number'
);
styles = ['color: ffaa'];
expect(() => quadrantDb.parseStyles(styles)).toThrowError(
'value for color ffaa is invalid, please use a valid hex code'
);
styles = ['stroke-color: #f677779'];
expect(() => quadrantDb.parseStyles(styles)).toThrowError(
'value for stroke-color #f677779 is invalid, please use a valid hex code'
);
styles = ['stroke-width: 30'];
expect(() => quadrantDb.parseStyles(styles)).toThrowError(
'value for stroke-width 30 is invalid, please use a valid number of pixels (eg. 10px)'
);
});
});

View File

@ -9,7 +9,14 @@ import {
setAccDescription,
clear as commonClear,
} from '../common/commonDb.js';
import type { StylesObject } from './quadrantBuilder.js';
import { QuadrantBuilder } from './quadrantBuilder.js';
import {
validateHexCode,
validateSizeInPixels,
validateNumber,
InvalidStyleError,
} from './utils.js';
const config = getConfig();
@ -56,8 +63,52 @@ function setYAxisBottomText(textObj: LexTextObj) {
quadrantBuilder.setData({ yAxisBottomText: textSanitizer(textObj.text) });
}
function addPoint(textObj: LexTextObj, x: number, y: number) {
quadrantBuilder.addPoints([{ x, y, text: textSanitizer(textObj.text) }]);
function parseStyles(styles: string[]): StylesObject {
const stylesObject: StylesObject = {};
for (const style of styles) {
const [key, value] = style.trim().split(/\s*:\s*/);
if (key === 'radius') {
if (validateNumber(value)) {
throw new InvalidStyleError(key, value, 'number');
}
stylesObject.radius = parseInt(value);
} else if (key === 'color') {
if (validateHexCode(value)) {
throw new InvalidStyleError(key, value, 'hex code');
}
stylesObject.color = value;
} else if (key === 'stroke-color') {
if (validateHexCode(value)) {
throw new InvalidStyleError(key, value, 'hex code');
}
stylesObject.strokeColor = value;
} else if (key === 'stroke-width') {
if (validateSizeInPixels(value)) {
throw new InvalidStyleError(key, value, 'number of pixels (eg. 10px)');
}
stylesObject.strokeWidth = value;
} else {
throw new Error(`style named ${key} is not supported.`);
}
}
return stylesObject;
}
function addPoint(textObj: LexTextObj, className: string, x: number, y: number, styles: string[]) {
const stylesObject = parseStyles(styles);
quadrantBuilder.addPoints([
{
x,
y,
text: textSanitizer(textObj.text),
className,
...stylesObject,
},
]);
}
function addClass(className: string, styles: string[]) {
quadrantBuilder.addClass(className, parseStyles(styles));
}
function setWidth(width: number) {
@ -111,7 +162,9 @@ export default {
setXAxisRightText,
setYAxisTopText,
setYAxisBottomText,
parseStyles,
addPoint,
addClass,
getQuadrantData,
clear,
setAccTitle,

View File

@ -152,7 +152,9 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram
.attr('cx', (data: QuadrantPointType) => data.x)
.attr('cy', (data: QuadrantPointType) => data.y)
.attr('r', (data: QuadrantPointType) => data.radius)
.attr('fill', (data: QuadrantPointType) => data.fill);
.attr('fill', (data: QuadrantPointType) => data.fill)
.attr('stroke', (data: QuadrantPointType) => data.strokeColor)
.attr('stroke-width', (data: QuadrantPointType) => data.strokeWidth);
dataPoints
.append('text')

View File

@ -0,0 +1,20 @@
class InvalidStyleError extends Error {
constructor(style: string, value: string, type: string) {
super(`value for ${style} ${value} is invalid, please use a valid ${type}`);
this.name = 'InvalidStyleError';
}
}
function validateHexCode(value: string): boolean {
return !/^#?([\dA-Fa-f]{6}|[\dA-Fa-f]{3})$/.test(value);
}
function validateNumber(value: string): boolean {
return !/^\d+$/.test(value);
}
function validateSizeInPixels(value: string): boolean {
return !/^\d+px$/.test(value);
}
export { validateHexCode, validateNumber, validateSizeInPixels, InvalidStyleError };

View File

@ -136,3 +136,66 @@ quadrantChart
quadrant-3 Delegate
quadrant-4 Delete
```
### Point styling
Points can either be styled directly or with defined shared classes
1. Direct styling
```md
Point A: [0.9, 0.0] radius: 12
Point B: [0.8, 0.1] color: #ff3300, radius: 10
Point C: [0.7, 0.2] radius: 25, color: #00ff33, stroke-color: #10f0f0
Point D: [0.6, 0.3] radius: 15, stroke-color: #00ff0f, stroke-width: 5px ,color: #ff33f0
```
2. Classes styling
```md
Point A:::class1: [0.9, 0.0]
Point B:::class2: [0.8, 0.1]
Point C:::class3: [0.7, 0.2]
Point D:::class3: [0.7, 0.2]
classDef class1 color: #109060
classDef class2 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
classDef class3 color: #f00fff, radius : 10
```
#### Available styles:
| Parameter | Description |
| ------------ | ---------------------------------------------------------------------- |
| color | Fill color of the point |
| radius | Radius of the point |
| stroke-width | Border width of the point |
| stroke-color | Border color of the point (useless when stroke-width is not specified) |
```note
Order of preference:
1. Direct styles
2. Class styles
3. Theme styles
```
## Example on styling
```mermaid-example
quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.9, 0.0] radius: 12
Campaign B:::class1: [0.8, 0.1] color: #ff3300, radius: 10
Campaign C: [0.7, 0.2] radius: 25, color: #00ff33, stroke-color: #10f0f0
Campaign D: [0.6, 0.3] radius: 15, stroke-color: #00ff0f, stroke-width: 5px ,color: #ff33f0
Campaign E:::class2: [0.5, 0.4]
Campaign F:::class3: [0.4, 0.5] color: #0000ff
classDef class1 color: #109060
classDef class2 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
classDef class3 color: #f00fff, radius : 10
```