feat(web): map region Information Card on hover#141
Conversation
lyubomir-bozhinov
left a comment
There was a problem hiding this comment.
И тук — чиста работа, Станислав.
Прогресивното подобрение е направено правилно: без JS остава <title> tooltip-ът, картата е страничен панел (не overlay), aria-live е на място, а withCard = total != null пази съществуващите callers (analytics-hub) необвързани. Браво и за това, че „Институции" е заключено само за oblast режим — MacroRegionSpend няма authorities, така че това е съзнателно, не случайно. Проверих с red-team тест, че знаменателят на „Дял" е консистентен в двата режима (sum(macroRegions) === sum(regions)), включително при NULL / непознати области — сметката е вярна.
Две неща:
-
Етикетът „Дял от всички области" в режим „район" (ниско, но точност). Етикетът е статичен и в двата режима; в режим „район" числителят е район (NUTS2), така че „области" е неточно — числото е вярно (същият знаменател), но съществителното не пасва. На министерски видим инструмент за прозрачност думата има значение. Предложение: режим-зависим етикет, или неутрално „Дял от разпределените разходи".
-
Тестове (препоръчително) за сметката на дела,
tierForShapeи mapping-а oblast ↔ район.
Mergeable, без блокери. 👍
Addresses review on midt-bg#141: - The card share label is now mode-aware (shareLabel): 'Дял от всички области' in oblast mode, 'Дял от всички райони' in район mode — the noun matches the numerator (denominator is unchanged; районите partition the same области). - Extract the choropleth pure logic (tierer, shareOfTotal, shareLabel, tierForShape, isActiveShape) into apps/web/app/lib/choropleth.ts and add choropleth.test.ts (share calc + zero/null guard, tier quantiles, the oblast↔район mapping per mode, active-shape highlighting). Follows the repo's pure-logic lib/*.test.ts convention — no render tests introduced. Component behaviour unchanged.
|
Благодаря! 🙏 Адресирах двете точки в 1. Етикетът „Дял" в режим „район". Направих го режим-зависим вместо неутрален: 2. Тестове. Изнесох чистата логика на картата в (Бележка: NULL/непознати области се обработват на data слоя — попадат в „без посочена област" преди да станат |
lyubomir-bozhinov
left a comment
There was a problem hiding this comment.
Благодаря, Станислав — проверих наново срещу текущия head (219a7cc).
И двете предишни бележки са затворени:
shareLabelвече ползва правилните родителни форми („Дял от всички райони" / „Дял от всички области") за двата режима.shareOfTotalе защитена срещу деление на нула / NaN (total && total > 0 ? value / total : 0).
Логиката на tierer е коректна — проследих я за реалния случай (6 района с положителна стойност, N=6): праговете дават разпределение [1,1,2,3,4,5], т.е. най-тъмният нюанс (tier 5) се пада на водещия район. Тестът t([0,100,...,500]) демонстрира тавана при N=5 (нулата отпада след филтъра v > 0), което си е по-друг случай — коментарът в кода е точен.
Approve от мен.
Review feedback on midt-bg#141: - SVG aria-label is now mode-dependent („по области" / „по райони") — it no longer asserts the wrong noun to screen readers after a toggle to район mode. - the grouping toggle renders only after hydration (it only does anything with JS); no-JS users no longer see a real-looking but inert control. - guard test pins the share denominator invariant: sum(regions) == sum(macros) == totalValueEur, so a future change that breaks the roll-up fails before ship.
|
Verification complete. This is a frontend-only PR (React choropleth + CSS + extracted pure logic with tests). I confirmed the Благодаря за тази чиста и добре обоснована промяна, Станислав. 🙏 Прегледах я стриктно — код, тестове, всички коментари по PR-а, и с фокус върху сигурност и цялост на данните. Сигурност (OWASP) — чисто. Промяната е изцяло на клиента (hover състояние + страничен панел + CSS) и не въвежда никаква нова повърхност за атака:
Цялост на данните — потвърдена. Знаменателят на „Дял" е един и същ в двата режима ( Съответствие с описанието на PR-а. Реализацията отговаря на заявеното: странична Information Card (не overlay), прогресивно подобрение ( Малка, необвързваща бележка: Забележка по проверката: не пуснах пълния vitest сют локално (worktree-ът е без инсталирани зависимости — нужен е Вердикт: Approve — без блокери. Промяната е сигурна, OWASP-съвместима, с коректна цялост на данните и покрита с тестове. |
Hovering a region on the /map choropleth now fills an Information Card beside the map (not an overlay) with that region's summary stats — value, share of all regions, contracts, institutions, and its NUTS2 район. Progressive enhancement: no-JS keeps the native <title> tooltip and the card shows a prompt. The compact analytics-hub map preview is unchanged (the card only renders when a total is passed).
Adds a toggle in the map Information Card to switch the choropleth between oblasts and planning regions (each район is a group of oblasts). In район mode each oblast is coloured by its parent район's total, hovering highlights the whole район, and the card shows the район's value/share/contracts. Client-side (data already loaded); the toggle lives in the card so all map controls stay together.
Addresses review on midt-bg#141: - The card share label is now mode-aware (shareLabel): 'Дял от всички области' in oblast mode, 'Дял от всички райони' in район mode — the noun matches the numerator (denominator is unchanged; районите partition the same области). - Extract the choropleth pure logic (tierer, shareOfTotal, shareLabel, tierForShape, isActiveShape) into apps/web/app/lib/choropleth.ts and add choropleth.test.ts (share calc + zero/null guard, tier quantiles, the oblast↔район mapping per mode, active-shape highlighting). Follows the repo's pure-logic lib/*.test.ts convention — no render tests introduced. Component behaviour unchanged.
Review feedback on midt-bg#141: - SVG aria-label is now mode-dependent („по области" / „по райони") — it no longer asserts the wrong noun to screen readers after a toggle to район mode. - the grouping toggle renders only after hydration (it only does anything with JS); no-JS users no longer see a real-looking but inert control. - guard test pins the share denominator invariant: sum(regions) == sum(macros) == totalValueEur, so a future change that breaks the roll-up fails before ship.
4d3323d to
35cd784
Compare
|
Rebase-нат върху новия main (linear, без merge commits): CSS-ът е преместен от app.css в styles/pages.css при choropleth блока; prettier-чист, lint/typecheck (7/7) и тестовете (web 349, db 184) минават. |
|
Verification complete. Frontend-only PR (React choropleth + грид/hover CSS + extracted pure logic with tests). Confirmed post-rebase: Благодаря за тази чиста и добре обоснована промяна, Станислав. 🙏 Прегледах я стриктно срещу текущия head след rebase-а — код, тестове и всички коментари по PR-а, с фокус върху сигурност и цялост на данните. Сигурност (OWASP Top 10) — чисто. Промяната е изцяло на клиента (hover състояние + страничен панел + toggle за групиране + CSS) и не отваря нова повърхност за атака:
Цялост на данните — потвърдена.
Съответствие с описанието на PR-а. Реализацията отговаря на заявеното: странична Information Card (не overlay), режим-зависими Малка, необвързваща бележка: Забележка по проверката: не пуснах пълния vitest сют локално (worktree-ът е без Вердикт: Approve — без блокери. Промяната е сигурна, OWASP-съвместима, с коректна цялост на данните и покрита с тестове. |
|
@todorkolev готов за ревю 🙏 — rebase-нат на main, CI зелен, prettier-чист, CSS промените в styles/* (app.css само @import). Резолвнати нишки. Approve-ни когато ти е удобно. |
|
Като цяло всичко трябва да работи добре и на мобилни устройства. В този случай hover не е добра интеракция - на телефон/таблет няма курсор, който да посочи, така че информационната карта остава недостъпна на докосване. Добре е детайлите да могат да се виждат и на click/tap, за да работи картата и без hover. |
onMouseEnter never fires on touch, so the region Information Card on /map stayed stuck on its placeholder hint for every mobile/tablet visitor. Add onClick alongside onMouseEnter so tapping a region populates the card the same way hovering does on desktop.
|
Одобрявам. Ре-проверих на Mobile: layout-ът е коректен — |
…t-bg#141) По области/По райони buttons were ~28px tall on touch — below the 44px floor already used for .pa-seg/.ov-seg/.q-grains elsewhere. Match that convention for coarse pointers.
|
Проверих |
What
On
/map, hovering a region now fills an Information Card beside the map (not an overlay) with that zone's summary stats:These are the same figures the ranked table below already lists — surfaced on hover, on the side, so they don't obscure the map.
Progressive enhancement (no chart JS in the baseline)
<title>tooltip still works and the card shows a prompt.totalis passed, so the compact map preview on/analyticsis unchanged.aria-liveon the card; the ranked table remains the primary keyboard/AT data path.Files
apps/web/app/components/Choropleth.tsx— hover state + side card (optionaltotalprop).apps/web/app/routes/map.tsx— passestotal.apps/web/app/app.css—.map-layout/.map-cardstyles + region hover highlight.Checks
pnpm --filter @sigma/web typecheck→ clean; the 3 changed files are Prettier-clean. Live queries only; no data changes.