[FIX] Resolve SonarQube findings from static analysis scan
SonarQube Community Edition 26.2 scan on commit 2a402dd — Feb 16, 2026.
Summary: 6 bugs, 0 vulnerabilities, 40 code smells, 0% test coverage, 0% duplication (2,195 LOC).
| Rating |
Grade |
| Security |
A |
| Reliability |
C |
| Maintainability |
A |
🔴 Bugs (6)
Missing keyboard listeners on click handlers
Non-interactive elements with onClick must also have onKeyDown/onKeyPress for keyboard and screen reader accessibility.
Fix: Add onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClick() }}, tabIndex={0}, and role="button". Or replace <td>/<div> with <button> styled as table cells.
React Hooks called in wrong context
useState and useMemo called inside a Storybook render function that doesn't start with an uppercase letter.
Fix: Extract to a named component (function InteractiveDemo() { ... }) or rename render to Render.
🔴 Critical Code Smells (3)
Cognitive complexity: projectEvents() — 37 (max 15)
The giant switch over 8 event types with nested logic. Will improve significantly with the event type refactor (#6) — recommend splitting into per-event-type handler functions:
const handlers: Record<PughEvent['type'], (state: Draft, event: PughEvent) => void> = {
CriterionAdded: (state, e) => { ... },
// ...
};
Cognitive complexity: PughMatrix render — 21 (max 15)
Fix: Extract score cell, header cell, and totals row into sub-components.
Function nesting > 4 levels deep
Fix: Extract the merge function to a named top-level helper.
🟡 Major Code Smells (14)
Non-native interactive elements missing ARIA roles (5)
Fix: Add role="button" (or role="option" for branch list items) + tabIndex={0} + keyboard handlers.
Nested ternary operations (5)
Fix: Extract to a helper function or use clsx:
const cellClass = clsx('pugh-tool-header', {
'pugh-winner-header': isWinner(tool.id),
'pugh-highlight-header': isHighlighted(tool.id),
});
CSS contrast failures (2)
Fix: Use Radix color step pairings that guarantee AA contrast (e.g., step 11 text on step 3 background).
React Hooks in Storybook render (2)
Covered above in Bugs section — same root cause.
🟢 Minor Code Smells (25)
Readonly props (4)
Component props interfaces should use Readonly<>:
Prefer Number.isNaN over isNaN (3)
Prefer String#replaceAll() over String#replace() (2)
Prefer globalThis over window (8)
Unexpected negated conditions (4)
Unused import + unnecessary assertion (2)
Acceptance Criteria
[FIX] Resolve SonarQube findings from static analysis scan
SonarQube Community Edition 26.2 scan on commit
2a402dd— Feb 16, 2026.Summary: 6 bugs, 0 vulnerabilities, 40 code smells, 0% test coverage, 0% duplication (2,195 LOC).
🔴 Bugs (6)
Missing keyboard listeners on click handlers
Non-interactive elements with
onClickmust also haveonKeyDown/onKeyPressfor keyboard and screen reader accessibility.src/PughMatrix.tsx#L266src/PughMatrix.tsx#L314src/PughMatrix.tsx#L379src/BranchSelector.tsx#L105Fix: Add
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClick() }},tabIndex={0}, androle="button". Or replace<td>/<div>with<button>styled as table cells.React Hooks called in wrong context
useStateanduseMemocalled inside a Storybookrenderfunction that doesn't start with an uppercase letter.src/PughMatrix.stories.tsx#L267useStateinrendersrc/PughMatrix.stories.tsx#L268useMemoinrenderFix: Extract to a named component (
function InteractiveDemo() { ... }) or renamerendertoRender.🔴 Critical Code Smells (3)
Cognitive complexity:
projectEvents()— 37 (max 15)src/events/projection.ts#L4function projectEvents(events: PughEvent[])The giant
switchover 8 event types with nested logic. Will improve significantly with the event type refactor (#6) — recommend splitting into per-event-type handler functions:Cognitive complexity:
PughMatrixrender — 21 (max 15)src/PughMatrix.tsx#L353Fix: Extract score cell, header cell, and totals row into sub-components.
Function nesting > 4 levels deep
src/store/createPughStore.ts#L207persist→merge→ lambda nestingFix: Extract the
mergefunction to a named top-level helper.🟡 Major Code Smells (14)
Non-native interactive elements missing ARIA roles (5)
src/BranchSelector.tsx#L105src/BranchSelector.tsx#L125src/PughMatrix.tsx#L266src/PughMatrix.tsx#L314src/PughMatrix.tsx#L379Fix: Add
role="button"(orrole="option"for branch list items) +tabIndex={0}+ keyboard handlers.Nested ternary operations (5)
src/PughMatrix.tsx#L262isWinner ? ... : isHighlighted ? ...src/PughMatrix.tsx#L288src/PughMatrix.tsx#L368src/PughMatrix.tsx#L426src/PughMatrix.tsx#L483Fix: Extract to a helper function or use
clsx:CSS contrast failures (2)
src/pugh-matrix.css#L480src/pugh-matrix.css#L486Fix: Use Radix color step pairings that guarantee AA contrast (e.g., step 11 text on step 3 background).
React Hooks in Storybook render (2)
Covered above in Bugs section — same root cause.
🟢 Minor Code Smells (25)
Readonlyprops (4)Component props interfaces should use
Readonly<>:src/PughMatrix.tsx#L63PughMatrixPropssrc/BranchSelector.tsx#L8BranchSelectorPropssrc/PughMatrix.stories.tsx#L109src/store/PughStoreProvider.tsx#L10Prefer
Number.isNaNoverisNaN(3)src/PughMatrix.tsxPrefer
String#replaceAll()overString#replace()(2)src/PughMatrix.tsxPrefer
globalThisoverwindow(8)src/persist/localStoragePersister.tsUnexpected negated conditions (4)
src/PughMatrix.tsxsrc/persist/localStoragePersister.tsUnused import + unnecessary assertion (2)
src/store/createPughStore.ts#L5PughDomainStatesrc/store/createPughStore.ts#L63Acceptance Criteria