Skip to content

Commit ba7a932

Browse files
committed
Encapsulate renderer label helpers and update tests
1 parent 352d83a commit ba7a932

2 files changed

Lines changed: 82 additions & 71 deletions

File tree

src/post/renderer.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "bun:test";
2-
import { Renderer, calculateLabelStep } from "./renderer";
2+
import { Renderer } from "./renderer";
33

44
describe("Renderer", () => {
55
const testMetricsID: string = "1234567890";
@@ -500,7 +500,7 @@ describe("Renderer", () => {
500500
expect(visibleIndices[0]).toBe(0);
501501
expect(visibleIndices[visibleIndices.length - 1]).toBe(labels.length - 1);
502502

503-
const labelStep: number = calculateLabelStep(labels.length);
503+
const labelStep: number = Renderer.calculateLabelStep(labels.length);
504504
for (let i = 1; i < visibleIndices.length; i += 1) {
505505
const current: number = visibleIndices[i];
506506
const previous: number = visibleIndices[i - 1];

src/post/renderer.ts

Lines changed: 80 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -16,86 +16,97 @@ const ZERO_WIDTH_ZERO: string = "\u200b";
1616
const ZERO_WIDTH_ONE: string = "\u200c";
1717
const ZERO_WIDTH_SENTINEL: string = "\u200d";
1818

19-
export const calculateLabelStep = (count: number): number => {
20-
if (count <= 2) {
21-
return 1;
19+
export class Renderer {
20+
public static calculateLabelStep(count: number): number {
21+
if (count <= 2) {
22+
return 1;
23+
}
24+
25+
const totalGapWidth: number = CHART_WIDTH_PX - TICK_WIDTH_PX * count;
26+
if (totalGapWidth <= 0) {
27+
return count;
28+
}
29+
30+
const numerator: number = REQUIRED_GAP_PX * (count - 1);
31+
return Math.max(1, Math.ceil(numerator / totalGapWidth));
2232
}
2333

24-
const totalGapWidth: number = CHART_WIDTH_PX - TICK_WIDTH_PX * count;
25-
if (totalGapWidth <= 0) {
26-
return count;
34+
private static encodeHiddenLabel(index: number): string {
35+
const binary: string = index.toString(2);
36+
return (
37+
ZERO_WIDTH_SENTINEL +
38+
binary
39+
.split("")
40+
.map((digit: string): string =>
41+
digit === "0" ? ZERO_WIDTH_ZERO : ZERO_WIDTH_ONE,
42+
)
43+
.join("")
44+
);
2745
}
2846

29-
const numerator: number = REQUIRED_GAP_PX * (count - 1);
30-
return Math.max(1, Math.ceil(numerator / totalGapWidth));
31-
};
32-
33-
const encodeHiddenLabel = (index: number): string => {
34-
const binary: string = index.toString(2);
35-
return (
36-
ZERO_WIDTH_SENTINEL +
37-
binary
38-
.split("")
39-
.map((digit: string): string =>
40-
digit === "0" ? ZERO_WIDTH_ZERO : ZERO_WIDTH_ONE,
41-
)
42-
.join("")
43-
);
44-
};
47+
private static formatTimeLabels(
48+
times: z.TypeOf<typeof timesSchema>,
49+
): string[] {
50+
if (times.length === 0) {
51+
return [];
52+
}
4553

46-
const formatTimeLabels = (times: z.TypeOf<typeof timesSchema>): string[] => {
47-
if (times.length === 0) {
48-
return [];
49-
}
54+
const formattedTimes: string[] = times.map((d: Date): string =>
55+
d.toLocaleTimeString("en-GB", { hour12: false }),
56+
);
5057

51-
const formattedTimes: string[] = times.map((d: Date): string =>
52-
d.toLocaleTimeString("en-GB", { hour12: false }),
53-
);
58+
const labelStep: number = Renderer.calculateLabelStep(
59+
formattedTimes.length,
60+
);
61+
if (labelStep <= 1 || formattedTimes.length <= 2) {
62+
return formattedTimes;
63+
}
5464

55-
const labelStep: number = calculateLabelStep(formattedTimes.length);
56-
if (labelStep <= 1 || formattedTimes.length <= 2) {
57-
return formattedTimes;
58-
}
65+
const lastIndex: number = formattedTimes.length - 1;
66+
const totalInteriorPositions: number = Math.max(
67+
formattedTimes.length - 2,
68+
0,
69+
);
70+
const estimatedInteriorLabels: number = Math.max(
71+
Math.floor(lastIndex / labelStep) - 1,
72+
0,
73+
);
74+
const usableInteriorLabels: number = Math.min(
75+
totalInteriorPositions,
76+
estimatedInteriorLabels,
77+
);
5978

60-
const lastIndex: number = formattedTimes.length - 1;
61-
const totalInteriorPositions: number = Math.max(formattedTimes.length - 2, 0);
62-
const estimatedInteriorLabels: number = Math.max(
63-
Math.floor(lastIndex / labelStep) - 1,
64-
0,
65-
);
66-
const usableInteriorLabels: number = Math.min(
67-
totalInteriorPositions,
68-
estimatedInteriorLabels,
69-
);
70-
71-
const visibleInteriorIndices: Set<number> = new Set<number>();
72-
if (usableInteriorLabels > 0) {
73-
const spacing: number = totalInteriorPositions / (usableInteriorLabels + 1);
74-
for (let slot: number = 1; slot <= usableInteriorLabels; slot += 1) {
75-
const targetIndex: number = 1 + Math.round(slot * spacing);
76-
let clamped: number = Math.min(lastIndex - 1, Math.max(1, targetIndex));
77-
while (visibleInteriorIndices.has(clamped) && clamped < lastIndex - 1) {
78-
clamped += 1;
79+
const visibleInteriorIndices: Set<number> = new Set<number>();
80+
if (usableInteriorLabels > 0) {
81+
const spacing: number =
82+
totalInteriorPositions / (usableInteriorLabels + 1);
83+
for (let slot: number = 1; slot <= usableInteriorLabels; slot += 1) {
84+
const targetIndex: number = 1 + Math.round(slot * spacing);
85+
let clamped: number = Math.min(lastIndex - 1, Math.max(1, targetIndex));
86+
while (
87+
visibleInteriorIndices.has(clamped) &&
88+
clamped < lastIndex - 1
89+
) {
90+
clamped += 1;
91+
}
92+
visibleInteriorIndices.add(clamped);
7993
}
80-
visibleInteriorIndices.add(clamped);
8194
}
82-
}
8395

84-
return formattedTimes.map(
85-
(label: string, index: number, array: string[]): string => {
86-
if (
87-
index === 0 ||
88-
index === array.length - 1 ||
89-
visibleInteriorIndices.has(index)
90-
) {
91-
return label;
92-
}
93-
return encodeHiddenLabel(index);
94-
},
95-
);
96-
};
96+
return formattedTimes.map(
97+
(label: string, index: number, array: string[]): string => {
98+
if (
99+
index === 0 ||
100+
index === array.length - 1 ||
101+
visibleInteriorIndices.has(index)
102+
) {
103+
return label;
104+
}
105+
return Renderer.encodeHiddenLabel(index);
106+
},
107+
);
108+
}
97109

98-
export class Renderer {
99110
render(
100111
renderParamsList: z.TypeOf<typeof renderParamsListSchema>,
101112
metricsID: string,
@@ -133,7 +144,7 @@ ${charts}`;
133144
}
134145

135146
private formatTimes(times: z.TypeOf<typeof timesSchema>): string {
136-
return JSON.stringify(formatTimeLabels(times));
147+
return JSON.stringify(Renderer.formatTimeLabels(times));
137148
}
138149

139150
private formatYAxisRange(range?: string): string {

0 commit comments

Comments
 (0)