From 375df59c3a674a0b274b2c71509899a6cb917f8c Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 7 Apr 2026 14:25:26 +0200 Subject: [PATCH 1/3] Add show_legend option to chart component --- .../sqlpage/migrations/01_documentation.sql | 3 ++- sqlpage/apexcharts.js | 3 +++ sqlpage/templates/chart.handlebars | 1 + tests/end-to-end/official-site.spec.ts | 11 +++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/official-site/sqlpage/migrations/01_documentation.sql b/examples/official-site/sqlpage/migrations/01_documentation.sql index a5f3bde38..ddac5786f 100644 --- a/examples/official-site/sqlpage/migrations/01_documentation.sql +++ b/examples/official-site/sqlpage/migrations/01_documentation.sql @@ -657,6 +657,7 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S ('color', 'The name of a color in which to display the chart. If there are multiple series in the chart, this parameter can be repeated multiple times.', 'COLOR', TRUE, TRUE), ('stacked', 'Whether to cumulate values from different series.', 'BOOLEAN', TRUE, TRUE), ('toolbar', 'Whether to display a toolbar at the top right of the chart, that offers downloading the data as CSV.', 'BOOLEAN', TRUE, TRUE), + ('show_legend', 'Whether to display the legend listing all chart series. Defaults to true.', 'BOOLEAN', TRUE, TRUE), ('logarithmic', 'Display the y-axis in logarithmic scale.', 'BOOLEAN', TRUE, TRUE), ('horizontal', 'Displays a bar chart with horizontal bars instead of vertical ones.', 'BOOLEAN', TRUE, TRUE), ('height', 'Height of the chart, in pixels. By default: 250', 'INTEGER', TRUE, TRUE), @@ -702,7 +703,7 @@ INSERT INTO example(component, description, properties) VALUES {"series": "Asia", "label": "China", "value": 20}, {"series": "Asia", "label": "Japan", "value": 10} ]')), - ('chart', 'A bar chart with multiple series.', json('[{"component":"chart", "title": "Expenses", "type": "bar", "stacked": true, "toolbar": true, "ystep": 10}, '|| + ('chart', 'A bar chart with multiple series.', json('[{"component":"chart", "title": "Expenses", "type": "bar", "stacked": true, "toolbar": true, "show_legend": false, "ystep": 10}, '|| '{"series": "Marketing", "x": 2021, "value": 35}, '|| '{"series": "Marketing", "x": 2022, "value": 15}, '|| '{"series": "Human resources", "x": 2021, "value": 30}, '|| diff --git a/sqlpage/apexcharts.js b/sqlpage/apexcharts.js index a0f46367b..3ea8609e0 100644 --- a/sqlpage/apexcharts.js +++ b/sqlpage/apexcharts.js @@ -155,6 +155,9 @@ sqlpage_chart = (() => { theme: { palette: "palette4", }, + legend: { + show: data.show_legend !== false, + }, dataLabels: { enabled: !!data.labels, dropShadow: { diff --git a/sqlpage/templates/chart.handlebars b/sqlpage/templates/chart.handlebars index 667e5eae8..34d3256e8 100644 --- a/sqlpage/templates/chart.handlebars +++ b/sqlpage/templates/chart.handlebars @@ -31,6 +31,7 @@ "xmax": {{stringify xmax}}, "ymax": {{stringify ymax}}, "toolbar": {{stringify toolbar}}, + "show_legend": {{stringify show_legend}}, "logarithmic": {{stringify logarithmic}}, "horizontal": {{stringify horizontal}}, "stacked": {{stringify stacked}}, diff --git a/tests/end-to-end/official-site.spec.ts b/tests/end-to-end/official-site.spec.ts index eda20e7e0..8b2c6b3fd 100644 --- a/tests/end-to-end/official-site.spec.ts +++ b/tests/end-to-end/official-site.spec.ts @@ -24,6 +24,17 @@ test("chart", async ({ page }) => { await expect(page.locator(".apexcharts-canvas").first()).toBeVisible(); }); +test("chart supports hiding legend", async ({ page }) => { + await page.goto(`${BASE}/documentation.sql?component=chart#component`); + + const expensesChart = page.locator(".card", { + has: page.getByRole("heading", { name: "Expenses" }), + }); + + await expect(expensesChart.locator(".apexcharts-canvas")).toBeVisible(); + await expect(expensesChart.locator(".apexcharts-legend")).toBeHidden(); +}); + test("map", async ({ page }) => { await page.goto(`${BASE}/documentation.sql?component=map#component`); await expect(page.getByText("Loading...")).not.toBeVisible(); From 4d8442bc35707d9ece52c7e824a5386b9325cf2c Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 7 Apr 2026 16:19:22 +0200 Subject: [PATCH 2/3] Fix show_legend for SQLite boolean values --- .../examples/chart_hide_legend.sql | 13 ++++++++++++ sqlpage/apexcharts.js | 21 ++++++++++++++++++- tests/end-to-end/official-site.spec.ts | 13 ++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 examples/official-site/examples/chart_hide_legend.sql diff --git a/examples/official-site/examples/chart_hide_legend.sql b/examples/official-site/examples/chart_hide_legend.sql new file mode 100644 index 000000000..6d7024361 --- /dev/null +++ b/examples/official-site/examples/chart_hide_legend.sql @@ -0,0 +1,13 @@ +SELECT + 'chart' AS component, + 'Legend Hidden' AS title, + 'line' AS type, + FALSE AS show_legend; + +SELECT 'Marketing' AS series, 2023 AS x, 30 AS y +UNION ALL +SELECT 'Marketing', 2024, 45 +UNION ALL +SELECT 'Sales', 2023, 35 +UNION ALL +SELECT 'Sales', 2024, 50; diff --git a/sqlpage/apexcharts.js b/sqlpage/apexcharts.js index 3ea8609e0..c43981191 100644 --- a/sqlpage/apexcharts.js +++ b/sqlpage/apexcharts.js @@ -1,6 +1,25 @@ /* !include https://cdn.jsdelivr.net/npm/apexcharts@5.3.6/dist/apexcharts.min.js */ sqlpage_chart = (() => { + /** + * Coerces SQL values to booleans. + * SQLite often returns booleans as 0/1 integers. + * @param {unknown} value + * @param {boolean} defaultValue + * @returns {boolean} + */ + function sqlBoolean(value, defaultValue = false) { + if (value == null) return defaultValue; + if (typeof value === "boolean") return value; + if (typeof value === "number") return value !== 0; + if (typeof value === "string") { + const normalized = value.trim().toLowerCase(); + if (normalized === "0" || normalized === "false") return false; + if (normalized === "1" || normalized === "true") return true; + } + return Boolean(value); + } + function sqlpage_chart() { for (const c of document.querySelectorAll("[data-pre-init=chart]")) { try { @@ -156,7 +175,7 @@ sqlpage_chart = (() => { palette: "palette4", }, legend: { - show: data.show_legend !== false, + show: sqlBoolean(data.show_legend, true), }, dataLabels: { enabled: !!data.labels, diff --git a/tests/end-to-end/official-site.spec.ts b/tests/end-to-end/official-site.spec.ts index 8b2c6b3fd..539431311 100644 --- a/tests/end-to-end/official-site.spec.ts +++ b/tests/end-to-end/official-site.spec.ts @@ -35,6 +35,19 @@ test("chart supports hiding legend", async ({ page }) => { await expect(expensesChart.locator(".apexcharts-legend")).toBeHidden(); }); +test("chart supports hiding legend with SQL boolean false", async ({ + page, +}) => { + await page.goto(`${BASE}/examples/chart_hide_legend.sql`); + + const chart = page.locator(".card", { + has: page.getByRole("heading", { name: "Legend Hidden" }), + }); + + await expect(chart.locator(".apexcharts-canvas")).toBeVisible(); + await expect(chart.locator(".apexcharts-legend")).toBeHidden(); +}); + test("map", async ({ page }) => { await page.goto(`${BASE}/documentation.sql?component=map#component`); await expect(page.getByText("Loading...")).not.toBeVisible(); From 0f91b1af3daffd2eddd74f52d0a8af48a25545e0 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 7 Apr 2026 17:00:21 +0200 Subject: [PATCH 3/3] remove ai cruft --- .../examples/chart_hide_legend.sql | 13 ------------ sqlpage/apexcharts.js | 21 +------------------ tests/end-to-end/official-site.spec.ts | 13 ------------ 3 files changed, 1 insertion(+), 46 deletions(-) delete mode 100644 examples/official-site/examples/chart_hide_legend.sql diff --git a/examples/official-site/examples/chart_hide_legend.sql b/examples/official-site/examples/chart_hide_legend.sql deleted file mode 100644 index 6d7024361..000000000 --- a/examples/official-site/examples/chart_hide_legend.sql +++ /dev/null @@ -1,13 +0,0 @@ -SELECT - 'chart' AS component, - 'Legend Hidden' AS title, - 'line' AS type, - FALSE AS show_legend; - -SELECT 'Marketing' AS series, 2023 AS x, 30 AS y -UNION ALL -SELECT 'Marketing', 2024, 45 -UNION ALL -SELECT 'Sales', 2023, 35 -UNION ALL -SELECT 'Sales', 2024, 50; diff --git a/sqlpage/apexcharts.js b/sqlpage/apexcharts.js index c43981191..7e71ea8f3 100644 --- a/sqlpage/apexcharts.js +++ b/sqlpage/apexcharts.js @@ -1,25 +1,6 @@ /* !include https://cdn.jsdelivr.net/npm/apexcharts@5.3.6/dist/apexcharts.min.js */ sqlpage_chart = (() => { - /** - * Coerces SQL values to booleans. - * SQLite often returns booleans as 0/1 integers. - * @param {unknown} value - * @param {boolean} defaultValue - * @returns {boolean} - */ - function sqlBoolean(value, defaultValue = false) { - if (value == null) return defaultValue; - if (typeof value === "boolean") return value; - if (typeof value === "number") return value !== 0; - if (typeof value === "string") { - const normalized = value.trim().toLowerCase(); - if (normalized === "0" || normalized === "false") return false; - if (normalized === "1" || normalized === "true") return true; - } - return Boolean(value); - } - function sqlpage_chart() { for (const c of document.querySelectorAll("[data-pre-init=chart]")) { try { @@ -175,7 +156,7 @@ sqlpage_chart = (() => { palette: "palette4", }, legend: { - show: sqlBoolean(data.show_legend, true), + show: data.show_legend === null || !!data.show_legend, }, dataLabels: { enabled: !!data.labels, diff --git a/tests/end-to-end/official-site.spec.ts b/tests/end-to-end/official-site.spec.ts index 539431311..8b2c6b3fd 100644 --- a/tests/end-to-end/official-site.spec.ts +++ b/tests/end-to-end/official-site.spec.ts @@ -35,19 +35,6 @@ test("chart supports hiding legend", async ({ page }) => { await expect(expensesChart.locator(".apexcharts-legend")).toBeHidden(); }); -test("chart supports hiding legend with SQL boolean false", async ({ - page, -}) => { - await page.goto(`${BASE}/examples/chart_hide_legend.sql`); - - const chart = page.locator(".card", { - has: page.getByRole("heading", { name: "Legend Hidden" }), - }); - - await expect(chart.locator(".apexcharts-canvas")).toBeVisible(); - await expect(chart.locator(".apexcharts-legend")).toBeHidden(); -}); - test("map", async ({ page }) => { await page.goto(`${BASE}/documentation.sql?component=map#component`); await expect(page.getByText("Loading...")).not.toBeVisible();