Skip to content

Commit

Permalink
[IMP] charts: font size editor
Browse files Browse the repository at this point in the history
- Users can now customize the font size of chart titles and axis titles
from the chart design editor.

closes #5249

Task: 4316051
Signed-off-by: Lucas Lefèvre (lul) <[email protected]>
  • Loading branch information
dhrp-odoo committed Dec 3, 2024
1 parent 22c8483 commit a3c357b
Show file tree
Hide file tree
Showing 19 changed files with 246 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, useState } from "@odoo/owl";
import { CHART_AXIS_TITLE_FONT_SIZE } from "../../../../../constants";
import {
ChartWithDataSetDefinition,
Color,
Expand Down Expand Up @@ -39,6 +40,7 @@ export class AxisDesignEditor extends Component<Props, SpreadsheetChildEnv> {
return {
color: "",
align: "center",
fontSize: CHART_AXIS_TITLE_FONT_SIZE,
...axisDesign.title,
};
}
Expand All @@ -59,6 +61,18 @@ export class AxisDesignEditor extends Component<Props, SpreadsheetChildEnv> {
this.props.updateChart(this.props.figureId, { axesDesign });
}

updateAxisTitleFontSize(fontSize: number) {
const axesDesign = this.props.definition.axesDesign ?? {};
axesDesign[this.state.currentAxis] = {
...axesDesign[this.state.currentAxis],
title: {
...(axesDesign[this.state.currentAxis]?.title ?? {}),
fontSize,
},
};
this.props.updateChart(this.props.figureId, { axesDesign });
}

toggleBoldAxisTitle() {
const axesDesign = this.props.definition.axesDesign ?? {};
const title = axesDesign[this.state.currentAxis]?.title ?? {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
updateAlignment.bind="updateAxisTitleAlignment"
updateColor.bind="updateAxisTitleColor"
style="axisTitleStyle"
onFontSizeChanged.bind="updateAxisTitleFontSize"
/>
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, useState } from "@odoo/owl";
import { CHART_TITLE_FONT_SIZE } from "../../../../../constants";
import {
ChartDefinition,
Color,
Expand All @@ -20,6 +21,7 @@ interface Props {
figureId: UID;
definition: ChartDefinition;
updateChart: (figureId: UID, definition: Partial<ChartDefinition>) => DispatchResult;
defaultChartTitleFontSize?: number;
}

export class GeneralDesignEditor extends Component<Props, SpreadsheetChildEnv> {
Expand All @@ -34,8 +36,12 @@ export class GeneralDesignEditor extends Component<Props, SpreadsheetChildEnv> {
figureId: String,
definition: Object,
updateChart: Function,
defaultChartTitleFontSize: { type: Number, optional: true },
slots: { type: Object, optional: true },
};
static defaultProps = {
defaultChartTitleFontSize: CHART_TITLE_FONT_SIZE,
};
private state!: GeneralDesignEditorState;

setup() {
Expand Down Expand Up @@ -67,6 +73,7 @@ export class GeneralDesignEditor extends Component<Props, SpreadsheetChildEnv> {
get titleStyle(): TitleDesign {
return {
align: "left",
fontSize: this.props.defaultChartTitleFontSize,
...this.title,
};
}
Expand All @@ -77,6 +84,11 @@ export class GeneralDesignEditor extends Component<Props, SpreadsheetChildEnv> {
this.state.activeTool = "";
}

updateChartTitleFontSize(fontSize: number) {
const title = { ...this.title, fontSize };
this.props.updateChart(this.props.figureId, { title });
}

toggleBoldChartTitle() {
let title = this.title;
title = { ...title, bold: !title.bold };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
updateAlignment.bind="updateChartTitleAlignment"
updateColor.bind="updateChartTitleColor"
style="titleStyle"
onFontSizeChanged.bind="updateChartTitleFontSize"
/>
<t t-slot="general-extension"/>
</t>
Expand Down
11 changes: 9 additions & 2 deletions src/components/side_panel/chart/building_blocks/title/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, useExternalListener, useState } from "@odoo/owl";
import { GRAY_300 } from "../../../../../constants";
import { Color, SpreadsheetChildEnv, TitleDesign } from "../../../../../types";
import { ColorPickerWidget } from "../../../../color_picker/color_picker_widget";
import { FontSizeEditor } from "../../../../font_size_editor/font_size_editor";
import { css } from "../../../../helpers";
import { Section } from "../../../components/section/section";

Expand Down Expand Up @@ -47,6 +48,7 @@ interface Props {
updateAlignment?: (string) => void;
updateColor?: (Color) => void;
style: TitleDesign;
onFontSizeChanged: (fontSize: number) => void;
}

export interface ChartTitleState {
Expand All @@ -55,7 +57,7 @@ export interface ChartTitleState {

export class ChartTitle extends Component<Props, SpreadsheetChildEnv> {
static template = "o-spreadsheet.ChartTitle";
static components = { Section, ColorPickerWidget };
static components = { Section, ColorPickerWidget, FontSizeEditor };
static props = {
title: { type: String, optional: true },
updateTitle: Function,
Expand All @@ -64,7 +66,8 @@ export class ChartTitle extends Component<Props, SpreadsheetChildEnv> {
toggleBold: { type: Function, optional: true },
updateAlignment: { type: Function, optional: true },
updateColor: { type: Function, optional: true },
style: { type: Object, optional: true },
style: Object,
onFontSizeChanged: Function,
};
static defaultProps = {
title: "",
Expand All @@ -83,6 +86,10 @@ export class ChartTitle extends Component<Props, SpreadsheetChildEnv> {
this.props.updateTitle((ev.target as HTMLInputElement).value);
}

updateFontSize(fontSize: number) {
this.props.onFontSizeChanged(fontSize);
}

toggleDropdownTool(tool: string, ev: MouseEvent) {
const isOpen = this.state.activeTool === tool;
this.closeMenus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
</div>
</div>
<div class="o-divider"/>
<FontSizeEditor
currentFontSize="props.style.fontSize"
onFontSizeChanged.bind="this.updateFontSize"
class="'o-hoverable-button'"
/>
<div class="o-divider"/>
<ColorPickerWidget
currentColor="props.style.color"
toggleColorPicker="(ev) => this.toggleDropdownTool('fillChartColorTool', ev)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from "@odoo/owl";
import { SCORECARD_CHART_TITLE_FONT_SIZE } from "../../../../constants";
import { _t } from "../../../../translation";
import { ScorecardChartDefinition } from "../../../../types/chart/scorecard_chart";
import { Color, DispatchResult, SpreadsheetChildEnv, UID } from "../../../../types/index";
Expand Down Expand Up @@ -43,6 +44,10 @@ export class ScorecardChartDesignPanel extends Component<Props, SpreadsheetChild
return _t("Humanize numbers");
}

get defaultScorecardTitleFontSize(): number {
return SCORECARD_CHART_TITLE_FONT_SIZE;
}

updateHumanizeNumbers(humanize: boolean) {
this.props.updateChart(this.props.figureId, { humanize });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<GeneralDesignEditor
figureId="props.figureId"
definition="props.definition"
updateChart="props.updateChart">
updateChart="props.updateChart"
defaultChartTitleFontSize="defaultScorecardTitleFontSize">
<t t-set-slot="general-extension">
<Section class="'pt-1'" title.translate="Number formatting">
<Checkbox
Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export const CHART_PADDING = 20;
export const CHART_PADDING_BOTTOM = 10;
export const CHART_PADDING_TOP = 15;
export const CHART_TITLE_FONT_SIZE = 16;
export const CHART_AXIS_TITLE_FONT_SIZE = 12;

export const SCORECARD_CHART_TITLE_FONT_SIZE = 14;

// Color picker defaults as upper case HEX to match `toHex`helper
export const COLOR_PICKER_DEFAULTS: Color[] = [
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/figures/charts/gauge_chart_rendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export function getGaugeRenderingConfig(
({ width: titleWidth, height: titleHeight } = computeTextDimension(
ctx,
runtime.title.text,
{ ...runtime.title, fontSize: CHART_TITLE_FONT_SIZE },
{ fontSize: CHART_TITLE_FONT_SIZE, ...runtime.title },
"px"
));
}
Expand All @@ -245,7 +245,7 @@ export function getGaugeRenderingConfig(
height: boundingRect.height,
title: {
label: runtime.title.text ?? "",
fontSize: CHART_TITLE_FONT_SIZE,
fontSize: runtime.title.fontSize ?? CHART_TITLE_FONT_SIZE,
textPosition: {
x,
y: CHART_PADDING_TOP + titleHeight / 2,
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/figures/charts/runtime/chartjs_scales.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChartOptions, LinearScaleOptions } from "chart.js";
import { DeepPartial } from "chart.js/dist/types/utils";
import { CHART_AXIS_TITLE_FONT_SIZE } from "../../../../constants";
import { LocaleFormat } from "../../../../types";
import {
AxisDesign,
Expand Down Expand Up @@ -202,6 +203,7 @@ function getChartAxisTitleRuntime(design?: AxisDesign):
font: {
style: "italic" | "normal";
weight: "bold" | "normal";
size: number;
};
align: "start" | "center" | "end";
}
Expand All @@ -215,6 +217,7 @@ function getChartAxisTitleRuntime(design?: AxisDesign):
font: {
style: italic ? "italic" : "normal",
weight: bold ? "bold" : "normal",
size: design.title.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE,
},
align: align === "left" ? "start" : align === "right" ? "end" : "center",
};
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/figures/charts/runtime/chartjs_title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function getChartTitle(
align:
chartTitle.align === "center" ? "center" : chartTitle.align === "right" ? "end" : "start",
font: {
size: CHART_TITLE_FONT_SIZE,
size: definition.title.fontSize ?? CHART_TITLE_FONT_SIZE,
weight: chartTitle.bold ? "bold" : "normal",
style: chartTitle.italic ? "italic" : "normal",
},
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/figures/charts/scorecard_chart_config_builder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Color } from "chart.js";
import { SCORECARD_CHART_TITLE_FONT_SIZE } from "../../../constants";
import { DOMDimension, Pixel, PixelPosition } from "../../../types";
import { BaselineArrowDirection, ScorecardChartRuntime } from "../../../types/chart";
import { getDefaultContextFont } from "../../text_helper";
Expand All @@ -9,7 +10,6 @@ const CHART_PADDING = 10;
const BOTTOM_PADDING_RATIO = 0.05;

/* Maximum font sizes of each element */
const CHART_TITLE_FONT_SIZE = 14;
const KEY_VALUE_FONT_SIZE = 32;
const BASELINE_MAX_FONT_SIZE = 16;

Expand Down Expand Up @@ -270,7 +270,7 @@ class ScorecardChartConfigBuilder {
return {
title: {
font: getDefaultContextFont(
CHART_TITLE_FONT_SIZE,
this.runtime.title.fontSize ?? SCORECARD_CHART_TITLE_FONT_SIZE,
this.runtime.title.bold,
this.runtime.title.italic
),
Expand Down
1 change: 1 addition & 0 deletions src/types/chart/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export interface TitleDesign {
readonly italic?: boolean;
readonly align?: Align;
readonly color?: Color;
readonly fontSize?: number;
}

export type TrendType = "polynomial" | "exponential" | "logarithmic" | "trailingMovingAverage";
Expand Down
10 changes: 6 additions & 4 deletions src/xlsx/functions/charts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CHART_TITLE_FONT_SIZE } from "../../constants";
import { CHART_AXIS_TITLE_FONT_SIZE, CHART_TITLE_FONT_SIZE } from "../../constants";
import { ColorGenerator, largeMax, range } from "../../helpers";
import { chartMutedFontColor } from "../../helpers/figures/charts";
import { Color, ExcelWorkbookData, FigureData } from "../../types";
Expand Down Expand Up @@ -58,9 +58,10 @@ export function createChart(
let title = escapeXml``;
if (chart.data.title?.text) {
const titleColor = toXlsxHexColor(chartMutedFontColor(chart.data.backgroundColor));
const fontSize = chart.data.title.fontSize ?? CHART_TITLE_FONT_SIZE;
title = escapeXml/*xml*/ `
<c:title>
${insertText(chart.data.title.text, titleColor, CHART_TITLE_FONT_SIZE, chart.data.title)}
${insertText(chart.data.title.text, titleColor, fontSize, chart.data.title)}
<c:overlay val="0" />
</c:title>
`;
Expand Down Expand Up @@ -158,7 +159,7 @@ function lineAttributes(params: LineAttributes): XMLString {
function insertText(
text: string,
fontColor: XlsxHexColor = "000000",
fontsize: number,
fontsize: number = CHART_TITLE_FONT_SIZE,
style: { bold?: boolean; italic?: boolean } = {}
): XMLString {
return escapeXml/*xml*/ `
Expand Down Expand Up @@ -794,6 +795,7 @@ function addAx(
// Each Axis present inside a graph needs to be identified by an unsigned integer in order to be referenced by its crossAxis.
// I.e. x-axis, will reference y-axis and vice-versa.
const color = title?.color ? toXlsxHexColor(title.color) : defaultFontColor;
const fontSize = title?.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE;
return escapeXml/*xml*/ `
<${axisName}>
<c:axId val="${axId}"/>
Expand All @@ -809,7 +811,7 @@ function addAx(
<c:minorTickMark val="none" />
<c:numFmt formatCode="General" sourceLinked="1" />
<c:title>
${insertText(title?.text ?? "", color, 10, title)}
${insertText(title?.text ?? "", color, fontSize, title)}
</c:title>
${insertTextProperties(10, defaultFontColor)}
</${axisName}>
Expand Down
35 changes: 35 additions & 0 deletions tests/figures/chart/charts_component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,41 @@ describe("charts", () => {
bold: true,
italic: true,
});

const fontSize = fixture.querySelector(".o-font-size-editor input") as HTMLInputElement;
await setInputValueAndTrigger(fontSize, "20", "onlyChange");
expect(model.getters.getChartDefinition(chartId).title).toEqual({
text: "title",
fontSize: 20,
bold: true,
italic: true,
});
});

test("can edit chart axis title font size", async () => {
createChart(
model,
{
dataSets: [{ dataRange: "C1:C4" }],
labelRange: "A2:A4",
type: "line",
},
chartId
);
await mountChartSidePanel();
await openChartDesignSidePanel(model, env, fixture, chartId);

const fontSize = fixture.querySelectorAll(".o-font-size-editor input")[1] as HTMLInputElement;
await setInputValueAndTrigger(fontSize, "20", "onlyChange");

await click(fixture, ".o-badge-selection button[data-id=y]");
await setInputValueAndTrigger(fontSize, "25", "onlyChange");

const definition = model.getters.getChartDefinition(chartId) as LineChartDefinition;
expect(definition.axesDesign).toEqual({
x: { title: { fontSize: 20 } },
y: { title: { fontSize: 25 } },
});
});

test("can edit chart axis title color", async () => {
Expand Down
29 changes: 29 additions & 0 deletions tests/side_panels/building_blocks/__snapshots__/title.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,35 @@ exports[`Chart title Can render a chart title component 1`] = `
</span>
<div
class="o-divider"
/>
<div
class="o-dropdown"
>
<div
class="o-font-size-editor d-flex align-items-center o-hoverable-button"
title="Font Size"
>
<input
class="o-font-size o-number-input bg-transparent border-0"
max="400"
min="1"
type="number"
/>
<span>
<div
class="o-icon fa-small"
>
<i
class="fa fa-caret-down"
/>
</div>
</span>
</div>
</div>
<div
class="o-divider"
/>
Expand Down
Loading

0 comments on commit a3c357b

Please sign in to comment.