[Perl] DAQ system for the FAPG.
feat step through timeframes with +/- buttons
Pressing minus below range 1 steps down to the previous timeframe (day→hour). Pressing plus above max range steps up to the next timeframe (day→week). Buttons are only disabled at absolute extremes (hour/1 or year/max).
Changed files
roles/dashboard/public/js/dashboard/graph-controls.js
@@ -32,11 +32,11 @@
32
32
});
33
33
34
34
document.querySelectorAll('[data-range-adjustment="decrease"]').forEach(button => {
35
Removed:
button.disabled = range <= 1;
35
Added:
button.disabled = timeframe.selected() === "hour" && range <= 1;
36
36
});
37
37
38
38
document.querySelectorAll('[data-range-adjustment="increase"]').forEach(button => {
39
Removed:
button.disabled = range >= MAX_TIMEFRAME_RANGE;
39
Added:
button.disabled = timeframe.selected() === "year" && range >= MAX_TIMEFRAME_RANGE;
40
40
});
41
41
42
42
updateRawDownloadLinks(timeframe);
roles/dashboard/public/js/dashboard/timeframe.js
@@ -5,6 +5,7 @@
5
5
const initial = TIMEFRAMES[pageDefault] ? pageDefault : DEFAULT_TIMEFRAME;
6
6
let active = initial;
7
7
const ranges = Object.fromEntries(Object.keys(TIMEFRAMES).map(timeframe => [timeframe, 1]));
8
Added:
const order = Object.keys(TIMEFRAMES);
8
9
9
10
function selected() {
10
11
return TIMEFRAMES[active] ? active : DEFAULT_TIMEFRAME;
@@ -27,10 +28,26 @@
27
28
if (!TIMEFRAMES[timeframe]) return;
28
29
29
30
active = timeframe;
30
Removed:
ranges[timeframe] = Math.min(
31
Removed:
MAX_TIMEFRAME_RANGE,
32
Removed:
Math.max(1, (ranges[timeframe] || 1) + adjustment)
33
Removed:
);
31
Added:
const current = ranges[timeframe] || 1;
32
Added:
const next = current + adjustment;
33
Added:
34
Added:
if (next < 1) {
35
Added:
// Step down to previous timeframe at range 1
36
Added:
const index = order.indexOf(active);
37
Added:
if (index > 0) {
38
Added:
active = order[index - 1];
39
Added:
ranges[active] = 1;
40
Added:
}
41
Added:
} else if (next > MAX_TIMEFRAME_RANGE) {
42
Added:
// Step up to next timeframe at range 1
43
Added:
const index = order.indexOf(active);
44
Added:
if (index < order.length - 1) {
45
Added:
active = order[index + 1];
46
Added:
ranges[active] = 1;
47
Added:
}
48
Added:
} else {
49
Added:
ranges[timeframe] = next;
50
Added:
}
34
51
}
35
52
};
36
53
}
roles/dashboard/t/js/frontend.test.cjs
@@ -57,6 +57,49 @@
57
57
assert.equal(timeframeToHours({ selected: () => "unknown", range: () => 1 }), 24);
58
58
});
59
59
60
Added:
test("adjust steps to next/previous timeframe at range bounds", async () => {
61
Added:
// We need a DOM element for createTimeframeState
62
Added:
const { JSDOM } = await import("jsdom").catch(() => ({ JSDOM: null }));
63
Added:
64
Added:
// Test the logic directly using a minimal mock
65
Added:
const module = await dashboardModule("timeframe.js");
66
Added:
const { createTimeframeState } = module.namespace;
67
Added:
68
Added:
// createTimeframeState needs document.querySelector — skip if unavailable
69
Added:
if (typeof globalThis.document === "undefined") {
70
Added:
// Patch minimal document
71
Added:
globalThis.document = { querySelector: () => ({ dataset: { defaultTimeframe: "day" } }) };
72
Added:
}
73
Added:
74
Added:
const state = createTimeframeState();
75
Added:
assert.equal(state.selected(), "day");
76
Added:
assert.equal(state.range(), 1);
77
Added:
78
Added:
// Decrease below range 1 should step down to hour
79
Added:
state.adjust("day", -1);
80
Added:
assert.equal(state.selected(), "hour");
81
Added:
assert.equal(state.range(), 1);
82
Added:
83
Added:
// Decrease at hour/1 should stay at hour (lowest)
84
Added:
state.adjust("hour", -1);
85
Added:
assert.equal(state.selected(), "hour");
86
Added:
assert.equal(state.range(), 1);
87
Added:
88
Added:
// Increase from hour should increment range
89
Added:
state.adjust("hour", 1);
90
Added:
assert.equal(state.selected(), "hour");
91
Added:
assert.equal(state.range(), 2);
92
Added:
93
Added:
// Select day, then increase past MAX should step to week
94
Added:
state.select("day");
95
Added:
state.ranges.day = 24;
96
Added:
state.adjust("day", 1);
97
Added:
assert.equal(state.selected(), "week");
98
Added:
assert.equal(state.range(), 1);
99
Added:
100
Added:
delete globalThis.document;
101
Added:
});
102
Added:
60
103
test("tooltipOptions returns independent callback configuration", async () => {
61
104
const module = await dashboardModule("chart-options.js");
62
105
const { TOOLTIP_DEFAULTS, tooltipOptions } = module.namespace;