Skip to content
Closed
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
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ cargo clippy --all-targets --all-features -- -D warnings
### Frontend

We use Biome for linting and formatting of the frontend code, and TypeScript
to typecheck it.
to typecheck it. TypeScript checks optional properties exactly: omit an unset option so library defaults
remain intact; use an explicit `| undefined` only when the property contract supports a present,
undefined value.

```bash
npm install # once
Expand Down
118 changes: 63 additions & 55 deletions frontend/src/apexcharts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { ApexChart, ApexOptions } from "apexcharts";
import type {
ApexChart,
ApexOptions,
ApexTooltip,
ApexXAxis,
ApexYAxis,
} from "apexcharts";
import ApexCharts from "apexcharts";
import { type ReferenceLine, read_chart_data } from "./chart_data.ts";
import {
Expand Down Expand Up @@ -225,12 +231,13 @@ const sqlpage_chart = (() => {
y = y.map((value) => new Date(value ?? 0).getTime());
else x = new Date(x ?? 0);
}
const fillColor = named_color(color);
point_series.data.push({
x,
y,
z,
link,
fillColor: named_color(color),
...(z === undefined ? {} : { z }),
...(link === undefined ? {} : { link }),
...(fillColor === undefined ? {} : { fillColor }),
});
}

Expand Down Expand Up @@ -297,6 +304,53 @@ const sqlpage_chart = (() => {
? ({ seriesIndex }: PointIndex) => points[seriesIndex]?.link
: ({ seriesIndex, dataPointIndex }: PointIndex) =>
aligned_series[seriesIndex]?.data[dataPointIndex]?.link;
// Unlike absent options, these two explicit undefined values disable
// ApexCharts defaults. Its types do not describe that runtime contract.
const xaxis: Omit<ApexXAxis, "type"> & { type: ApexXAxis["type"] } = {
type: xaxis_type,
tooltip: { enabled: false },
labels: { datetimeUTC: false },
};
if (data.xmin !== undefined) xaxis.min = data.xmin;
if (data.xmax !== undefined) xaxis.max = data.xmax;
if (axis_titles.x !== undefined) xaxis.title = { text: axis_titles.x };
// Numeric axes count intervals; category and time axes use tickAmount
// as a target for label density. Zero leaves the library's default.
if (data.xticks) xaxis.tickAmount = data.xticks;

const yaxis: ApexYAxis = { logarithmic: data.logarithmic };
if (data.ymin !== undefined) yaxis.min = data.ymin;
if (data.ymax !== undefined) yaxis.max = data.ymax;
if (data.ystep !== undefined) yaxis.stepSize = data.ystep;
if (data.yticks !== undefined) yaxis.tickAmount = data.yticks;
if (axis_titles.y !== undefined) yaxis.title = { text: axis_titles.y };

const tooltip: Omit<ApexTooltip, "custom"> & {
custom: ApexTooltip["custom"];
} = {
fillSeriesColor: false,
interactive: has_point_links,
// A rangeBar's default custom tooltip would replace our linked title.
custom:
chart_type === "bubble" || chart_type === "scatter"
? axisTooltip(aligned_series, axis_titles, formatValue)
: undefined,
...(has_point_links && text_x_values
? {
x: {
formatter: (value, args) =>
linkTooltipValue(value, args && pointLink(args)),
},
}
: {}),
y: {
formatter: (value, args) =>
linkTooltipValue(
formatValue(value),
args?.seriesIndex !== undefined ? pointLink(args) : undefined,
),
},
};
const options: ApexOptions = {
annotations: {
[`${value_axis}axis`]: reference_lines(
Expand All @@ -315,7 +369,7 @@ const sqlpage_chart = (() => {
chart: {
// The query may name any type ApexCharts draws, not only the ones
// the component documents.
type: chart_type as ApexChart["type"],
type: chart_type as NonNullable<ApexChart["type"]>,
fontFamily: "inherit",
background: "transparent",
parentHeightOffset: 0,
Expand Down Expand Up @@ -364,62 +418,16 @@ const sqlpage_chart = (() => {
lineCap: "round",
curve: "smooth",
},
xaxis: {
tooltip: {
enabled: false,
},
min: data.xmin,
max: data.xmax,
title: {
text: axis_titles.x,
},
type: xaxis_type,
labels: {
datetimeUTC: false,
},
// Numeric axes count intervals; category and time axes use tickAmount
// as a target for label density.
tickAmount: data.xticks || undefined,
},
yaxis: {
logarithmic: data.logarithmic,
min: data.ymin,
max: data.ymax,
stepSize: data.ystep,
tickAmount: data.yticks,
title: {
text: axis_titles.y,
},
},
xaxis: xaxis as ApexXAxis,
yaxis,
markers: {
size: data.marker ?? 0,
strokeWidth: 0,
hover: {
sizeOffset: 5,
},
},
tooltip: {
fillSeriesColor: false,
interactive: has_point_links,
custom:
chart_type === "bubble" || chart_type === "scatter"
? axisTooltip(aligned_series, axis_titles, formatValue)
: undefined,
x: {
formatter:
has_point_links && text_x_values
? (value, args) =>
linkTooltipValue(value, args && pointLink(args))
: undefined,
},
y: {
formatter: (value, args) =>
linkTooltipValue(
formatValue(value),
args?.seriesIndex !== undefined ? pointLink(args) : undefined,
),
},
},
tooltip: tooltip as ApexTooltip,
plotOptions: {
bar: {
horizontal: data.horizontal || chart_type === "rangeBar",
Expand All @@ -429,7 +437,7 @@ const sqlpage_chart = (() => {
},
colors,
// ApexCharts draws a numeric series name but declares only a string.
series: series as ApexOptions["series"],
series: series as NonNullable<ApexOptions["series"]>,
};
if (is_pie) options.labels = pie_labels;
const chart = new ApexCharts(chartContainer, options) as RenderedChart;
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/tomselect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ function sqlpage_select_dropdown_individual(s: HTMLSelectElement) {
// This is a workaround to fix that
const is_focused = s === document.activeElement;

const load = sqlpage_load_options_source(s.dataset.options_source);
const tom = new TomSelect(s, {
load: sqlpage_load_options_source(s.dataset.options_source),
...(load ? { load } : {}),
valueField: "value",
labelField: "label",
searchField: ["label"],
Expand Down
2 changes: 1 addition & 1 deletion tests/end-to-end/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default defineConfig({
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
...(process.env.CI ? { workers: 1 } : {}),
reporter: "html",
use: {
trace: "on-first-retry",
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"types": [],
"noImplicitReturns": true
"noImplicitReturns": true,
"exactOptionalPropertyTypes": true
},
"include": ["frontend/src"]
}
Loading