Skip to content

Commit d64c5eb

Browse files
fix: address code review feedback - extract capitalizeFirst utility, fix aria attrs, clean up
- Extract shared capitalizeFirst() utility to utils.ts, use in RecentApps/StarredApps - Remove redundant aria-label on select-all Checkbox (already has associated label) - Add id to per-item Checkbox in AppManagementPage - Fix aria-required expression to use ternary instead of falsy OR - Remove redundant className="text-xs" from Badge in AppCard - Remove conflicting readOnly attr from disabled role input in ProfilePage Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/f23eebba-1576-4704-8d15-fa21129d9261 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
1 parent a68ae67 commit d64c5eb

File tree

8 files changed

+26
-6
lines changed

8 files changed

+26
-6
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Improved
11+
12+
- **Console UI design optimization sweep** (`@object-ui/console`): Comprehensive accessibility and design consistency improvements across all major console interfaces:
13+
- **Accessibility (WCAG 2.1 AA)**: Added `aria-label`, `aria-pressed`, `aria-required`, `aria-live`, `role="status"`, `role="link"`, `role="toolbar"`, `role="img"` attributes across HomePage, SystemHubPage, MetadataManagerPage, MetadataDetailPage, AppManagementPage, ProfilePage, SearchResultsPage, AuthPageLayout, and DashboardView. Added keyboard navigation (Enter/Space) to all clickable Card components. Added `<title>` element to SVG logo. Added screen-reader-only `<label>` elements for search inputs.
14+
- **Design tokens**: Replaced hardcoded `bg-blue-50`/`text-blue-700` badge in AppCard with Shadcn `<Badge variant="secondary">` for consistent theming.
15+
- **Shadcn component alignment**: Replaced raw `<input type="checkbox">` elements in AppManagementPage with Shadcn `<Checkbox>` component for consistent styling and accessibility.
16+
- **Spacing consistency**: Standardized responsive padding (`px-4 sm:px-6`) on HomePage, unified grid columns across RecentApps/StarredApps (4 columns at xl), standardized `gap-4` in MetadataManagerPage grid, graduated padding (`p-2 sm:p-4 md:p-6`) in DashboardView.
17+
- **Focus management**: Added `focus-visible:opacity-100` on AppCard favorite button so keyboard users can discover it, added focus rings to search result card links, added `focus-visible:ring-2` to DashboardView widget toolbar buttons and MetadataFormDialog native select.
18+
- **i18n compatibility**: Replaced CSS `capitalize` with programmatic string casing in RecentApps/StarredApps type labels.
19+
1020
### Fixed
1121

1222
- **Home page star/favorite not reactive** (`@object-ui/console`): Migrated `useFavorites` from standalone hook to React Context (`FavoritesProvider`) so all consumers (HomePage, AppCard, AppSidebar, UnifiedSidebar) share a single state instance. Previously, each component calling `useFavorites()` created independent state, so toggling a favorite in AppCard did not trigger re-render in HomePage. localStorage persistence is retained as the storage layer.

apps/console/src/components/MetadataFormDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export function MetadataFormDialog({
149149
handleChange(field.key, e.target.value)
150150
}
151151
disabled={disabled}
152-
aria-required={field.required || undefined}
152+
aria-required={field.required ? true : undefined}
153153
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
154154
data-testid={`metadata-field-${field.key}`}
155155
>

apps/console/src/pages/home/AppCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export function AppCard({ app, onClick, isFavorite }: AppCardProps) {
9090
{/* App Badge (if default) */}
9191
{app.isDefault && (
9292
<div className="mt-3">
93-
<Badge variant="secondary" className="text-xs">
93+
<Badge variant="secondary">
9494
{t('home.appCard.default', { defaultValue: 'Default' })}
9595
</Badge>
9696
</div>

apps/console/src/pages/home/RecentApps.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useObjectTranslation } from '@object-ui/i18n';
1111
import { Card, CardContent } from '@object-ui/components';
1212
import { Clock } from 'lucide-react';
1313
import { getIcon } from '../../utils/getIcon';
14+
import { capitalizeFirst } from '../../utils';
1415
import type { RecentItem } from '../../hooks/useRecentItems';
1516

1617
interface RecentAppsProps {
@@ -56,7 +57,7 @@ export function RecentApps({ items }: RecentAppsProps) {
5657
</div>
5758
<div className="flex-1 min-w-0">
5859
<h3 className="font-medium text-sm truncate">{item.label}</h3>
59-
<p className="text-xs text-muted-foreground">{item.type.charAt(0).toUpperCase() + item.type.slice(1)}</p>
60+
<p className="text-xs text-muted-foreground">{capitalizeFirst(item.type)}</p>
6061
</div>
6162
</div>
6263
</CardContent>

apps/console/src/pages/home/StarredApps.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useObjectTranslation } from '@object-ui/i18n';
1111
import { Card, CardContent } from '@object-ui/components';
1212
import { Star } from 'lucide-react';
1313
import { getIcon } from '../../utils/getIcon';
14+
import { capitalizeFirst } from '../../utils';
1415
import type { FavoriteItem } from '../../hooks/useFavorites';
1516

1617
interface StarredAppsProps {
@@ -56,7 +57,7 @@ export function StarredApps({ items }: StarredAppsProps) {
5657
</div>
5758
<div className="flex-1 min-w-0">
5859
<h3 className="font-medium text-sm truncate">{item.label}</h3>
59-
<p className="text-xs text-muted-foreground">{item.type.charAt(0).toUpperCase() + item.type.slice(1)}</p>
60+
<p className="text-xs text-muted-foreground">{capitalizeFirst(item.type)}</p>
6061
</div>
6162
</div>
6263
</CardContent>

apps/console/src/pages/system/AppManagementPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ export function AppManagementPage() {
177177
id="select-all-apps"
178178
checked={selectedIds.size === filteredApps.length && filteredApps.length > 0}
179179
onCheckedChange={() => toggleSelectAll()}
180-
aria-label="Select all apps"
181180
/>
182181
<label htmlFor="select-all-apps" className="cursor-pointer">Select all ({filteredApps.length})</label>
183182
</div>
@@ -200,6 +199,7 @@ export function AppManagementPage() {
200199
<Card key={app.name} className={!isActive ? 'opacity-60' : ''} data-testid={`app-card-${app.name}`}>
201200
<CardContent className="flex items-center gap-3 py-3 px-4">
202201
<Checkbox
202+
id={`select-app-${app.name}`}
203203
checked={selectedIds.has(app.name)}
204204
onCheckedChange={() => toggleSelect(app.name)}
205205
aria-label={`Select ${app.label || app.name}`}

apps/console/src/pages/system/ProfilePage.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ export function ProfilePage() {
129129
type="text"
130130
value={user.role ?? 'member'}
131131
disabled
132-
readOnly
133132
className="bg-muted text-muted-foreground"
134133
/>
135134
</div>

apps/console/src/utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ export function resolveI18nLabel(
2121
return label.defaultValue || label.key;
2222
}
2323

24+
/**
25+
* Capitalize the first letter of a string.
26+
* Preferred over CSS `capitalize` for i18n compatibility.
27+
*/
28+
export function capitalizeFirst(str: string): string {
29+
if (!str) return str;
30+
return str.charAt(0).toUpperCase() + str.slice(1);
31+
}
32+
2433
/**
2534
* Format a record title using the titleFormat pattern
2635
* @param titleFormat Pattern like "{name} - {email}" or "{firstName} {lastName}"

0 commit comments

Comments
 (0)