Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v0.46.1

- Chart data points can now include a `link`, which is shown as a clickable link in the point's tooltip.
- Upgraded the bundled ApexCharts from v5.13.0 to [v7.1.0](https://github.com/apexcharts/apexcharts.js/releases/tag/v7.1.0) and the Tabler core from v1.4.0 to v1.5.0. The ApexCharts upgrade fixes logarithmic-axis scaling, stacked baselines on irregular data, and annotations on charts with no data, and ships a smaller default bundle.
- Fixed modal dialog boxes appearing behind their backdrop, which made them impossible to close by clicking their close button. Tabler 1.5 sets `contain: layout` on the page container, which broke the fixed positioning of modals rendered inside it; modals are now moved to the top level of the page, as recommended by Bootstrap.
- Fixed a regression introduced in v0.46 that could replace a variable with `NULL` while building a value that also used database expressions and `sqlpage.*` functions. For example, this API request could lose `john.doe` and produce a URL ending at `https://api.example.com/`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S
('label', 'An alias for parameter "x". On a row that draws a reference line, the text to display next to the line.', 'TEXT', FALSE, TRUE),
('value', 'An alias for parameter "y"', 'REAL', FALSE, TRUE),
('series', 'If multiple series are represented and share the same y-axis, this parameter can be used to distinguish between them.', 'TEXT', FALSE, TRUE),
('link', 'Adds a clickable link to this point in its tooltip.', 'URL', FALSE, TRUE),
('yline', 'Draws a reference line across the chart at this value of the y axis instead of plotting a point, to show a limit such as a quota or an alarm threshold. Not drawn if it falls outside of the axis, so set ymax when the limit is above the data.', 'REAL', FALSE, TRUE),
('yline_end', 'Makes the yline a band instead of a line, reaching to this value.', 'REAL', FALSE, TRUE),
('xline', 'Draws a reference line across the chart at this position of the x axis instead of plotting a point, to mark an event such as a deployment. A date or a timestamp when time is set, otherwise one of the x values.', 'TEXT', FALSE, TRUE),
Expand Down Expand Up @@ -789,10 +790,10 @@ The `color` property sets the color of each series separately, in order.
{ "series": "PostgreSQL", "x": "2010", "y": 65},{ "series": "SQLite", "x": "2010", "y": 62},{ "series": "MySQL", "x": "2010", "y": 83},
{ "series": "PostgreSQL", "x": "2020", "y": 73},{ "series": "SQLite", "x": "2020", "y": 38},{ "series": "MySQL", "x": "2020", "y": 87}
]')),
('chart', 'A timeline displaying events with a start and an end date',
('chart', 'A timeline displaying events with a start and an end date. A data row can include a `link` to make it available as a clickable action in the tooltip.',
json('[
{"component":"chart", "title": "Project Timeline", "type": "rangeBar", "time": true, "color": ["teal", "cyan"], "labels": true, "xmin": "2021-12-28", "xmax": "2022-01-04" },
{"series": "Phase 1", "label": "Operations", "value": ["2021-12-29", "2022-01-02"]},
{"series": "Phase 1", "label": "Operations", "value": ["2021-12-29", "2022-01-02"], "link": "/examples/chart.sql?phase=1"},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when I go to http://127.0.0.1:7071/component.sql?component=chart I see that there is an open link button, but the tooltip dissappears when I try to click it. also the link seems visually disabled

Screenshot_2026-09-07_13-05-43

{"series": "Phase 2", "label": "Operations", "value": ["2022-01-03", "2022-01-04"]},
{"series": "Yearly maintenance", "label": "Maintenance", "value": ["2022-01-01", "2022-01-03"]}
]')),
Expand Down
42 changes: 34 additions & 8 deletions sqlpage/apexcharts.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ sqlpage_chart = (() => {
};

/** @typedef {number|string|Date} XValue */
/** @typedef { {x:XValue, y:number|null, z?:number, fillColor?:string} } ChartPoint */
/** @typedef { {x:XValue, y:number|null, z?:number, fillColor?:string, link?:string} } ChartPoint */
/** @typedef { {name:string, data:ChartPoint[]} } ChartSeries */
/** @typedef { { [name:string]: ChartSeries } } Series */

Expand Down Expand Up @@ -182,7 +182,7 @@ sqlpage_chart = (() => {
const reference_rows = data.points.filter((row) => !Array.isArray(row));
/** @type { Series } */
const series_map = {};
for (const [name, old_x, old_y, color, z] of points) {
for (const [name, old_x, old_y, color, z, link] of points) {
series_map[name] = series_map[name] || { name, data: [] };
let x = old_x;
let y = old_y;
Expand All @@ -192,7 +192,13 @@ sqlpage_chart = (() => {
y = y.map((y) => new Date(y).getTime());
else x = new Date(x);
}
series_map[name].data.push({ x, y, z, fillColor: named_color(color) });
series_map[name].data.push({
x,
y,
z,
link,
fillColor: named_color(color),
});
}
if (data.xmin == null) data.xmin = undefined;
if (data.xmax == null) data.xmax = undefined;
Expand Down Expand Up @@ -332,8 +338,9 @@ sqlpage_chart = (() => {
},
tooltip: {
fillSeriesColor: false,
custom:
chart_type === "bubble" || chart_type === "scatter"
custom: points.some((point) => point[5])
? (args) => chartTooltip(args, points)
: chart_type === "bubble" || chart_type === "scatter"
Comment on lines +341 to +343

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be skipping the formatter. when you check on the live website, the dates are formatted

Image

? bubbleTooltip
: undefined,
y: {
Expand Down Expand Up @@ -372,9 +379,11 @@ sqlpage_chart = (() => {
c.removeAttribute("data-pre-init");
}

function bubbleTooltip({ seriesIndex, dataPointIndex, w }) {
const { name, data } = w.config.series[seriesIndex];
const point = data[dataPointIndex];
function chartTooltip({ seriesIndex, dataPointIndex, w }, raw_points) {
const series = w.config.series[seriesIndex];
const name = series?.name || w.config.labels?.[dataPointIndex] || "";
const point = series?.data?.[dataPointIndex] || {};
const link = point.link || raw_points[dataPointIndex]?.[5];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the screenshot also shows that rows without links still show the Open Link hyperlink. I think it's leaking into other rows from here


const tooltip = document.createElement("div");
tooltip.className = "apexcharts-tooltip-text";
Expand Down Expand Up @@ -404,9 +413,26 @@ sqlpage_chart = (() => {
axisValue.appendChild(valueSpan);
tooltip.appendChild(axisValue);
}
add_link_to_tooltip(tooltip, link);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] snake case?

return tooltip.outerHTML;
}

function bubbleTooltip(args) {
return chartTooltip(args, []);
}

/** @param {HTMLElement} tooltip @param {string|undefined} link */
function add_link_to_tooltip(tooltip, link) {
if (!link) return;
const linkContainer = document.createElement("div");
linkContainer.className = "apexcharts-tooltip-y-group";
const anchor = document.createElement("a");
anchor.href = link;
anchor.textContent = "Open link";
linkContainer.appendChild(anchor);
tooltip.appendChild(linkContainer);
}

return sqlpage_chart;
})();

Expand Down
5 changes: 3 additions & 2 deletions sqlpage/templates/chart.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@
{{~ stringify (default series (default ../title "")) ~}},
{{~ stringify (default x label) ~}},
{{~ stringify (default y value) ~}}
{{~#if (or color z)}}, {{~ stringify color ~}} {{~/if~}}
{{~#if z}}, {{~ stringify z ~}} {{~/if~}}
{{~#if (or color z link)}}, {{~ stringify color ~}} {{~/if~}}
{{~#if (or z link)}}, {{~ stringify z ~}} {{~/if~}}
{{~#if link}}, {{~ stringify link ~}} {{~/if~}}
]
{{~/if~}}
{{~/each_row~}}
Expand Down
1 change: 1 addition & 0 deletions tests/components/chart_point_serialization.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ SELECT 'plain' AS x, '1' AS y;
SELECT 'colored' AS x, '2' AS y, 'red' AS color;
SELECT 'sized' AS x, '3' AS y, '30' AS z;
SELECT 'both' AS x, '4' AS y, 'green' AS color, '40' AS z;
SELECT 'linked' AS x, '5' AS y, '/edit.sql?id=5' AS link;
SELECT '70' AS yline, 'limit' AS label, 'orange' AS color;
5 changes: 5 additions & 0 deletions tests/end-to-end/fixtures/chart/link.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SELECT 'chart' AS component, 'test-chart' AS id, 'Chart test fixture' AS title,
'rangeBar' AS type, TRUE AS time;
SELECT 'Design' AS series, 'Alice' AS label,
'2024-03-01' AS value, '2024-03-05' AS value,
'/workpackage_edit.sql?workpackage_name=Design' AS link;
12 changes: 12 additions & 0 deletions tests/end-to-end/fixtures/chart/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ test("leaves a rangeBar chart on a category axis alone", async ({ page }) => {
expect(chart.shapes).toHaveLength(2);
});

test("shows a data point link in its tooltip", async ({ page }) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be worth to test multiple different kinds of charts since not all of them use the rows in the same way. claude is pointing out that there may be a code smell with pie charts

await renderChart(page, "link");

await page.locator("#test-chart .apexcharts-rangebar-area").hover();
const link = page.locator("#test-chart .apexcharts-tooltip a");
await expect(link).toHaveText("Open link");
await expect(link).toHaveAttribute(
"href",
"/workpackage_edit.sql?workpackage_name=Design",
);
});

test("leaves a treemap chart alone", async ({ page }) => {
const chart = await renderChart(page, "treemap");

Expand Down