diff --git a/src/__snapshots__/index.spec.ts.snap b/src/__snapshots__/index.spec.ts.snap index eed2018..d7de040 100644 --- a/src/__snapshots__/index.spec.ts.snap +++ b/src/__snapshots__/index.spec.ts.snap @@ -89,217 +89,45 @@ exports[`mjml-bar-chart mjml markup should render the bar chart 1`] = ` - - - - - - - - +
- - - - - - - - - - - - - - - - - - - - - - - - - - -
66
49
33
16
0
-
- - - - - - - - -
- - - - -
Sum of Requests by Department
-
- - - - - - - - - - - - +
- - - - - - - -
33
-
- - - - - - - -
14
-
- - - - - - - -
27
-
+ - - -
+ + + +
66
49
33
16
0
+ - - - - - + - - - - - - - -
Sum of Requests by Department
+ + + - - - - - - - - - + + + - - - - - - - - - - - -
+
33
+
14
+
27
- - - - - - - -
18
-
- - - - - - - -
66
-
- - - - - - - -
42
-
+
18
+
66
+
42
- - - - - - - -
7
-
- - - - - - - -
15
-
- - - - - - - -
21
-
-
- - - + + + +
+
7
+
15
+
21
- - -
January February March
-
- - - - - - - -
-

- support,sales,tech -

-
-
-
- +
+

support +sales +tech

@@ -326,3 +154,689 @@ exports[`mjml-bar-chart mjml markup should render the bar chart 1`] = ` " `; + +exports[`mjml-bar-chart renderJSON should render barChart 1`] = ` +{ + "attributes": { + "class": "mjmlBarChart", + "style": "border-collapse:collapse;margin:0 auto;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;vertical-align:top;", + }, + "children": [ + { + "attributes": { + "style": "border-collapse:collapse;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "firstStep", + }, + "content": "66", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "otherStep", + }, + "content": "49", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "otherStep", + }, + "content": "33", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "otherStep", + }, + "content": "16", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "otherStep", + }, + "content": "0", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;", + }, + "children": [ + { + "attributes": { + "style": "border-collapse:collapse;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;", + }, + "children": [ + { + "attributes": { + "style": "border-collapse:collapse;border-left:2px solid d4d4d4;border-bottom:2px solid d4d4d4;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:116px;", + }, + "content": "33", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:100px;background-color:#ffe5ec;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:174px;", + }, + "content": "14", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:42px;background-color:#ffb3c6;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:134px;", + }, + "content": "27", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:82px;background-color:#fb6f92;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:161px;", + }, + "content": "18", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:55px;background-color:#ffe5ec;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:16px;", + }, + "content": "66", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:200px;background-color:#ffb3c6;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:89px;", + }, + "content": "42", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:127px;background-color:#fb6f92;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:195px;", + }, + "content": "7", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:21px;background-color:#ffe5ec;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:171px;", + }, + "content": "15", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:45px;background-color:#ffb3c6;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0", + }, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;font-size:12px;vertical-align:bottom;text-align:center;line-height:16px;height:152px;", + }, + "content": "21", + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:64px;background-color:#fb6f92;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;", + }, + "children": [ + { + "attributes": { + "style": "border-collapse:collapse;border-left:2px solid transparent;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + { + "attributes": { + "style": "height:30px;padding:0;font-size:14px;text-align:center;overflow:hidden;min-width:90px;max-width:90px;", + }, + "content": "January", + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + { + "attributes": { + "style": "height:30px;padding:0;font-size:14px;text-align:center;overflow:hidden;min-width:90px;max-width:90px;", + }, + "content": "February", + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + { + "attributes": { + "style": "height:30px;padding:0;font-size:14px;text-align:center;overflow:hidden;min-width:90px;max-width:90px;", + }, + "content": "March", + "tagName": "td", + }, + { + "attributes": { + "style": "padding:0;min-width:30px;max-width:30px;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;", + }, + "children": [ + { + "attributes": { + "style": "border-collapse:collapse;width:100%;", + }, + "children": [ + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;height:10px;", + }, + "tagName": "td", + }, + ], + "tagName": "tr", + }, + { + "attributes": {}, + "children": [ + { + "attributes": { + "style": "padding:0;", + }, + "children": [ + { + "attributes": { + "style": "margin:0;padding:0;max-width:392px;line-height:20px;text-align:center;", + }, + "children": [ + { + "attributes": { + "style": "padding:0 10px;height:20px;font-size:14px;border-left:30px solid #ffe5ec;", + }, + "content": "support", + "tagName": "span", + }, + { + "attributes": { + "style": "padding:0 10px;height:20px;font-size:14px;border-left:30px solid #ffb3c6;", + }, + "content": "sales", + "tagName": "span", + }, + { + "attributes": { + "style": "padding:0 10px;height:20px;font-size:14px;border-left:30px solid #fb6f92;", + }, + "content": "tech", + "tagName": "span", + }, + ], + "tagName": "p", + }, + ], + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", + }, + ], + "tagName": "td", + }, + ], + "tagName": "tr", + }, + ], + "tagName": "table", +} +`; diff --git a/src/index.spec.ts b/src/index.spec.ts index 3bc4c70..6ffdf57 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1,6 +1,8 @@ import mjml2html from "mjml"; import { registerComponent } from "mjml-core"; import MjBarChart from "./index"; +// @ts-expect-error +import jsonToXML from "mjml-core/lib/helpers/jsonToXML"; function toHtml(mjml: string): string { const { html, errors } = mjml2html(mjml); @@ -45,29 +47,60 @@ describe("mjml-bar-chart", () => { describe("getChartBarSeparator", () => { it("should render chart bar separator", () => { - const gen = barChart["getChartBarSeparator"](); + const json = barChart["getChartBarSeparator"](); + const html = jsonToXML(json); - expect(gen).toBe(''); + expect(json).toStrictEqual({ + tagName: "td", + attributes: { + style: "padding:0;min-width:30px;max-width:30px;", + }, + }); + expect(html).toBe(''); }); }); - describe("getLabel", () => { + describe("getDatasetLabel", () => { it("should render label", () => { - const gen = barChart["getLabel"]("test"); + const json = barChart["getDatasetLabel"](2); + const html = jsonToXML(json); - expect(gen).toBe( - 'test' + expect(json).toStrictEqual({ + tagName: "td", + attributes: { + style: "height:30px;padding:0;font-size:14px;text-align:center;overflow:hidden;min-width:90px;max-width:90px;", + }, + content: "March", + }); + expect(html).toBe( + 'March' ); }); }); describe("getLegend", () => { it("should render legend", () => { - const gen = barChart["getLegend"]("test", "#abcdef"); + const json = barChart["getLegend"](1); + const html = jsonToXML(json); - expect(gen).toBe( - 'test' + expect(json).toStrictEqual({ + tagName: "span", + attributes: { + style: "padding:0 10px;height:20px;font-size:14px;border-left:30px solid #ffb3c6;", + }, + content: "sales", + }); + expect(html).toBe( + 'sales' ); }); }); + + describe("renderJSON", () => { + it("should render barChart", () => { + const json = barChart["renderJSON"](); + + expect(json).toMatchSnapshot(); + }); + }); }); diff --git a/src/index.ts b/src/index.ts index c9632e7..03b0551 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,45 @@ import { BodyComponent } from "mjml-core"; import { registerDependencies } from "mjml-validator"; +// @ts-expect-error +import jsonToXML from "mjml-core/lib/helpers/jsonToXML"; -interface dataset { +interface Dataset { label: string; data: number[]; } +interface Attributes { + title?: string; + colors: string; + "dataset-labels": string; + datasets: string; + groups: string; + "axis-color"?: string; + height?: string; + "bar-width"?: string; + "separator-width"?: string; + "step-count"?: string; + "show-values"?: string; +} + +interface InitialData { + attributes: Attributes; + [key: string]: any; +} + +interface JsonNode { + tagName: string; + attributes: Record; + children?: JsonNode[]; + content?: string; +} + +const TABLE = { tagName: "table", attributes: {} }; +const TR = { tagName: "tr", attributes: {} }; +const TD = { tagName: "td", attributes: {} }; +const SPAN = { tagName: "span", attributes: {} }; +const P = { tagName: "p", attributes: {} }; + export default class MjBarChart extends BodyComponent { private readonly title: string; private readonly colors: string[]; @@ -17,11 +51,11 @@ export default class MjBarChart extends BodyComponent { private readonly stepCount: number; private readonly showValues: boolean; private readonly datasetValues: number[][]; - private readonly datasets: dataset[]; + private readonly datasets: Dataset[]; private readonly higherValue: number; private readonly chartWidth: number; - constructor(initialData = {}) { + constructor(initialData: InitialData) { super(initialData); this.title = this.getAttribute("title"); @@ -87,161 +121,223 @@ export default class MjBarChart extends BodyComponent { "show-values": "true", }; - private getChartTitle() { - if (!this.title) { - return ""; - } + private getChartTitle(): JsonNode | undefined { + if (!this.title) return; - return ` - - - - - ${this.title} - - - - - `; + return { + ...TR, + children: [ + { + ...TD, + attributes: { style: "padding:0" }, + children: [ + { + ...TABLE, + attributes: { style: this.styles("chartTitleWrapper") }, + children: [ + { + ...TR, + children: [ + { + ...TD, + attributes: { style: this.styles("chartTitle") }, + content: this.title, + }, + ], + }, + ], + }, + ], + }, + ], + }; } - private getChartBar(value: number, color: string) { + private getChartBar(datasetIndex: number, dataIndex: number): JsonNode { + const value = this.datasets[datasetIndex].data[dataIndex]; const v = value > 0 ? value : 0; const plainPartHeight = Math.round((v / this.higherValue) * this.chartHeight); const emptyPartHeight = this.chartHeight - plainPartHeight + 16; - const emptyCellStyle = { - padding: "0", - height: `${emptyPartHeight}px`, - "font-size": "12px", - "vertical-align": "bottom", - "text-align": "center", - "line-height": "16px", - }; + const emptyCellStyle = `${this.styles("emptyCell")}height:${emptyPartHeight}px;`; + const plainCellStyle = `${this.styles( + "plainCell" + )}height:${plainPartHeight}px;background-color:${this.colors[dataIndex]};`; - const plainCellStyle = { - padding: "0", - height: `${plainPartHeight}px`, - "background-color": color, + return { + ...TD, + attributes: { style: "padding:0" }, + children: [ + { + ...TABLE, + attributes: { style: this.styles("chartBarWrapper") }, + children: [ + { + ...TR, + children: [ + { + ...TD, + attributes: { style: emptyCellStyle }, + content: this.showValues ? `${value}` : "", + }, + ], + }, + { + ...TR, + children: [ + { + ...TD, + attributes: { style: plainCellStyle }, + }, + ], + }, + ], + }, + ], }; - - return ` - - - - ${ - this.showValues ? value : "" - } - - - - - - - `; } - private getChartBarSeparator() { - return ``; + private getChartBarSeparator(): JsonNode { + return { ...TD, attributes: { style: this.styles("chartBarSeparator") } }; } - private getChartBars() { - const bars = [this.getChartBarSeparator()]; - - this.datasets.forEach((dataset) => { - dataset.data.forEach((datum, idx) => - bars.push(this.getChartBar(datum, this.colors[idx])) - ); - bars.push(this.getChartBarSeparator()); - }); - - return ` - - - - - ${bars.join("\n")} - - - - - `; + private getChartBars(): JsonNode { + const bars = this.datasets.flatMap((dataset, dsi) => [ + ...dataset.data.map((_, di) => this.getChartBar(dsi, di)), + this.getChartBarSeparator(), + ]); + + return { + ...TR, + children: [ + { + ...TD, + attributes: { style: "padding:0;" }, + children: [ + { + ...TABLE, + attributes: { style: this.styles("barChart") }, + children: [{ ...TR, children: [this.getChartBarSeparator(), ...bars] }], + }, + ], + }, + ], + }; } - private getLabel(value: string) { - return `${value}`; + private getDatasetLabel(index: number): JsonNode { + return { + ...TD, + attributes: { style: this.styles("chartLabel") }, + content: this.datasets[index].label, + }; } private getChartLabels() { - const labels = [this.getChartBarSeparator()]; - - this.datasets.forEach((dataset) => { - labels.push(this.getLabel(dataset.label)); - labels.push(this.getChartBarSeparator()); - }); - - return ` - - - - - ${labels.join("\n")} - - - - - `; - } + const labels = this.datasets.flatMap((_, i) => [ + this.getDatasetLabel(i), + this.getChartBarSeparator(), + ]); - private getLegend(value: string, color: string) { - const legendStyle = { - padding: "0 10px", - height: "20px", - "font-size": "14px", - "border-left": `${this.barWidth}px solid ${color}`, + return { + ...TR, + children: [ + { + ...TD, + attributes: { style: "padding:0;" }, + children: [ + { + ...TABLE, + attributes: { style: this.styles("chartLabelWrapper") }, + children: [ + { + ...TR, + children: [this.getChartBarSeparator(), ...labels], + }, + ], + }, + ], + }, + ], }; + } + + private getLegend(index: number): JsonNode { + const content = this.groups[index]; + const color = this.colors[index]; + const style = `${this.styles("legend")}border-left:${this.barWidth}px solid ${color};`; - return `${value}`; + return { ...SPAN, attributes: { style }, content }; } - private getChartLegend() { - return ` - - - - - - - - - - ${this.groups.map((g, i) => this.getLegend(g, this.colors[i]))} -

- - - - - - `; + private getChartLegend(): JsonNode { + return { + ...TR, + children: [ + { + ...TD, + attributes: { style: "padding:0;" }, + children: [ + { + ...TABLE, + attributes: { style: this.styles("chartLegendWrapper") }, + children: [ + { + ...TR, + children: [ + { + ...TD, + attributes: { style: "padding:0;height:10px;" }, + }, + ], + }, + { + ...TR, + children: [ + { + ...TD, + attributes: { style: "padding:0;" }, + children: [ + { + ...P, + attributes: { + style: this.styles("chartLegend"), + }, + children: this.groups.map((_, i) => + this.getLegend(i) + ), + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; } - private getChart() { - return ` - - - ${this.getChartTitle()} - ${this.getChartBars()} - ${this.getChartLabels()} - ${this.getChartLegend()} -
- - `; + private getChart(): JsonNode { + const chartTitle = this.getChartTitle(); + const children = [this.getChartBars(), this.getChartLabels(), this.getChartLegend()]; + + return { + ...TD, + attributes: { style: "padding:0;" }, + children: [ + { + ...TABLE, + attributes: { style: "border-collapse:collapse;" }, + children: chartTitle ? [chartTitle, ...children] : children, + }, + ], + }; } - private getScale() { - if (this.stepCount < 2) { - return ""; - } + private getScale(): JsonNode | undefined { + if (this.stepCount < 2) return; const steps = []; @@ -249,20 +345,29 @@ export default class MjBarChart extends BodyComponent { const value = Math.trunc((this.higherValue / (this.stepCount - 1)) * (i - 1)); const style = i === this.stepCount ? "firstStep" : "otherStep"; - steps.push(` - - ${value} - - `); + steps.push({ + ...TR, + children: [ + { + ...TD, + attributes: { style }, + content: `${value}`, + }, + ], + }); } - return ` - - - ${steps.join("\n")} -
- - `; + return { + ...TD, + attributes: { style: "padding:0;vertical-align:top;" }, + children: [ + { + ...TABLE, + attributes: { style: "border-collapse:collapse;" }, + children: steps, + }, + ], + }; } override getStyles(): object { @@ -293,6 +398,16 @@ export default class MjBarChart extends BodyComponent { "border-left": `2px solid ${this.axisColor}`, "border-bottom": `2px solid ${this.axisColor}`, }, + plainCell: { + padding: "0", + }, + emptyCell: { + padding: "0", + "font-size": "12px", + "vertical-align": "bottom", + "text-align": "center", + "line-height": "16px", + }, chartLabelWrapper: { "border-collapse": "collapse", "border-left": "2px solid transparent", @@ -317,6 +432,11 @@ export default class MjBarChart extends BodyComponent { "line-height": "20px", "text-align": "center", }, + legend: { + padding: "0 10px", + height: "20px", + "font-size": "14px", + }, firstStep: { padding: "0 5px 0 0", height: "56px", @@ -336,15 +456,23 @@ export default class MjBarChart extends BodyComponent { }; } + private renderJSON(): JsonNode { + const scale = this.getScale(); + const chart = this.getChart(); + + return { + ...TABLE, + attributes: { + class: "mjmlBarChart", + style: "border-collapse:collapse;margin:0 auto;", + }, + children: [{ ...TR, children: scale ? [scale, chart] : [chart] }], + }; + } + render() { - return ` - - - ${this.getScale()} - ${this.getChart()} - -
- `; + const json = this.renderJSON(); + return jsonToXML(json); } }