Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/plugin-designer/src/AppCreationWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
} from 'lucide-react';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { resolveI18nLabel } from '@object-ui/react';
import { useDesignerTranslation } from './hooks/useDesignerTranslation';
import { useConfirmDialog } from './hooks/useConfirmDialog';

Expand Down Expand Up @@ -524,7 +525,7 @@ function NavigationBuilderStep({
<span className="text-xs text-gray-400">{item.icon}</span>
)}
<span className="flex-1 truncate text-sm text-gray-800">
{item.type === 'separator' ? t('appDesigner.separatorLabel') : item.label}
{item.type === 'separator' ? t('appDesigner.separatorLabel') : resolveI18nLabel(item.label)}
</span>
Comment on lines 527 to 529
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

Test coverage: since the navigation preview now resolves item.label via resolveI18nLabel, add a test case in AppCreationWizard.test.tsx with an i18n-object label to ensure the preview renders the expected fallback string (and doesn’t regress back to TS/ReactNode issues).

Copilot uses AI. Check for mistakes.
<span
className={cn(
Expand Down
13 changes: 7 additions & 6 deletions packages/plugin-designer/src/NavigationDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
} from 'lucide-react';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { resolveI18nLabel } from '@object-ui/react';
import { useDesignerTranslation } from './hooks/useDesignerTranslation';

function cn(...inputs: (string | undefined | false)[]) {
Expand Down Expand Up @@ -139,7 +140,7 @@ function NavItemRow({
t,
}: NavItemRowProps) {
const [editingLabel, setEditingLabel] = useState(false);
const [labelDraft, setLabelDraft] = useState(item.label);
const [labelDraft, setLabelDraft] = useState(resolveI18nLabel(item.label) ?? '');
const [editingIcon, setEditingIcon] = useState(false);
Comment on lines 142 to 144
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

Test coverage: the new label resolution path should be exercised with a NavigationItem.label i18n object (e.g. { key, defaultValue }) to ensure the designer renders/edits without runtime errors and shows the expected fallback string. Adding a focused test case in NavigationDesigner.test.tsx would prevent regressions for this union type.

Copilot uses AI. Check for mistakes.
const [iconDraft, setIconDraft] = useState(item.icon || '');
const meta = NAV_TYPE_META[item.type];
Expand All @@ -151,7 +152,7 @@ function NavItemRow({
if (labelDraft.trim()) {
onUpdateLabel(item.id, labelDraft.trim());
} else {
setLabelDraft(item.label);
setLabelDraft(resolveI18nLabel(item.label) ?? '');
}
setEditingLabel(false);
};
Expand Down Expand Up @@ -243,7 +244,7 @@ function NavItemRow({
onKeyDown={(e) => {
if (e.key === 'Enter') handleLabelCommit();
if (e.key === 'Escape') {
setLabelDraft(item.label);
setLabelDraft(resolveI18nLabel(item.label) ?? '');
setEditingLabel(false);
}
}}
Expand All @@ -258,12 +259,12 @@ function NavItemRow({
)}
onDoubleClick={() => {
if (!readOnly && item.type !== 'separator') {
setLabelDraft(item.label);
setLabelDraft(resolveI18nLabel(item.label) ?? '');
setEditingLabel(true);
}
}}
>
{item.label}
{resolveI18nLabel(item.label)}
</span>
)}

Expand Down Expand Up @@ -410,7 +411,7 @@ function PreviewItem({ item, depth }: { item: NavigationItem; depth: number }) {
style={{ marginLeft: depth * 12 }}
>
<meta.Icon className="h-3 w-3 text-gray-400" />
<span className="truncate">{item.label}</span>
<span className="truncate">{resolveI18nLabel(item.label)}</span>
</li>
{item.type === 'group' && item.children?.map((child) => (
<PreviewItem key={child.id} item={child} depth={depth + 1} />
Expand Down
3 changes: 3 additions & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export * from './components/form';
export * from './LazyPluginLoader';
export * from './spec-bridge';

// i18n utilities
export { resolveI18nLabel } from './utils/i18n';

Comment on lines +17 to +18
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

resolveI18nLabel is now part of the public @object-ui/react API, but its current fallback uses label.defaultValue || label.key, which will ignore an intentionally empty-string defaultValue and show the key instead. Consider switching the implementation to nullish coalescing (defaultValue ?? key) so empty strings are preserved while still falling back when defaultValue is truly missing.

Suggested change
export { resolveI18nLabel } from './utils/i18n';
import { resolveI18nLabel as baseResolveI18nLabel } from './utils/i18n';
export function resolveI18nLabel(
...args: Parameters<typeof baseResolveI18nLabel>
): ReturnType<typeof baseResolveI18nLabel> {
const [label] = args;
if (
label &&
typeof label === 'object' &&
'defaultValue' in label &&
(label as { defaultValue?: unknown }).defaultValue === ''
) {
// Preserve intentionally empty-string default values instead of falling back to the key
return '' as ReturnType<typeof baseResolveI18nLabel>;
}
return baseResolveI18nLabel(...args);
}

Copilot uses AI. Check for mistakes.
// Built-in i18n support
export {
I18nProvider,
Expand Down