feat(org-lens): add score-breakdown drawer to leaderboard rows UI only - #1276
feat(org-lens): add score-breakdown drawer to leaderboard rows UI only#1276danoqualls wants to merge 2 commits into
Conversation
Clicking a row in the Org Lens Project Detail Technical or Ecosystem leaderboard now opens a right-side drawer with that org's category score breakdown for the clicked influence dimension. Category points/counts are DEMO/PLACEHOLDER data pending a real Snowflake-backed data source; orgs not present in the demo lookup render a graceful empty state instead of crashing. LFXV2-2934 Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
PR SummaryLow Risk Overview Org project detail — Leaderboard table rows are now clickable ( New drawer component — Shows org + project subtitle, a summary (level, activity %, score, rank), category bars with points/%, total score, and methodology copy. Data comes from demo lookups keyed by org display name in shared constants until Snowflake (LFXV2-2934); orgs not in the map get an empty state instead of an error. Shared package — New interfaces, placeholder company/category/methodology constants, and utils for influence level thresholds and category row sorting. E2E — Covers row click (technical/ecosystem), title match, content or empty state, and Escape to close without leaving the page. Reviewed by Cursor Bugbot for commit 104602c. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds a score-breakdown drawer for technical and ecosystem leaderboard rows on the organization project detail page, using shared interfaces, demo data, calculation utilities, row-click wiring, and E2E coverage for opening, rendering, and closing the drawer. ChangesLeaderboard score-breakdown drawer
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant OrgProjectDetailComponent
participant OrgLeaderboardDetailDrawerComponent
participant SharedLeaderboardData
User->>OrgProjectDetailComponent: Click technical or ecosystem leaderboard row
OrgProjectDetailComponent->>OrgLeaderboardDetailDrawerComponent: Set dimension, organization, and visibility
OrgLeaderboardDetailDrawerComponent->>SharedLeaderboardData: Resolve company breakdown and methodology
SharedLeaderboardData-->>OrgLeaderboardDetailDrawerComponent: Provide score and category data
OrgLeaderboardDetailDrawerComponent-->>User: Display breakdown or empty state
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html`:
- Around line 364-368: The technical leaderboard row trigger at
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L364-L368
and ecosystem leaderboard row trigger at
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L494-L498
are mouse-only. Make both organization-cell triggers keyboard operable using an
accessible button while preserving row styling, or add equivalent focusable
Enter/Space handling and accessible names; keep each trigger opening
openLeaderboardDetail with its existing leaderboard type and row.
In
`@apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.ts`:
- Around line 427-432: Guard openLeaderboardDetail so it returns without opening
the drawer unless metric() is influence mode, and ensure both activity
leaderboard tables no longer expose a clickable row affordance or invoke this
handler. Add coverage confirming activity mode cannot open the score breakdown
while preserving influence-mode behavior.
In `@packages/shared/src/constants/org-leaderboard-detail-drawer.constants.ts`:
- Around line 46-194: Align ORG_LEADERBOARD_DETAIL_TECHNICAL_COMPANIES and
ORG_LEADERBOARD_DETAIL_ECOSYSTEM_COMPANIES with
ORG_LEADERBOARD_DETAIL_METHODOLOGY: ensure maintainer points are 10 whenever
counts.maintainer is positive and board points are 0 whenever counts.board is
zero, while recalculating affected scores as needed. Alternatively, explicitly
label both the demo data and methodology copy as illustrative so they are not
presented as matching calculations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e2dd5ffd-d0ee-4e0b-a244-a03a26d4ed7e
📒 Files selected for processing (11)
apps/lfx-one/e2e/org-project-detail.spec.tsapps/lfx-one/src/app/modules/dashboards/components/org-leaderboard-detail-drawer/org-leaderboard-detail-drawer.component.htmlapps/lfx-one/src/app/modules/dashboards/components/org-leaderboard-detail-drawer/org-leaderboard-detail-drawer.component.tsapps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.htmlapps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.tspackages/shared/src/constants/index.tspackages/shared/src/constants/org-leaderboard-detail-drawer.constants.tspackages/shared/src/interfaces/index.tspackages/shared/src/interfaces/org-leaderboard-detail-drawer.interface.tspackages/shared/src/utils/index.tspackages/shared/src/utils/org-leaderboard-detail.utils.ts
| <tr | ||
| class="border-b border-gray-100" | ||
| class="cursor-pointer border-b border-gray-100 hover:bg-blue-50" | ||
| [class.bg-blue-50]="row.isViewingOrg" | ||
| [attr.data-testid]="row.isViewingOrg ? 'project-detail-leaderboard-technical-viewing-row' : null"> | ||
| [attr.data-testid]="row.isViewingOrg ? 'project-detail-leaderboard-technical-viewing-row' : null" | ||
| (click)="openLeaderboardDetail('technical', row)"> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make leaderboard-row activation keyboard accessible.
Both rows are mouse-only <tr> click targets, so keyboard users cannot open the drawer. Use a keyboard-operable button in the organization cell (while retaining the row styling), or provide equivalent focus, Enter/Space handling, and accessible naming.
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L364-L368: make the technical-row trigger keyboard operable.apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L494-L498: make the ecosystem-row trigger keyboard operable.
📍 Affects 1 file
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L364-L368(this comment)apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L494-L498
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html`
around lines 364 - 368, The technical leaderboard row trigger at
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L364-L368
and ecosystem leaderboard row trigger at
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html#L494-L498
are mouse-only. Make both organization-cell triggers keyboard operable using an
accessible button while preserving row styling, or add equivalent focusable
Enter/Space handling and accessible names; keep each trigger opening
openLeaderboardDetail with its existing leaderboard type and row.
| /** Opens the leaderboard row score-breakdown drawer for the clicked technical/ecosystem row. */ | ||
| protected openLeaderboardDetail(dimension: LeaderboardDimension, row: BoardDisplayRow): void { | ||
| this.leaderboardDetailDimension.set(dimension); | ||
| this.leaderboardDetailOrgName.set(row.orgName); | ||
| this.leaderboardDetailOpen.set(true); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not open a score breakdown from activity leaderboards.
This handler runs while metric() === 'activity', where the row represents contribution/collaboration counts rather than influence scores. It then opens a static influence-score breakdown for that organization. Guard this handler for influence mode and remove the clickable affordance from both activity tables; add activity-mode coverage.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.ts`
around lines 427 - 432, Guard openLeaderboardDetail so it returns without
opening the drawer unless metric() is influence mode, and ensure both activity
leaderboard tables no longer expose a clickable row affordance or invoke this
handler. Add coverage confirming activity mode cannot open the score breakdown
while preserving influence-mode behavior.
| Google: { | ||
| rank: 1, | ||
| activityPct: 31, | ||
| score: 104, | ||
| points: { maintainer: 6, contributors: 22, commits: 52, prs: 24 }, | ||
| counts: { maintainer: 1, contributors: 218, commits: 3842, prs: 296 }, | ||
| }, | ||
| 'Red Hat': { | ||
| rank: 2, | ||
| activityPct: 24, | ||
| score: 98, | ||
| points: { maintainer: 18, contributors: 32, commits: 30, prs: 18 }, | ||
| counts: { maintainer: 3, contributors: 314, commits: 2210, prs: 221 }, | ||
| }, | ||
| IBM: { | ||
| rank: 3, | ||
| activityPct: 13, | ||
| score: 71, | ||
| points: { maintainer: 22, contributors: 23, commits: 16, prs: 10 }, | ||
| counts: { maintainer: 4, contributors: 226, commits: 1182, prs: 123 }, | ||
| }, | ||
| VMware: { | ||
| rank: 4, | ||
| activityPct: 9, | ||
| score: 58, | ||
| points: { maintainer: 15, contributors: 20, commits: 14, prs: 9 }, | ||
| counts: { maintainer: 2, contributors: 196, commits: 1034, prs: 111 }, | ||
| }, | ||
| Microsoft: { | ||
| rank: 5, | ||
| activityPct: 21, | ||
| score: 68, | ||
| points: { maintainer: 6, contributors: 20, commits: 26, prs: 16 }, | ||
| counts: { maintainer: 1, contributors: 197, commits: 1921, prs: 197 }, | ||
| }, | ||
| Intel: { | ||
| rank: 6, | ||
| activityPct: 11, | ||
| score: 47, | ||
| points: { maintainer: 6, contributors: 14, commits: 18, prs: 9 }, | ||
| counts: { maintainer: 1, contributors: 137, commits: 1330, prs: 111 }, | ||
| }, | ||
| Cisco: { | ||
| rank: 7, | ||
| activityPct: 6, | ||
| score: 34, | ||
| points: { maintainer: 5, contributors: 10, commits: 12, prs: 7 }, | ||
| counts: { maintainer: 1, contributors: 98, commits: 887, prs: 86 }, | ||
| }, | ||
| D2iQ: { | ||
| rank: 8, | ||
| activityPct: 3, | ||
| score: 22, | ||
| points: { maintainer: 3, contributors: 6, commits: 8, prs: 5 }, | ||
| counts: { maintainer: 1, contributors: 59, commits: 591, prs: 62 }, | ||
| }, | ||
| }; | ||
|
|
||
| /** DEMO DATA — illustrative per-company ecosystem-influence breakdown, keyed by org display name. */ | ||
| export const ORG_LEADERBOARD_DETAIL_ECOSYSTEM_COMPANIES: Record<string, OrgLeaderboardDetailCompany> = { | ||
| 'Red Hat': { | ||
| rank: 1, | ||
| activityPct: 22, | ||
| score: 96, | ||
| points: { collab: 40, meeting: 12, event: 10, committee: 12, board: 9, speakers: 7, meetup: 3, sponsor: 2, certified: 1 }, | ||
| counts: { collab: 812, meeting: 145, event: 28, committee: 14, board: 2, speakers: 19, meetup: 9, sponsor: 3, certified: 12 }, | ||
| }, | ||
| Google: { | ||
| rank: 2, | ||
| activityPct: 27, | ||
| score: 88, | ||
| points: { collab: 46, meeting: 8, event: 12, committee: 4, board: 2, speakers: 9, meetup: 5, sponsor: 1, certified: 1 }, | ||
| counts: { collab: 934, meeting: 97, event: 34, committee: 5, board: 0, speakers: 24, meetup: 15, sponsor: 2, certified: 8 }, | ||
| }, | ||
| IBM: { | ||
| rank: 3, | ||
| activityPct: 15, | ||
| score: 91, | ||
| points: { collab: 12, meeting: 16, event: 6, committee: 19, board: 18, speakers: 8, meetup: 4, sponsor: 5, certified: 3 }, | ||
| counts: { collab: 244, meeting: 193, event: 17, committee: 22, board: 3, speakers: 21, meetup: 11, sponsor: 6, certified: 27 }, | ||
| }, | ||
| VMware: { | ||
| rank: 4, | ||
| activityPct: 11, | ||
| score: 62, | ||
| points: { collab: 18, meeting: 7, event: 5, committee: 6, board: 4, speakers: 5, meetup: 2, sponsor: 2, certified: 2 }, | ||
| counts: { collab: 366, meeting: 85, event: 14, committee: 7, board: 1, speakers: 13, meetup: 6, sponsor: 3, certified: 15 }, | ||
| }, | ||
| Microsoft: { | ||
| rank: 5, | ||
| activityPct: 18, | ||
| score: 79, | ||
| points: { collab: 28, meeting: 10, event: 14, committee: 5, board: 3, speakers: 12, meetup: 4, sponsor: 2, certified: 1 }, | ||
| counts: { collab: 571, meeting: 121, event: 39, committee: 6, board: 1, speakers: 31, meetup: 10, sponsor: 3, certified: 9 }, | ||
| }, | ||
| Intel: { | ||
| rank: 6, | ||
| activityPct: 9, | ||
| score: 54, | ||
| points: { collab: 14, meeting: 9, event: 4, committee: 8, board: 7, speakers: 4, meetup: 3, sponsor: 3, certified: 2 }, | ||
| counts: { collab: 284, meeting: 109, event: 11, committee: 9, board: 2, speakers: 10, meetup: 8, sponsor: 4, certified: 14 }, | ||
| }, | ||
| Cisco: { | ||
| rank: 7, | ||
| activityPct: 7, | ||
| score: 41, | ||
| points: { collab: 10, meeting: 6, event: 4, committee: 5, board: 4, speakers: 4, meetup: 3, sponsor: 3, certified: 2 }, | ||
| counts: { collab: 202, meeting: 73, event: 11, committee: 6, board: 1, speakers: 10, meetup: 8, sponsor: 4, certified: 13 }, | ||
| }, | ||
| D2iQ: { | ||
| rank: 8, | ||
| activityPct: 4, | ||
| score: 26, | ||
| points: { collab: 8, meeting: 3, event: 3, committee: 3, board: 2, speakers: 3, meetup: 2, sponsor: 1, certified: 1 }, | ||
| counts: { collab: 162, meeting: 36, event: 8, committee: 4, board: 0, speakers: 8, meetup: 5, sponsor: 2, certified: 6 }, | ||
| }, | ||
| }; | ||
|
|
||
| /** Condensed "Company influence metrics" methodology copy shown in the drawer, per dimension. */ | ||
| export const ORG_LEADERBOARD_DETAIL_METHODOLOGY: Record<'technical' | 'ecosystem', OrgLeaderboardDetailMethodology> = { | ||
| technical: { | ||
| intro: | ||
| 'Only four categories earn technical-influence points. Each earns points independently — there are no preset weights per category. Points from all categories are added together to get the total score.', | ||
| bullets: [ | ||
| { label: 'Maintainers', text: '— 10 pts if the company has 1+ project maintainers, else 0.' }, | ||
| { | ||
| label: 'Contributors, Commit Activities, PRs Opened', | ||
| text: '— each 0.1% share of the project total earns 0.1 pt, plus a +1 bonus for any share above 0%.', | ||
| }, | ||
| ], | ||
| levelMapping: 'Total score maps to a level: 0 = Silent, 1–4 = Participating, 5–14 = Contributing, 15+ = Leading.', | ||
| }, | ||
| ecosystem: { | ||
| intro: | ||
| 'Each category below earns points independently — there are no preset weights per category. Points from all categories are added together to get the total score.', | ||
| bullets: [ | ||
| { | ||
| label: 'Collaboration Activity, Meeting Attendance, Committee Members, Event Speakers', | ||
| text: '— each 0.1% share of the project/foundation total earns 0.1 pt, plus a +1 bonus for any share above 0%.', | ||
| }, | ||
| { label: 'Board Members', text: '— 1 pt if the company has 1+ foundation board members, else 0.' }, | ||
| { | ||
| label: 'Event Attendance, Event Sponsorships, Meetup Attendance, Certified Individuals', | ||
| text: '— tiered by share: 0 / 0.33 / 0.66 / 1 pt for none / up to 25% / up to 50% / over 50%.', | ||
| }, | ||
| ], | ||
| levelMapping: 'Total score maps to a level: 0–2 = Silent, 3–10 = Participating, 11–19 = Contributing, 20+ = Leading.', | ||
| }, | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Align the demo scores with the displayed methodology.
The drawer presents this as the scoring calculation, but the data contradicts it: Google has one technical maintainer worth 6 points (Lines 50-51) despite Line 170 saying that one maintainer earns 10; Google also has zero board members worth 2 points (Lines 117-118) despite Line 186 saying zero earns 0. Either make the demo values follow these rules or explicitly present both the data and copy as illustrative.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/shared/src/constants/org-leaderboard-detail-drawer.constants.ts`
around lines 46 - 194, Align ORG_LEADERBOARD_DETAIL_TECHNICAL_COMPANIES and
ORG_LEADERBOARD_DETAIL_ECOSYSTEM_COMPANIES with
ORG_LEADERBOARD_DETAIL_METHODOLOGY: ensure maintainer points are 10 whenever
counts.maintainer is positive and board points are 0 whenever counts.board is
zero, while recalculating affected scores as needed. Alternatively, explicitly
label both the demo data and methodology copy as illustrative so they are not
presented as matching calculations.
There was a problem hiding this comment.
Pull request overview
Adds an Org Lens leaderboard drawer for viewing category-level influence score breakdowns, currently backed by placeholder data.
Changes:
- Adds drawer UI and leaderboard row interactions.
- Adds shared score types, constants, and calculations.
- Adds Playwright coverage for opening and closing the drawer.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
packages/shared/src/utils/org-leaderboard-detail.utils.ts |
Calculates levels and category rows. |
packages/shared/src/utils/index.ts |
Exports the new utilities. |
packages/shared/src/interfaces/org-leaderboard-detail-drawer.interface.ts |
Defines drawer data types. |
packages/shared/src/interfaces/index.ts |
Exports the new interfaces. |
packages/shared/src/constants/org-leaderboard-detail-drawer.constants.ts |
Provides placeholder breakdowns and methodology. |
packages/shared/src/constants/index.ts |
Exports the new constants. |
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.ts |
Manages drawer selection and visibility. |
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html |
Makes leaderboard rows open the drawer. |
apps/lfx-one/src/app/modules/dashboards/components/org-leaderboard-detail-drawer/org-leaderboard-detail-drawer.component.ts |
Implements drawer state and derived data. |
apps/lfx-one/src/app/modules/dashboards/components/org-leaderboard-detail-drawer/org-leaderboard-detail-drawer.component.html |
Renders breakdown, methodology, and empty states. |
apps/lfx-one/e2e/org-project-detail.spec.ts |
Tests drawer interactions. |
Comments suppressed due to low confidence (3)
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html:498
- The ecosystem row has the same mouse-only interaction: it cannot receive focus or respond to Enter/Space, preventing keyboard users from opening its drawer. Add the same keyboard affordances used by other interactive table rows in this repository.
(click)="openLeaderboardDetail('ecosystem', row)">
packages/shared/src/constants/org-leaderboard-detail-drawer.constants.ts:189
- The ecosystem breakdowns also cannot be produced by these displayed rules. For example, Google and D2iQ have zero board members but receive 2 board points even though the rule says zero, and Google receives 12 event-attendance points although the tiered rule caps that category at 1. Align the placeholder allocations with the methodology before presenting them together.
{ label: 'Board Members', text: '— 1 pt if the company has 1+ foundation board members, else 0.' },
{
label: 'Event Attendance, Event Sponsorships, Meetup Attendance, Certified Individuals',
text: '— tiered by share: 0 / 0.33 / 0.66 / 1 pt for none / up to 25% / up to 50% / over 50%.',
apps/lfx-one/e2e/org-project-detail.spec.ts:345
- This substring locator likewise matches the page's existing
Ecosystem Influence Leaderboardheading as well as the drawer subtitle, causing a strict-mode failure. Scope it to the opened drawer so the test validates the intended dimension label.
await expect(page.getByText('Ecosystem Influence')).toBeVisible();
| [class.bg-blue-50]="row.isViewingOrg" | ||
| [attr.data-testid]="row.isViewingOrg ? 'project-detail-leaderboard-technical-viewing-row' : null"> | ||
| [attr.data-testid]="row.isViewingOrg ? 'project-detail-leaderboard-technical-viewing-row' : null" | ||
| (click)="openLeaderboardDetail('technical', row)"> |
| score: 62, | ||
| points: { collab: 18, meeting: 7, event: 5, committee: 6, board: 4, speakers: 5, meetup: 2, sponsor: 2, certified: 2 }, |
| position="right" | ||
| [modal]="true" | ||
| [showCloseIcon]="false" | ||
| styleClass="xl:w-[40%] lg:w-[50%] md:w-[65%] sm:w-[85%] w-full" |
| intro: | ||
| 'Only four categories earn technical-influence points. Each earns points independently — there are no preset weights per category. Points from all categories are added together to get the total score.', | ||
| bullets: [ | ||
| { label: 'Maintainers', text: '— 10 pts if the company has 1+ project maintainers, else 0.' }, |
| export function orgLeaderboardDetailCategoryRows( | ||
| categories: OrgLeaderboardDetailCategory[], | ||
| points: Record<string, number>, | ||
| counts: Record<string, number>, | ||
| score: number | ||
| ): OrgLeaderboardDetailCategoryRow[] { |
|
|
||
| await expect(page.getByTestId('org-leaderboard-detail-title')).toBeVisible(); | ||
| await expect(page.getByTestId('org-leaderboard-detail-title')).toHaveText(orgName); | ||
| await expect(page.getByText('Technical Influence')).toBeVisible(); |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 104602c. Configure here.
| <span class="font-semibold text-blue-600">#{{ company()?.rank }}</span> | ||
| out of | ||
| <span class="font-semibold text-blue-600">{{ totalCompanies() }}</span> | ||
| companies participating in the project. |
There was a problem hiding this comment.
Summary sentence whitespace stripped
Medium Severity
The summary sentence separates styled spans from the following text with newline/indent only. With Angular's default preserveWhitespaces: false, those whitespace-only nodes are removed, so the copy runs together (for example level jammed into the dimension word, and % jammed into of).
Triggered by learned rule: Flag whitespace-only text nodes between interpolation and @if/@for blocks — stripped by preserveWhitespaces: false
Reviewed by Cursor Bugbot for commit 104602c. Configure here.
|
|
||
| await expect(page.getByTestId('org-leaderboard-detail-title')).toBeVisible(); | ||
| await expect(page.getByTestId('org-leaderboard-detail-title')).toHaveText(orgName); | ||
| await expect(page.getByText('Ecosystem Influence')).toBeVisible(); |
There was a problem hiding this comment.
Ambiguous e2e influence text match
Medium Severity
getByText('Technical Influence') and getByText('Ecosystem Influence') also match the leaderboard board titles already on the page (Technical/Ecosystem Influence Leaderboard). After the drawer opens, Playwright strict mode can resolve multiple visible matches and fail the assertion.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 104602c. Configure here.
🚀 Deployment StatusYour branch has been deployed to: https://ui-pr-1276.dev.v2.cluster.linuxfound.info Deployment Details:
The deployment will be automatically removed when this PR is closed. |
Replaces the inline level chip with plain colored text, shrinks the total-score value out of hero-stat territory, and gives the methodology box's paragraphs and bullets explicit, consistent size/weight classes instead of relying on inherited defaults. LFXV2-2934 Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (7)
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html:368
- These rows are mouse-only: adding
(click)without a focus target or keyboard handlers prevents keyboard users from opening the drawer. Match the repository's interactive-row pattern (for example,foundation-row.component.html:4-10) by making the row focusable and handling Enter/Space.
class="cursor-pointer border-b border-gray-100 hover:bg-blue-50"
[class.bg-blue-50]="row.isViewingOrg"
[attr.data-testid]="row.isViewingOrg ? 'project-detail-leaderboard-technical-viewing-row' : null"
(click)="openLeaderboardDetail('technical', row)">
apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html:498
- This second clickable row has the same mouse-only behavior, so keyboard users cannot open the ecosystem drawer. Make it focusable and activate it on Enter/Space as the existing interactive-row components do.
class="cursor-pointer border-b border-gray-100 hover:bg-blue-50"
[class.bg-blue-50]="row.isViewingOrg"
[attr.data-testid]="row.isViewingOrg ? 'project-detail-leaderboard-ecosystem-viewing-row' : null"
(click)="openLeaderboardDetail('ecosystem', row)">
packages/shared/src/constants/org-leaderboard-detail-drawer.constants.ts:131
- VMware's ecosystem category points sum to 51, but
scoreis 62. Because percentages usescoreas their denominator and the drawer labels the score as the category total, this renders a breakdown totaling only about 81% while showing “Total score 62 pts.” Reconcile the score and category points.
score: 62,
points: { collab: 18, meeting: 7, event: 5, committee: 6, board: 4, speakers: 5, meetup: 2, sponsor: 2, certified: 2 },
packages/shared/src/constants/org-leaderboard-detail-drawer.constants.ts:170
- The displayed methodology says any company with maintainers earns exactly 10 points, but every technical fixture with a positive maintainer count assigns a different value (for example, Google shows 1 maintainer and 6 points). Users therefore see a breakdown that contradicts the “How this score is calculated” text in the same drawer. Make the fixtures follow this rule or adjust the methodology/demo model consistently.
{ label: 'Maintainers', text: '— 10 pts if the company has 1+ project maintainers, else 0.' },
apps/lfx-one/e2e/org-project-detail.spec.ts:333
getByTextperforms substring matching by default, and the page already contains “Technical Influence Leaderboard,” so after the drawer opens this locator matches both that heading and the drawer subtitle and fails strict-mode resolution. Use an exact match for the drawer label.
await expect(page.getByText('Technical Influence')).toBeVisible();
apps/lfx-one/e2e/org-project-detail.spec.ts:345
- This fuzzy text locator also matches the existing “Ecosystem Influence Leaderboard” heading in addition to the drawer subtitle, causing a Playwright strict-mode failure. Scope it to the drawer or request an exact match.
await expect(page.getByText('Ecosystem Influence')).toBeVisible();
packages/shared/src/utils/org-leaderboard-detail.utils.ts:15
- The new threshold and percentage calculations have no deterministic unit coverage. The added live-data E2E explicitly accepts either content or empty state, so it may never execute these helpers with a demo organization and cannot catch boundary, sorting, zero-score, or percentage regressions. Add a Vitest spec alongside this utility covering both dimensions' thresholds and category-row calculation.
export function orgLeaderboardDetailLevelFor(dimension: LeaderboardDimension, score: number): OrgLeaderboardDetailLevel {


Business case: show details of how influence leaderboards are calculated.
This pull request adds a new leaderboard row score-breakdown drawer to the Org Project Detail page, allowing users to click on leaderboard rows to view a detailed breakdown of an organization's influence score. The drawer displays demo data or a graceful empty state if data is unavailable. The implementation includes new component files, integration into the Org Project Detail page, and end-to-end tests to ensure correct behavior.
Feature: Leaderboard Row Score-Breakdown Drawer
OrgLeaderboardDetailDrawerComponentto display a detailed breakdown of an organization's leaderboard score, including summary, category breakdown, and methodology, with demo data and an empty state fallback (org-leaderboard-detail-drawer.component.ts,org-leaderboard-detail-drawer.component.html). [1] [2]org-project-detail.component.html,org-project-detail.component.ts). [1] [2] [3] [4] [5] [6] [7]Testing
org-project-detail.spec.ts).Constants
index.ts).UI Improvements
org-project-detail.component.html). [1] [2]Summary
pd-leaderboardstab) now opens a right-side drawer showing that organization's category-level score breakdown for the clicked influence dimension.OrgLeaderboardDetailDrawerComponent(apps/lfx-one/.../dashboards/components/org-leaderboard-detail-drawer/) renders: header (org name + "{{project}} · Technical/Ecosystem Influence"), a summary sentence (level + activity % + score + rank), category rows sorted by points descending with a highlighted top category, a bold total-score line, and a methodology explainer box — matching the validated prototype design.cursor-pointer/hover styling + a(click)handler to each leaderboard's#bodyrow template; no other changes to the existing leaderboard tables.org-leaderboard-detail-drawer.interface.ts), constants (org-leaderboard-detail-drawer.constants.ts), and utils (org-leaderboard-detail.utils.ts) added to@lfx-one/shared.Demo-data caveat: category points/counts/company lookups in
org-leaderboard-detail-drawer.constants.tsare explicitly DEMO/PLACEHOLDER data (called out in file-level comments), keyed by org display name since the real leaderboard rows carry no org id. This is pending a real Snowflake-backed data source in a follow-up ticket. Any org clicked that isn't in the demo lookup renders a graceful "not yet available" empty state instead of crashing — this covers the vast majority of real leaderboard rows today.JIRA: LFXV2-2934
Test plan
yarn check-typespassesyarn lintpasses (no new warnings/errors introduced)yarn buildpasses (SSR bundle builds clean)yarn format:checkpasses./check-headers.shpasses (license headers on all new files)apps/lfx-one/e2e/org-project-detail.spec.ts: clicking a technical/ecosystem row opens the drawer with the correct org name + dimension label and renders either the breakdown content or the empty state (never crashes); closing via Escape returns to the detail page