You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make the DashboardBus actually re-filter data, not just propagate selection events. Phase 2.0 (#14) landed the infrastructure — bus, setSelection hook, donut self-highlight; this phase adds the user-visible payoff: clicking a slice somewhere re-renders every subscribed widget with its statistic restricted to that selection's subset.
Background
The Phase 2.0 pilot widget trio (births-per-century donut + marital-status donut + top-surnames tag cloud) carried disjoint dimensions (century / maritalStatus / surname), so visual-highlight on its own could not deliver the "decade-bar filters the migration sankey" UX the original RFC described. The bus is in place; what is still missing is a way for a widget to recompute its aggregate against a filtered subset.
Folds in #33 (concrete century → sankey + stream-graph pilot).
Three sub-tracks
1. Predicate-dimension spec
Define a canonical predicate shape so cross-widget compatibility stops being widget-pair-specific:
Update existing emitters in chart-lib so each widget annotates its own dimension instead of leaking widget-internal field names ({slice}, {name}, {label}). The bus payload stays opaque; receivers match on dimension first, then on value. Migrate donut-chart, sankey-flow, stream-graph, bar-chart and stacked-bar in one chart-lib commit.
2. Repository-level filtering
Every statistic that should be filterable accepts an optional filter: ?Predicate argument and routes it to the underlying repository as a WHERE clause. Pilot statistics:
Server-side re-aggregation is fast for census-scale trees and keeps the ≤100 ms acceptance criterion of #14 alive. The repositories validate dimension/value pairs against an allow-list per repository — unknown dimensions fall back to no filter.
3. Pilot integration
Two pilot flows ship together so the cross-dimension story is testable end-to-end:
Surname-pilot — Top-surnames tag cloud emits {dimension: "surname", value: "<surname>"} on click. Births-per-century donut + marital-status donut subscribe to the bus, refetch their payload, and re-render.
Century-pilot (folded in from Cross-filter pilot: births-century donut → migration sankey + stream graph #33) — Births-by-century donut emits {dimension: "century", value: "<century>"} on click. Migration sankey + given-name stream graph subscribe, refetch, and re-render with the cohort restricted to that century.
Both pilots share a single "Active filter: : " banner at the top of the Statistics chart with a one-click reset.
Tests + fixtures (required for every shipped repo + widget integration)
Repository tests: assert the WHERE clause restricts the result; assert unknown dimension falls back to the unfiltered statistic; assert null filter yields the same result as omitting the argument.
Jest tests: simulate a selection on the tag cloud / donut, assert sibling widgets call setSelection and request a new payload.
Curated GEDCOM fixture (tests/fixtures/cross-filter-pilot.ged) with ≥3 surnames across ≥3 centuries, named in the PR description.
Out of scope (Phase 2.2)
Persisting selections across tab switches.
URL-encoding the active filter so a filter survives a page reload.
Multi-predicate filters (currently one dimension at a time).
Filter chaining (clicking "1900s" then "Sonntag" stacks both predicates).
Goal
Make the DashboardBus actually re-filter data, not just propagate selection events. Phase 2.0 (#14) landed the infrastructure — bus,
setSelectionhook, donut self-highlight; this phase adds the user-visible payoff: clicking a slice somewhere re-renders every subscribed widget with its statistic restricted to that selection's subset.Background
The Phase 2.0 pilot widget trio (births-per-century donut + marital-status donut + top-surnames tag cloud) carried disjoint dimensions (century / maritalStatus / surname), so visual-highlight on its own could not deliver the "decade-bar filters the migration sankey" UX the original RFC described. The bus is in place; what is still missing is a way for a widget to recompute its aggregate against a filtered subset.
Folds in #33 (concrete century → sankey + stream-graph pilot).
Three sub-tracks
1. Predicate-dimension spec
Define a canonical predicate shape so cross-widget compatibility stops being widget-pair-specific:
Update existing emitters in chart-lib so each widget annotates its own dimension instead of leaking widget-internal field names (
{slice},{name},{label}). The bus payload stays opaque; receivers match ondimensionfirst, then onvalue. Migrate donut-chart, sankey-flow, stream-graph, bar-chart and stacked-bar in one chart-lib commit.2. Repository-level filtering
Every statistic that should be filterable accepts an optional
filter: ?Predicateargument and routes it to the underlying repository as a WHERE clause. Pilot statistics:EventRepository::getBirthsByCentury(?Predicate $filter = null)EventRepository::getMaritalStatus(?Predicate $filter = null)NameRepository::getTopSurnames(int $n, ?Predicate $filter = null)MigrationRepository::flows(?Predicate $filter = null)— sankeyGivenNameTrendsRepository::topByDecade(int $n, ?Predicate $filter = null)— stream-graphServer-side re-aggregation is fast for census-scale trees and keeps the ≤100 ms acceptance criterion of #14 alive. The repositories validate dimension/value pairs against an allow-list per repository — unknown dimensions fall back to no filter.
3. Pilot integration
Two pilot flows ship together so the cross-dimension story is testable end-to-end:
{dimension: "surname", value: "<surname>"}on click. Births-per-century donut + marital-status donut subscribe to the bus, refetch their payload, and re-render.{dimension: "century", value: "<century>"}on click. Migration sankey + given-name stream graph subscribe, refetch, and re-render with the cohort restricted to that century.Both pilots share a single "Active filter: : " banner at the top of the Statistics chart with a one-click reset.
Tests + fixtures (required for every shipped repo + widget integration)
setSelectionand request a new payload.tests/fixtures/cross-filter-pilot.ged) with ≥3 surnames across ≥3 centuries, named in the PR description.Out of scope (Phase 2.2)