Skip to content

Feat: add sortability and click to focus to team leadboard#3340

Open
Lako-Lako wants to merge 8 commits intoopenfrontio:mainfrom
Lako-Lako:feature/improve_team_leaderboard
Open

Feat: add sortability and click to focus to team leadboard#3340
Lako-Lako wants to merge 8 commits intoopenfrontio:mainfrom
Lako-Lako:feature/improve_team_leaderboard

Conversation

@Lako-Lako
Copy link
Copy Markdown

@Lako-Lako Lako-Lako commented Mar 3, 2026

If this PR fixes an issue, link it below. If not, delete these two lines.
Resolves #3330

Description:

Describe the PR.
This PR adds the sortability of the player leaderboard to the team leaderboard. This allows sorting for tiles, gold, troops and the units i.e. SAMS etc. Additionally clicking on a team in the leaderboard now focuses on the largest player of that team.
This is my first pr overall so any feedback would be appreciated (esspecially for the test).
I also thought about adding a new row that displays the amount of alive players / nations per team, what do you guys think?

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced
Screenshot from 2026-03-03 22-06-49 Screenshot from 2026-03-03 22-07-14

Please put your Discord username so you can be contacted if a bug or regression is found:

lakoeris

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 3, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

TeamStats: added keyboard/clickable sortable headers, stored raw numeric totals for correct sorting, and emits a GoToPlayerEvent (via eventBus) targeting the strongest alive player when a team row is activated. Reactive @state fields track sort key and order.

Changes

Cohort / File(s) Summary
Team Leaderboard Interactivity & Sorting
src/client/graphics/layers/TeamStats.ts
Added @state fields _sortKey and _sortOrder. Extended TeamEntry with raw numeric totals (totalGoldRaw, totalMaxTroopsRaw, totalSAMsRaw, totalLaunchersRaw, totalWarShipsRaw, totalCitiesRaw) for sorting while keeping formatted strings for display. Added _handleKeyDown keyboard handler, setSort() toggling sort key/order, and handleTeamClick() that finds the strongest alive player and emits GoToPlayerEvent via eventBus. updateTeamStats() now populates raw fields; rendering updated for clickable/keyboard-accessible headers and rows, sort indicators, dynamic grid sizing, and highlighted current user team. Review focus: bigint-aware gold comparator, multi-key numeric sorting, keyboard accessibility, and event emission target selection.

Sequence Diagram(s)

sequenceDiagram
    participant User as "User (click / Enter / Space)"
    participant UI as "TeamStats UI"
    participant BUS as "eventBus"
    participant Game as "Game / Players"

    User->>UI: activate team row (click / Enter / Space)
    UI->>Game: determine strongest alive player in team (use raw totals)
    Game-->>UI: returns playerId
    UI->>BUS: emit GoToPlayerEvent(playerId)
    BUS->>Game: route event -> focus / goto player
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

Click the header, hear columns sing,
Numbers march in tidy string.
Press Enter—follow the strongest light,
An event flies off into night. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implementation meets all coding requirements from issue #3330: sortable team leaderboard by tiles/gold/troops/units [#3330], clickable teams that focus the strongest player [#3330], and maintained cursor affordance [#3330].
Out of Scope Changes check ✅ Passed All changes are scoped to the TeamStats component and directly support the linked issue objectives; no extraneous modifications detected beyond sortability, click handling, and associated state/event management.
Description check ✅ Passed The PR description clearly relates to the changeset, explaining the new sortability feature for team leaderboards and team-click functionality.
Title check ✅ Passed The title accurately summarizes the main changes: adding sortability and click-to-focus functionality to the team leaderboard, which matches the primary objectives in the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/client/graphics/layers/TeamStats.ts (1)

46-47: Remove duplicate @state() on _sortKey.

@state() is applied twice to the same field. Keep only one to avoid duplicate reactive metadata registration.

Proposed fix
-  `@state`()
   `@state`()
   private _sortKey:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 46 - 47, The field
decorator `@state`() is applied twice to the same private field _sortKey in
TeamStats; remove the duplicate so _sortKey has only one `@state`() decorator to
avoid duplicate reactive metadata registration — locate the _sortKey declaration
in the TeamStats class and delete the redundant `@state`(), leaving a single
`@state`() above _sortKey.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/StructureIconsLayer.ts`:
- Around line 192-198: When unitUpdates reference units that no longer exist,
the loop in StructureIconsLayer skips them but leaves stale entries in
rendersByUnitId; update the loop handling unitUpdates so that when
this.game.unit(update.id) returns undefined you remove the corresponding entry
from rendersByUnitId and also clean up any visual resources for that unit (e.g.,
call the existing render disposal/remove method or destroy the sprite/container
stored in rendersByUnitId[update.id]). Ensure you reference the same keys
(update.id) and use the existing render cleanup code/path so orphaned renders
are removed when a unit is deleted.

In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 119-123: The current mapping in TeamStats.ts builds a translation
key via `team_colors.${rawTeam.toLowerCase()}` and calls `translateText(key)`
for every `rawTeam`; avoid doing this for generic team IDs like "Team 1"/"Team
N" to prevent repeated missing-key fallbacks. Update the logic around
`rawTeam`/`translateText` (the block that computes `key`, `translated`, and
`teamName`) to first detect generic labels (e.g., match `/^team\s*\d+$/i` or a
"Team <number>" pattern) and, when matched, set `teamName = rawTeam` (skip
creating `key` and calling `translateText`); otherwise continue to build `key`
and use `translateText` as before. This change should be localized to the
mapping in TeamStats.ts that references `rawTeam`, `key`, and `translateText`.
- Around line 169-176: totalGoldRaw is a bigint and casting to Number loses
precision; update the sorting logic in TeamStats.ts to compare bigints directly
instead of Number(a.totalGoldRaw). Create a bigint comparator (e.g.,
compareBigInt) that accepts two bigint values and returns -1/0/1 based on
this._sortOrder (use v1 < v2 / v1 > v2 checks, and invert for "desc"), and use
that comparator in the "gold" branch of the switch on this._sortKey so the sort
uses the bigint comparison without converting to Number.
- Around line 242-313: Header divs that call setSort are not
keyboard-accessible; add role="button", tabindex="0", and an `@keydown` handler
that invokes this.setSort(...) when Enter or Space is pressed (mirror the
existing `@click` behavior in the same elements that call setSort). For team rows
(the elements currently using class="contents" with an `@click` handler to select
a team), stop using display:contents for that interactive element — either
change the container to a semantic interactive element (button or anchor) or
move the clickable element inside so it can be focused; then add tabindex="0"
(if not a native control) plus role/aria attributes and an `@keydown` handler that
triggers the same team-selection handler used by the `@click`. Ensure the keyboard
handlers check for Enter/Space and call the same selection methods so both mouse
and keyboard invoke identical logic.

---

Nitpick comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 46-47: The field decorator `@state`() is applied twice to the same
private field _sortKey in TeamStats; remove the duplicate so _sortKey has only
one `@state`() decorator to avoid duplicate reactive metadata registration —
locate the _sortKey declaration in the TeamStats class and delete the redundant
`@state`(), leaving a single `@state`() above _sortKey.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28bbd93 and 7354b99.

📒 Files selected for processing (19)
  • resources/lang/en.json
  • src/client/FlagInput.ts
  • src/client/FlagInputModal.ts
  • src/client/GameModeSelector.ts
  • src/client/LangSelector.ts
  • src/client/PatternInput.ts
  • src/client/Utils.ts
  • src/client/components/Footer.ts
  • src/client/components/LobbyPlayerView.ts
  • src/client/components/MainLayout.ts
  • src/client/components/PatternButton.ts
  • src/client/components/PlayPage.ts
  • src/client/graphics/layers/GameLeftSidebar.ts
  • src/client/graphics/layers/PlayerInfoOverlay.ts
  • src/client/graphics/layers/PlayerPanel.ts
  • src/client/graphics/layers/StructureIconsLayer.ts
  • src/client/graphics/layers/TeamStats.ts
  • src/client/graphics/layers/UnitLayer.ts
  • tests/perf/StructureIconsLayerLookupPerf.ts

Comment thread src/client/graphics/layers/StructureIconsLayer.ts
Comment thread src/client/graphics/layers/TeamStats.ts
Comment thread src/client/graphics/layers/TeamStats.ts
Comment thread src/client/graphics/layers/TeamStats.ts
@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management Mar 3, 2026
@Lako-Lako Lako-Lako marked this pull request as draft March 4, 2026 09:07
@Lako-Lako Lako-Lako force-pushed the feature/improve_team_leaderboard branch from 7354b99 to 02a2400 Compare March 4, 2026 10:25
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/client/graphics/layers/TeamStats.ts (2)

169-172: ⚠️ Potential issue | 🟠 Major

Fix gold sorting to compare bigint directly.

totalGoldRaw is bigint; converting to Number(...) can lose precision and produce wrong order for large totals.

Suggested fix
       .sort((a, b) => {
         const compare = (v1: number, v2: number) =>
           this._sortOrder === "asc" ? v1 - v2 : v2 - v1;
+        const compareBigInt = (v1: bigint, v2: bigint) => {
+          if (v1 === v2) return 0;
+          const less = v1 < v2;
+          return this._sortOrder === "asc" ? (less ? -1 : 1) : less ? 1 : -1;
+        };

         switch (this._sortKey) {
           case "gold":
-            return compare(Number(a.totalGoldRaw), Number(b.totalGoldRaw));
+            return compareBigInt(a.totalGoldRaw, b.totalGoldRaw);
#!/bin/bash
# Verify bigint field + lossy numeric cast in sort branch
rg -nP 'totalGoldRaw:\s*bigint|Number\(a\.totalGoldRaw\)|Number\(b\.totalGoldRaw\)' src/client/graphics/layers/TeamStats.ts -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 169 - 172, The "gold"
sort branch in TeamStats is casting totalGoldRaw (a bigint) to Number which can
lose precision; replace the Number(...) casts in the case "gold" branch so you
compare the bigint values directly (e.g., use a BigInt-aware comparison or call
compare with a.totalGoldRaw and b.totalGoldRaw rather than
Number(a.totalGoldRaw)/Number(b.totalGoldRaw)), keeping the branch inside the
method that switches on this._sortKey.

239-240: ⚠️ Potential issue | 🟠 Major

Add keyboard-accessible interaction semantics for sortable headers and team-row actions.

These handlers are click-only, and the row target uses class="contents" (not a robust focus/semantic target). Keyboard users currently cannot perform sorting/team focus actions.

Also applies to: 250-251, 261-262, 272-273, 285-286, 296-297, 307-308, 324-329, 348-353

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 239 - 240, The sortable
header cells in TeamStats.ts are only click-driven and some team rows use
class="contents", making them inaccessible to keyboard users; update each
clickable header (where `@click` calls this.setSort("...")) to include
role="button", tabindex="0" and a keydown handler that triggers
this.setSort(...) on Enter or Space, and similarly add role="button",
tabindex="0" and a keydown handler for the team-row actions (the elements with
class="contents") to invoke the existing team-selection/focus action (the same
callback used by the click handler) on Enter/Space so keyboard users can
activate sorting and team focus.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 46-47: The property _sortKey has two consecutive `@state`()
decorators; remove the duplicate so only a single `@state`() decorates the
_sortKey field (locate the declaration of _sortKey in the TeamStats class and
delete the extra `@state`() line immediately above it).

---

Duplicate comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 169-172: The "gold" sort branch in TeamStats is casting
totalGoldRaw (a bigint) to Number which can lose precision; replace the
Number(...) casts in the case "gold" branch so you compare the bigint values
directly (e.g., use a BigInt-aware comparison or call compare with
a.totalGoldRaw and b.totalGoldRaw rather than
Number(a.totalGoldRaw)/Number(b.totalGoldRaw)), keeping the branch inside the
method that switches on this._sortKey.
- Around line 239-240: The sortable header cells in TeamStats.ts are only
click-driven and some team rows use class="contents", making them inaccessible
to keyboard users; update each clickable header (where `@click` calls
this.setSort("...")) to include role="button", tabindex="0" and a keydown
handler that triggers this.setSort(...) on Enter or Space, and similarly add
role="button", tabindex="0" and a keydown handler for the team-row actions (the
elements with class="contents") to invoke the existing team-selection/focus
action (the same callback used by the click handler) on Enter/Space so keyboard
users can activate sorting and team focus.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 626f261b-6396-4ac7-ad4d-c66b7e035862

📥 Commits

Reviewing files that changed from the base of the PR and between 7354b99 and 02a2400.

📒 Files selected for processing (1)
  • src/client/graphics/layers/TeamStats.ts

Comment thread src/client/graphics/layers/TeamStats.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/client/graphics/layers/TeamStats.ts (1)

354-360: ⚠️ Potential issue | 🟠 Major

Interactive row wrapper still uses display: contents, which breaks keyboard/AT behavior.

Line 354 and Line 379 keep class="contents" on the focusable interactive element (role + tabindex + key handler). This pattern is unreliable for screen readers and keyboard focus. Please move interactivity to a real focusable element (e.g., button/a) that is not display: contents, or make per-cell controls focusable.

#!/bin/bash
# Verify focusable/interactive semantics on display: contents wrappers.
rg -nP 'class="contents"[\s\S]{0,220}(role="(link|button)"|tabindex="0"|@keydown=)' src/client/graphics/layers/TeamStats.ts -C2

Also applies to: 379-385

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 354 - 360, The
interactive row wrapper uses class="contents" (CSS display: contents) with
role="link", tabindex="0", `@click` and `@keydown` handlers (see the element
wrapping rows in TeamStats.ts that calls handleTeamClick(team) and
_handleKeyDown), which breaks keyboard/AT support; fix by replacing the
display:contents wrapper with a real focusable element (e.g., a <button> or <a>)
or move the interactive handlers onto per-cell focusable controls: create a
semantic element (button or anchor) that is not display:contents, attach `@click`
to handleTeamClick(team) and `@keydown` to _handleKeyDown there, remove
role/tabindex from the contents class, and ensure each interactive cell has its
own focusable control if row-level semantics are not appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 354-355: Prettier/style CI failed for the TeamStats component due
to formatting inconsistencies (e.g. the <div class="contents"> block and nearby
lines around the render/JSX), so run Prettier on
src/client/graphics/layers/TeamStats.ts and commit the formatting-only changes;
specifically reformat the JSX/HTML blocks (including the occurrences around the
"contents" div and the lines flagged at roughly the later JSX section) to match
the project's Prettier config and push the resulting diff.

---

Duplicate comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 354-360: The interactive row wrapper uses class="contents" (CSS
display: contents) with role="link", tabindex="0", `@click` and `@keydown` handlers
(see the element wrapping rows in TeamStats.ts that calls handleTeamClick(team)
and _handleKeyDown), which breaks keyboard/AT support; fix by replacing the
display:contents wrapper with a real focusable element (e.g., a <button> or <a>)
or move the interactive handlers onto per-cell focusable controls: create a
semantic element (button or anchor) that is not display:contents, attach `@click`
to handleTeamClick(team) and `@keydown` to _handleKeyDown there, remove
role/tabindex from the contents class, and ensure each interactive cell has its
own focusable control if row-level semantics are not appropriate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 228f2117-8874-483c-a5e1-4cf22e200dc6

📥 Commits

Reviewing files that changed from the base of the PR and between 02a2400 and d823f68.

📒 Files selected for processing (1)
  • src/client/graphics/layers/TeamStats.ts

Comment thread src/client/graphics/layers/TeamStats.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/client/graphics/layers/TeamStats.ts (1)

362-368: ⚠️ Potential issue | 🟠 Major

Interactive row wrapper still uses display: contents, which is unreliable for accessibility.

class="contents" on a focusable/interactive container (role + tabindex + key/click handlers) can break semantics and keyboard/screen-reader behavior. Move interaction to a real interactive element (button/a) that is not display: contents.

Proposed direction
- <div
-   class="contents"
-   role="link"
-   tabindex="0"
-   `@click`=${() => this.handleTeamClick(team)}
-   `@keydown`=${(e: KeyboardEvent) =>
-     this._handleKeyDown(e, () => this.handleTeamClick(team))}
- >
-   <div class="py-1.5 ... cursor-pointer ${team.isMyTeam ? "font-bold" : ""}">
-     ${team.teamName}
-   </div>
+ <div class="contents">
+   <button
+     type="button"
+     class="py-1.5 border-b border-slate-500 text-center hover:bg-slate-600/60 ${team.isMyTeam ? "font-bold" : ""}"
+     `@click`=${() => this.handleTeamClick(team)}
+     `@keydown`=${(e: KeyboardEvent) =>
+       this._handleKeyDown(e, () => this.handleTeamClick(team))}
+   >
+     ${team.teamName}
+   </button>

Apply the same pattern to the non-units branch as well.

#!/bin/bash
# Verify there are no focusable/interactive wrappers still using class="contents"
rg -nP 'class="contents"[\s\S]{0,200}(role=|tabindex=|@click=|@keydown=)' src/client/graphics/layers/TeamStats.ts -C2

Expected result after fix: no matches where class="contents" is also interactive.

Also applies to: 400-406

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 362 - 368, The
interactive wrapper uses class="contents" with role/tabindex and handlers which
is inaccessible; replace the focusable/display:contents container with a real
interactive element (e.g., a <button> or <a>) and attach the click and key
handlers directly to it instead of using display:contents. Update the instances
in TeamStats (the element that calls this.handleTeamClick(team) and
this._handleKeyDown) and the similar non-units branch (the block around the
400-406 region) so the wrapper is a semantic interactive element (no
class="contents") and preserve existing handler calls and ARIA role as needed.
🧹 Nitpick comments (1)
src/client/graphics/layers/TeamStats.ts (1)

82-106: Please add targeted regression tests for new interaction paths.

This introduces non-trivial behavior (setSort, keyboard activation, and GoToPlayerEvent emission). Add tests for sort toggling, keyboard-triggered sorting, and team click choosing the strongest alive player.

Also applies to: 171-205, 210-221

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 82 - 106, Add targeted
unit/integration tests covering the new interaction paths: (1) test setSort
toggles sort order when called twice for the same key and sets default "desc"
when switching keys by exercising the setSort method and asserting
_sortKey/_sortOrder and that updateTeamStats is invoked; (2) test keyboard
activation by simulating KeyboardEvent inputs to _handleKeyDown (Enter and
Space) to ensure it prevents default and invokes the provided callback; and (3)
test team-click behavior that chooses the strongest alive player and emits a
GoToPlayerEvent by simulating the team click UI path (or invoking the handler
used by the click) on a team with mixed alive/dead players and asserting the
emitted GoToPlayerEvent payload targets the strongest alive player. Include
assertions for event emission and state changes and mock/stub updateTeamStats
and event emitter to isolate behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 362-368: The interactive wrapper uses class="contents" with
role/tabindex and handlers which is inaccessible; replace the
focusable/display:contents container with a real interactive element (e.g., a
<button> or <a>) and attach the click and key handlers directly to it instead of
using display:contents. Update the instances in TeamStats (the element that
calls this.handleTeamClick(team) and this._handleKeyDown) and the similar
non-units branch (the block around the 400-406 region) so the wrapper is a
semantic interactive element (no class="contents") and preserve existing handler
calls and ARIA role as needed.

---

Nitpick comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 82-106: Add targeted unit/integration tests covering the new
interaction paths: (1) test setSort toggles sort order when called twice for the
same key and sets default "desc" when switching keys by exercising the setSort
method and asserting _sortKey/_sortOrder and that updateTeamStats is invoked;
(2) test keyboard activation by simulating KeyboardEvent inputs to
_handleKeyDown (Enter and Space) to ensure it prevents default and invokes the
provided callback; and (3) test team-click behavior that chooses the strongest
alive player and emits a GoToPlayerEvent by simulating the team click UI path
(or invoking the handler used by the click) on a team with mixed alive/dead
players and asserting the emitted GoToPlayerEvent payload targets the strongest
alive player. Include assertions for event emission and state changes and
mock/stub updateTeamStats and event emitter to isolate behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd0f486b-03a9-4730-a80e-48e0292b36d3

📥 Commits

Reviewing files that changed from the base of the PR and between d823f68 and 5aaa16b.

📒 Files selected for processing (1)
  • src/client/graphics/layers/TeamStats.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 4, 2026
@Lako-Lako Lako-Lako marked this pull request as ready for review March 4, 2026 18:10
@Lako-Lako
Copy link
Copy Markdown
Author

Except for tests this should be done, I am unsure how to make tests so it'll take me a bit to add them if needed.

@Lako-Lako Lako-Lako changed the title Feature/improve team leaderboard Feat: improve team leaderboard Mar 10, 2026
@Lako-Lako
Copy link
Copy Markdown
Author

While playing more, I thought it would be cool if the amount of players per team was also displayed. This would also make the table same size for normal and unit stats. But that is a change ill only do if it is wanted.

@Lako-Lako Lako-Lako force-pushed the feature/improve_team_leaderboard branch from 5aaa16b to 3c1ab81 Compare March 16, 2026 19:22
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
src/client/graphics/layers/TeamStats.ts (2)

126-128: ⚠️ Potential issue | 🟡 Minor

Skip translation lookup for generic team labels like "Team 1".

On Line 126–128, generic team IDs still go through translateText("team_colors.*"), which causes repeated missing-key fallback behavior. Use rawTeam directly when it matches Team <number>.

Proposed fix
-        const key = `team_colors.${rawTeam.toLowerCase()}`;
-        const translated = translateText(key);
-        const teamName = translated !== key ? translated : rawTeam;
+        const isGenericTeamName = /^team\s+\d+$/i.test(rawTeam);
+        const key = `team_colors.${rawTeam.toLowerCase()}`;
+        const translated = isGenericTeamName ? rawTeam : translateText(key);
+        const teamName = translated !== key ? translated : rawTeam;
Based on learnings: In `GameImpl.ts` lines 124-139, when `numPlayerTeams >= 8`, teams can be generic identifiers like `"Team 1"` rather than `ColoredTeams` names.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 126 - 128, Modify the
team-name resolution so generic labels like "Team 1" bypass translateText:
before building key/translated, detect if rawTeam matches the generic pattern
(e.g., /^Team \d+$/) and if so set teamName = rawTeam; otherwise proceed to
compute key = `team_colors.${rawTeam.toLowerCase()}`, call translateText(key)
and use translated !== key ? translated : rawTeam. This change touches the team
name logic in TeamStats.ts (variables/key: rawTeam, key, translated, teamName,
translateText).

365-372: ⚠️ Potential issue | 🟠 Major

Avoid interactive roles/handlers on class="contents" row wrappers.

Line 365 and Line 403 attach role, tabindex, and activation handlers to display: contents containers. This pattern is unreliable for accessibility. Move interaction to a real focusable element (or a non-contents row wrapper).

To verify this pattern quickly, run:

#!/bin/bash
# Detect "display: contents" blocks that also carry interactive semantics.
python - <<'PY'
from pathlib import Path

path = Path("src/client/graphics/layers/TeamStats.ts")
lines = path.read_text().splitlines()

for i, line in enumerate(lines, start=1):
    if 'class="contents"' in line:
        block = "\n".join(lines[i-1:i+10])
        if 'role=' in block or 'tabindex=' in block or '@click=${() => this.handleTeamClick' in block:
            print(f"Interactive 'contents' block near Line {i}:")
            print(block)
            print("-" * 60)
PY

Expected result: it reports the two row containers where class="contents" is combined with interactive semantics.

Also applies to: 403-410

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/TeamStats.ts` around lines 365 - 372, The wrapper
element with class="contents" should not carry interactive semantics; remove
role="link", tabindex="0", `@click` and `@keydown` from the display: contents
container and move them onto a real focusable element (e.g., the team row
element or a button/anchor inside it) so keyboard/AT can interact reliably;
update references to handleTeamClick and _handleKeyDown so the new focusable
element calls this.handleTeamClick(team) and this._handleKeyDown(e, () =>
this.handleTeamClick(team)), and ensure the original container keeps only
presentational styling (display: contents) or is replaced with a non-contents
wrapper if needed for layout.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/client/graphics/layers/TeamStats.ts`:
- Around line 126-128: Modify the team-name resolution so generic labels like
"Team 1" bypass translateText: before building key/translated, detect if rawTeam
matches the generic pattern (e.g., /^Team \d+$/) and if so set teamName =
rawTeam; otherwise proceed to compute key =
`team_colors.${rawTeam.toLowerCase()}`, call translateText(key) and use
translated !== key ? translated : rawTeam. This change touches the team name
logic in TeamStats.ts (variables/key: rawTeam, key, translated, teamName,
translateText).
- Around line 365-372: The wrapper element with class="contents" should not
carry interactive semantics; remove role="link", tabindex="0", `@click` and
`@keydown` from the display: contents container and move them onto a real
focusable element (e.g., the team row element or a button/anchor inside it) so
keyboard/AT can interact reliably; update references to handleTeamClick and
_handleKeyDown so the new focusable element calls this.handleTeamClick(team) and
this._handleKeyDown(e, () => this.handleTeamClick(team)), and ensure the
original container keeps only presentational styling (display: contents) or is
replaced with a non-contents wrapper if needed for layout.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0871c3fc-d345-47c3-ab22-0b18a54a5300

📥 Commits

Reviewing files that changed from the base of the PR and between 5aaa16b and 3c1ab81.

📒 Files selected for processing (1)
  • src/client/graphics/layers/TeamStats.ts

@github-actions
Copy link
Copy Markdown

This pull request is stale because it has been open for fourteen days with no activity. If you want to keep this pull request open, add a comment or update the branch.

@github-actions github-actions Bot added the Stale PRs that haven't been touched for over two weeks. label Mar 31, 2026
@Lako-Lako Lako-Lako force-pushed the feature/improve_team_leaderboard branch from c3d69cc to 5660b5e Compare March 31, 2026 18:01
@github-actions github-actions Bot removed the Stale PRs that haven't been touched for over two weeks. label Apr 1, 2026
@Lako-Lako Lako-Lako changed the title Feat: improve team leaderboard Feat: add sortability and click to focus to team leadboard Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

Feature - Improve Team leaderboard

2 participants