Skip to content

Commit 6fa8f48

Browse files
committed
improve ux of bench groups with filters
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent a31a0db commit 6fa8f48

8 files changed

Lines changed: 703 additions & 23 deletions

File tree

benchmarks-website/server/src/html/landing.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use maud::PreEscaped;
1313
use maud::html;
1414

1515
use super::render::escape_json_for_script;
16+
use super::render::filter_icon;
1617
use super::summary::summary_markup;
1718
use super::toolbar::per_chart_toolbar;
1819
use super::toolbar::range_strip;
@@ -48,7 +49,7 @@ pub(super) struct LandingGroup {
4849
/// Render the landing-page body — one `<section>` per group, each wrapping a
4950
/// `<details>` disclosure. The `chart-data-N` script ids are globally
5051
/// indexed so `chart-init.js` can find every payload by integer.
51-
pub(super) fn landing_body(groups: &[LandingGroup]) -> Markup {
52+
pub(super) fn landing_body(groups: &[LandingGroup], universe: &api::FilterUniverse) -> Markup {
5253
if groups.is_empty() {
5354
return html! {
5455
p.empty { "No data ingested yet." }
@@ -73,6 +74,7 @@ pub(super) fn landing_body(groups: &[LandingGroup]) -> Markup {
7374
}
7475
}
7576
(summary_markup(group.summary.as_ref()))
77+
(per_group_toolbar(universe))
7678
div.chart-grid {
7779
@for (chart_idx, link) in group.chart_links.iter().enumerate() {
7880
@let idx = idx_iter.next().expect("indices match charts");
@@ -88,6 +90,102 @@ pub(super) fn landing_body(groups: &[LandingGroup]) -> Markup {
8890
}
8991
}
9092

93+
/// Render the per-group toolbar that lets the user override the global filter
94+
/// and Y-axis scale across every chart in the group. The toolbar is a sibling
95+
/// of `.chart-grid`; CSS hides it when the enclosing `<details>` is closed,
96+
/// mirroring the rule that hides `.chart-grid` itself.
97+
///
98+
/// Layout: Y-axis buttons on the left, a centered "Filter series" dropdown
99+
/// trigger, and a Reset button on the right. The dropdown panel contains
100+
/// engine and format macro chips (which expand to "toggle every series with
101+
/// this engine/format") plus a series row whose chips are populated by JS as
102+
/// charts in the group hydrate and surface their `payload.series_meta`.
103+
///
104+
/// Resolution layering (driven by `chart-init.js`):
105+
/// - Per-card legend overrides win over everything.
106+
/// - The per-group filter (`hiddenSeries`) hides next.
107+
/// - The global filter hides last.
108+
/// - The Y-axis pass skips charts where the user previously clicked the
109+
/// per-chart Y toolbar (`canvas.__bench_y_user_set`).
110+
fn per_group_toolbar(universe: &api::FilterUniverse) -> Markup {
111+
html! {
112+
section.group-toolbar data-role="group-toolbar" {
113+
div.toolbar-group.group-toolbar-y role="group" aria-label="Group Y-axis scale" {
114+
span.toolbar-label { "Y" }
115+
// Linear is the resting default (matches each chart's
116+
// own default) so it ships highlighted; the JS keeps it
117+
// lit while the per-group Y is unset or explicitly linear.
118+
button.toolbar-btn.toolbar-btn--active
119+
type="button" data-group-y="linear" { "linear" }
120+
button.toolbar-btn type="button" data-group-y="log" { "log" }
121+
}
122+
div.group-filter-dropdown data-role="group-filter-dropdown" {
123+
button.control-btn.filter-trigger.group-filter-trigger
124+
type="button"
125+
data-role="group-filter-trigger"
126+
aria-haspopup="true"
127+
aria-expanded="false" {
128+
(filter_icon())
129+
span { "Filter series" }
130+
}
131+
div.filter-panel.group-filter-panel data-role="group-filter-panel" hidden {
132+
(group_macro_row("Engine", "engine", &universe.engines))
133+
(group_macro_row("Format", "format", &universe.formats))
134+
div.global-filter-row.group-series-row {
135+
span.global-filter-label { "Series" }
136+
button.filter-chip.filter-chip--all
137+
type="button"
138+
data-group-filter="series"
139+
data-value="*"
140+
aria-pressed="false" {
141+
"all"
142+
}
143+
// Series chips hydrate client-side once a chart in this
144+
// group exposes its `payload.series_meta`. Until then
145+
// the row only shows the Engine/Format macros above.
146+
div.group-series-chips data-role="group-series-chips" {}
147+
}
148+
}
149+
}
150+
button.group-toolbar-reset
151+
type="button"
152+
data-role="group-toolbar-reset" {
153+
"Reset group"
154+
}
155+
}
156+
}
157+
}
158+
159+
/// Render an engine/format macro row inside the per-group filter panel. The
160+
/// macro chip click bulk-toggles every known series whose `engine`/`format`
161+
/// matches; the chip's active state reflects "every matching series is
162+
/// currently visible". `data-group-filter` distinguishes these chips from the
163+
/// global filter's `data-filter` ones so the click handler can route them to
164+
/// the per-group state.
165+
fn group_macro_row(label: &str, dim: &str, universe: &[String]) -> Markup {
166+
html! {
167+
div.global-filter-row.group-macro-row {
168+
span.global-filter-label { (label) }
169+
button.filter-chip.filter-chip--all
170+
type="button"
171+
data-group-filter=(dim)
172+
data-value="*"
173+
aria-pressed="false" {
174+
"all"
175+
}
176+
@for value in universe {
177+
button.filter-chip.filter-chip--active
178+
type="button"
179+
data-group-filter=(dim)
180+
data-value=(value)
181+
aria-pressed="true" {
182+
(value)
183+
}
184+
}
185+
}
186+
}
187+
}
188+
91189
/// Render the small ⓘ info icon that surfaces the group's editorial
92190
/// description on hover and on focus. The CSS-only tooltip uses a
93191
/// `data-tooltip` attribute so it shows below the icon (see `style.css`'s

benchmarks-website/server/src/html/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ async fn landing(State(state): State<AppState>, Query(ui): Query<UiQuery>) -> Re
207207
render_page(
208208
"bench.vortex.dev",
209209
"Vortex benchmarks (v3 alpha)",
210-
landing_body(&groups),
210+
landing_body(&groups, &universe),
211211
scripts,
212212
Some(&universe),
213213
&filter,

benchmarks-website/server/src/html/render.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,26 @@ fn site_header(universe: Option<&api::FilterUniverse>, filter: &FilterState) ->
9090
header.sticky-header {
9191
div.header-content {
9292
div.header-left {
93+
button.control-btn.nav-mobile-toggle
94+
type="button"
95+
data-role="nav-mobile-toggle"
96+
aria-haspopup="true"
97+
aria-expanded="false"
98+
aria-controls="bench-nav-controls"
99+
aria-label="Toggle navigation menu" {
100+
(hamburger_icon())
101+
}
93102
a.logo-link href="/" aria-label="bench.vortex.dev home" {
94103
img.site-logo.logo-light src=(black_logo) alt="Vortex";
95104
img.site-logo.logo-dark src=(white_logo) alt="Vortex";
96105
}
97106
h1.site-title { "Vortex Benchmarks" }
98107
}
99108
div.header-center {
100-
div.nav-controls aria-label="Benchmark group controls" {
109+
div.nav-controls
110+
id="bench-nav-controls"
111+
data-role="nav-controls"
112+
aria-label="Benchmark group controls" {
101113
button.control-btn type="button" data-action="expand-all" {
102114
(chevrons_down_icon())
103115
span { "Expand All" }
@@ -109,13 +121,25 @@ fn site_header(universe: Option<&api::FilterUniverse>, filter: &FilterState) ->
109121
@if show_filters {
110122
(filter_dropdown(universe.expect("show_filters guard"), filter, active_count))
111123
}
124+
// Mobile-only GitHub link rendered inside the
125+
// hamburger panel. Hidden on desktop via CSS;
126+
// the desktop GitHub link in `.header-right`
127+
// covers the wide-viewport case.
128+
a.repo-link.nav-controls-github
129+
href="https://github.com/vortex-data/vortex"
130+
rel="noopener noreferrer"
131+
target="_blank" {
132+
(github_icon())
133+
span { "GitHub" }
134+
}
112135
}
113136
}
114137
div.header-right {
115-
a.repo-link href="https://github.com/vortex-data/vortex" rel="noopener noreferrer" target="_blank" {
116-
svg.github-logo viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true" {
117-
path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" {}
118-
}
138+
a.repo-link.repo-link-desktop
139+
href="https://github.com/vortex-data/vortex"
140+
rel="noopener noreferrer"
141+
target="_blank" {
142+
(github_icon())
119143
span { "GitHub" }
120144
}
121145
button.control-btn.theme-toggle type="button" data-role="theme-toggle" data-next-theme="light" aria-label="Toggle color theme" {
@@ -129,6 +153,24 @@ fn site_header(universe: Option<&api::FilterUniverse>, filter: &FilterState) ->
129153
}
130154
}
131155

156+
fn hamburger_icon() -> Markup {
157+
html! {
158+
svg.btn-icon viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" {
159+
path d="M4 6h16" {}
160+
path d="M4 12h16" {}
161+
path d="M4 18h16" {}
162+
}
163+
}
164+
}
165+
166+
fn github_icon() -> Markup {
167+
html! {
168+
svg.github-logo viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true" {
169+
path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" {}
170+
}
171+
}
172+
}
173+
132174
/// Funnel icon used by the global filter dropdown trigger.
133175
pub(super) fn filter_icon() -> Markup {
134176
html! {

0 commit comments

Comments
 (0)