Skip to content

Commit a46f413

Browse files
authored
feat(web-ui): migrate @hugeicons/react 0.3.4 → 1.x (#869) (#873)
* feat(web-ui): migrate @hugeicons/react 0.3.4 → 1.x (#869) @hugeicons/react 1.x split the icon data (now @hugeicons/core-free-icons) from the renderer (@hugeicons/react exports only HugeiconsIcon). The previous bump attempt (#850) failed with 388 Turbopack errors because the named icon exports (Add01Icon, etc.) were removed in 1.x. This migration: - Bumps @hugeicons/react to ^1.1.9 and adds @hugeicons/core-free-icons ^4.2.2. - Rewrites every @hugeicons/react import (61 files) to import HugeiconsIcon from @hugeicons/react and the icon data from @hugeicons/core-free-icons. - Wraps every <IconName .../> JSX usage in <HugeiconsIcon icon={IconName} .../>. - Updates 7 value-typed icon maps (eventStyles, RecentActivityFeed, FileTreePanel, BlockerCard, AppSidebar, NotificationCenter, BatchExecutionMonitor, VerificationEvent) from ComponentType<>/ElementType to IconSvgElement, with consumer call sites updated to render via <HugeiconsIcon icon={...} />. - Updates the Jest mock: @hugeicons/react now mocks HugeiconsIcon; a new @hugeicons/core-free-icons mock returns stable icon-name sentinels so tests can assert on data-testid='icon-<Name>' patterns. All 56 unique icon names used in the app exist 1:1 in core-free-icons@4.2.2 — no renames needed. Free-tier (MIT) icons only; no Pro packs introduced. Verified: npm run build (Turbopack) green, npm test 1052/1052 green, npm run lint clean, tsc --noEmit clean for app source. * refactor(blockers): rename ORIGIN_CONFIG.Icon → .icon for camelCase consistency After the hugeicons 1.x migration, the Icon field holds IconSvgElement data (not a React component), so the other 6 icon-map sites were renamed to camelCase (icon, stateIcon, navIcon, config.icon, etc.). ORIGIN_CONFIG was the lone outlier still using PascalCase. Aligns it with the rest of the migration. Addresses claude[bot] review nit on PR #873. * refactor(notifications): rename iconForNotification Icon → icon for camelCase Final consistency fix: the iconForNotification() return type used a capitalized 'Icon' field, same pattern as the ORIGIN_CONFIG fix in f824c19. Now all 8 value-typed icon maps use camelCase field names (icon, stateIcon, navIcon, config.icon, origin.icon, activityIcons, changeTypeIcon, statusConfig) consistently. Addresses claude[bot] follow-up review nit on PR #873.
1 parent c9f865d commit a46f413

66 files changed

Lines changed: 407 additions & 499 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Test mock for @hugeicons/core-free-icons (1.x icon data package).
2+
//
3+
// In production, each icon is a readonly array of [tag, attrs] tuples (an
4+
// `IconSvgElement`). For unit tests we don't need real SVG data — we just
5+
// need a stable sentinel that the @hugeicons/react mock can identify. Each
6+
// accessed `XxxIcon` property returns `{ __iconName: '<name>' }`, which the
7+
// HugeiconsIcon mock renders as `<svg data-testid="icon-<name>" />`.
8+
//
9+
// A Proxy keeps this resilient to new icons being added without touching
10+
// this file.
11+
12+
module.exports = new Proxy(
13+
{},
14+
{
15+
get(_target, prop) {
16+
if (typeof prop !== 'string') return undefined;
17+
// Non-string symbols (e.g. Symbol.toStringTag, inspect customs) → undefined.
18+
// Any string property access returns a stable sentinel object.
19+
return { __iconName: prop };
20+
},
21+
},
22+
);
Lines changed: 17 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,23 @@
11
const React = require('react');
22

3-
const createIconMock = (name) => {
4-
const IconComponent = React.forwardRef((props, ref) => {
5-
return React.createElement('svg', {
6-
'data-testid': `icon-${name}`,
7-
ref,
8-
...props,
9-
});
3+
const HugeiconsIcon = React.forwardRef(function HugeiconsIcon(
4+
{ icon, altIcon, showAlt, size, color, strokeWidth, absoluteStrokeWidth, primaryColor, secondaryColor, disableSecondaryOpacity, ...rest },
5+
ref,
6+
) {
7+
const iconName = icon && typeof icon === 'object' && '__iconName' in icon ? icon.__iconName : 'unknown';
8+
const effectiveIcon = showAlt && altIcon ? altIcon : icon;
9+
const effectiveName =
10+
effectiveIcon && typeof effectiveIcon === 'object' && '__iconName' in effectiveIcon
11+
? effectiveIcon.__iconName
12+
: iconName;
13+
return React.createElement('svg', {
14+
'data-testid': `icon-${effectiveName}`,
15+
ref,
16+
...rest,
1017
});
11-
IconComponent.displayName = name;
12-
return IconComponent;
13-
};
18+
});
1419

1520
module.exports = {
16-
// WorkspaceHeader
17-
Folder01Icon: createIconMock('Folder01Icon'),
18-
Loading03Icon: createIconMock('Loading03Icon'),
19-
// WorkspaceStatsCards
20-
CodeIcon: createIconMock('CodeIcon'),
21-
Task01Icon: createIconMock('Task01Icon'),
22-
PlayIcon: createIconMock('PlayIcon'),
23-
// QuickActions
24-
FileEditIcon: createIconMock('FileEditIcon'),
25-
GitBranchIcon: createIconMock('GitBranchIcon'),
26-
// RecentActivityFeed
27-
Time01Icon: createIconMock('Time01Icon'),
28-
CheckmarkCircle01Icon: createIconMock('CheckmarkCircle01Icon'),
29-
Alert02Icon: createIconMock('Alert02Icon'),
30-
// WorkspaceSelector
31-
Cancel01Icon: createIconMock('Cancel01Icon'),
32-
// PRD components
33-
Upload04Icon: createIconMock('Upload04Icon'),
34-
MessageSearch01Icon: createIconMock('MessageSearch01Icon'),
35-
TaskEdit01Icon: createIconMock('TaskEdit01Icon'),
36-
TestTube01Icon: createIconMock('TestTube01Icon'),
37-
ArtificialIntelligence01Icon: createIconMock('ArtificialIntelligence01Icon'),
38-
SentIcon: createIconMock('SentIcon'),
39-
// AppSidebar
40-
Home01Icon: createIconMock('Home01Icon'),
41-
Add01Icon: createIconMock('Add01Icon'),
42-
// PipelineProgressBar
43-
Tick01Icon: createIconMock('Tick01Icon'),
44-
// Task Board components
45-
PlayCircleIcon: createIconMock('PlayCircleIcon'),
46-
LinkCircleIcon: createIconMock('LinkCircleIcon'),
47-
ViewIcon: createIconMock('ViewIcon'),
48-
Search01Icon: createIconMock('Search01Icon'),
49-
CheckListIcon: createIconMock('CheckListIcon'),
50-
// Execution Monitor components
51-
Idea01Icon: createIconMock('Idea01Icon'),
52-
ArrowTurnBackwardIcon: createIconMock('ArrowTurnBackwardIcon'),
53-
CommandLineIcon: createIconMock('CommandLineIcon'),
54-
AlertDiamondIcon: createIconMock('AlertDiamondIcon'),
55-
WifiDisconnected01Icon: createIconMock('WifiDisconnected01Icon'),
56-
SidebarLeftIcon: createIconMock('SidebarLeftIcon'),
57-
ArrowDown01Icon: createIconMock('ArrowDown01Icon'),
58-
ArrowUp01Icon: createIconMock('ArrowUp01Icon'),
59-
StopIcon: createIconMock('StopIcon'),
60-
// AgentChatPanel
61-
ArrowRight01Icon: createIconMock('ArrowRight01Icon'),
62-
Alert01Icon: createIconMock('Alert01Icon'),
63-
// SplitPane
64-
ArrowLeft01Icon: createIconMock('ArrowLeft01Icon'),
65-
// Proof page
66-
InformationCircleIcon: createIconMock('InformationCircleIcon'),
67-
// FileTreePanel / DiffViewer
68-
FileAddIcon: createIconMock('FileAddIcon'),
69-
FileRemoveIcon: createIconMock('FileRemoveIcon'),
70-
// BlockerCard origin badges
71-
Settings01Icon: createIconMock('Settings01Icon'),
72-
UserCircle02Icon: createIconMock('UserCircle02Icon'),
73-
// PRHistoryPanel
74-
ArrowUpRight01Icon: createIconMock('ArrowUpRight01Icon'),
75-
// Costs page
76-
MoneyBag02Icon: createIconMock('MoneyBag02Icon'),
77-
Analytics01Icon: createIconMock('Analytics01Icon'),
78-
ChartLineData01Icon: createIconMock('ChartLineData01Icon'),
79-
// NotificationCenter
80-
Notification02Icon: createIconMock('Notification02Icon'),
81-
// GitHub issue import (issue #564)
82-
GithubIcon: createIconMock('GithubIcon'),
83-
// Auth / login (issue #336)
84-
Login01Icon: createIconMock('Login01Icon'),
85-
Logout01Icon: createIconMock('Logout01Icon'),
86-
LockIcon: createIconMock('LockIcon'),
87-
Mail01Icon: createIconMock('Mail01Icon'),
21+
HugeiconsIcon,
22+
default: HugeiconsIcon,
8823
};

web-ui/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const customJestConfig = {
1212
moduleNameMapper: {
1313
'^@/(.*)$': '<rootDir>/src/$1',
1414
'^@hugeicons/react$': '<rootDir>/__mocks__/@hugeicons/react.js',
15+
'^@hugeicons/core-free-icons$': '<rootDir>/__mocks__/@hugeicons/core-free-icons.js',
1516
},
1617
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
1718
testPathIgnorePatterns: [

web-ui/package-lock.json

Lines changed: 13 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web-ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"test:e2e:headed": "npx playwright test --headed"
1616
},
1717
"dependencies": {
18-
"@hugeicons/react": "^0.3.0",
18+
"@hugeicons/core-free-icons": "^4.2.2",
19+
"@hugeicons/react": "^1.1.9",
1920
"@radix-ui/react-alert-dialog": "^1.1.19",
2021
"@radix-ui/react-checkbox": "^1.3.7",
2122
"@radix-ui/react-dialog": "^1.1.15",

web-ui/src/app/costs/page.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22

33
import { useState } from 'react';
44
import useSWR from 'swr';
5-
import {
6-
MoneyBag02Icon,
7-
Task01Icon,
8-
Analytics01Icon,
9-
} from '@hugeicons/react';
5+
import { HugeiconsIcon } from '@hugeicons/react';
6+
import { MoneyBag02Icon, Task01Icon, Analytics01Icon } from '@hugeicons/core-free-icons';
107
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
118
import { SpendBarChart } from '@/components/costs/SpendBarChart';
129
import { TopTasksTable } from '@/components/costs/TopTasksTable';
@@ -126,7 +123,7 @@ export default function CostsPage() {
126123
<Card>
127124
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
128125
<CardTitle className="text-sm font-medium">Total Spend</CardTitle>
129-
<MoneyBag02Icon className="h-4 w-4 text-muted-foreground" />
126+
<HugeiconsIcon icon={MoneyBag02Icon} className="h-4 w-4 text-muted-foreground" />
130127
</CardHeader>
131128
<CardContent>
132129
<p
@@ -141,7 +138,7 @@ export default function CostsPage() {
141138
<Card>
142139
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
143140
<CardTitle className="text-sm font-medium">Tasks Run</CardTitle>
144-
<Task01Icon className="h-4 w-4 text-muted-foreground" />
141+
<HugeiconsIcon icon={Task01Icon} className="h-4 w-4 text-muted-foreground" />
145142
</CardHeader>
146143
<CardContent>
147144
<p
@@ -156,7 +153,7 @@ export default function CostsPage() {
156153
<Card>
157154
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
158155
<CardTitle className="text-sm font-medium">Avg Cost / Task</CardTitle>
159-
<Analytics01Icon className="h-4 w-4 text-muted-foreground" />
156+
<HugeiconsIcon icon={Analytics01Icon} className="h-4 w-4 text-muted-foreground" />
160157
</CardHeader>
161158
<CardContent>
162159
<p

web-ui/src/app/execution/page.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import { Suspense, useState, useEffect } from 'react';
44
import { useSearchParams, useRouter } from 'next/navigation';
55
import Link from 'next/link';
6-
import { Loading03Icon } from '@hugeicons/react';
6+
import { HugeiconsIcon } from '@hugeicons/react';
7+
import { Loading03Icon } from '@hugeicons/core-free-icons';
78
import { tasksApi } from '@/lib/api';
89
import { BatchExecutionMonitor } from '@/components/execution/BatchExecutionMonitor';
910
import { WorkspaceSelector } from '@/components/workspace/WorkspaceSelector';
@@ -20,7 +21,7 @@ export default function ExecutionLandingPage() {
2021
<Suspense
2122
fallback={
2223
<main className="flex min-h-screen items-center justify-center bg-background">
23-
<Loading03Icon className="h-6 w-6 animate-spin text-muted-foreground" />
24+
<HugeiconsIcon icon={Loading03Icon} className="h-6 w-6 animate-spin text-muted-foreground" />
2425
</main>
2526
}
2627
>
@@ -109,7 +110,7 @@ function ExecutionLandingContent() {
109110
if (taskIdParam || resolving) {
110111
return (
111112
<main className="flex min-h-screen items-center justify-center bg-background">
112-
<Loading03Icon className="h-6 w-6 animate-spin text-muted-foreground" />
113+
<HugeiconsIcon icon={Loading03Icon} className="h-6 w-6 animate-spin text-muted-foreground" />
113114
</main>
114115
);
115116
}

0 commit comments

Comments
 (0)