Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 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
11 changes: 11 additions & 0 deletions messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,11 +744,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "حفظ الرابط"
},
"iconPicker": {
"title": "اختر أيقونة",
"searchPlaceholder": "ابحث عن أيقونة",
"emptyMessage": "لم يتم العثور على أيقونات"
},
"customFeed": {
"createTitle": "إنشاء خلاصة",
"editTitle": "تعديل الخلاصة",
"feedName": "اسم الخلاصة",
"feedNamePlaceholder": "ليست مفاتيحك...",
"feedIcon": "أيقونة الخلاصة",
"chooseIcon": "تحديد أيقونة",
"feedIconCreateDescription": "اختر أيقونة مخصصة لخلاصتك الجديدة.",
"feedIconEditDescription": "اختر أيقونة مخصصة لخلاصتك.",
"editFeedLabel": "تعديل {name}",
"moreFeeds": "المزيد من الخلاصات",
"reach": "النطاق",
"reachPlaceholder": "اختر نطاقاً",
"sort": "الترتيب",
Expand Down
11 changes: 11 additions & 0 deletions messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,11 +744,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "Link speichern"
},
"iconPicker": {
"title": "Symbol auswählen",
"searchPlaceholder": "Nach Symbol suchen",
"emptyMessage": "Keine Symbole gefunden"
},
"customFeed": {
"createTitle": "Feed erstellen",
"editTitle": "Feed bearbeiten",
"feedName": "Feed-Name",
"feedNamePlaceholder": "Nicht deine Schlüssel...",
"feedIcon": "Feed-Symbol",
"chooseIcon": "Symbol auswählen",
"feedIconCreateDescription": "Wähle ein eigenes Symbol für deinen neuen Feed.",
"feedIconEditDescription": "Wähle ein eigenes Symbol für deinen Feed.",
"editFeedLabel": "{name} bearbeiten",
"moreFeeds": "Weitere Feeds",
"reach": "Reichweite",
"reachPlaceholder": "Reichweite auswählen",
"sort": "Sortierung",
Expand Down
11 changes: 11 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,11 +742,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "Save link"
},
"iconPicker": {
"title": "Choose icon",
"searchPlaceholder": "Search for icon",
"emptyMessage": "No icons found"
},
"customFeed": {
"createTitle": "Create Feed",
"editTitle": "Edit Feed",
"feedName": "Feed Name",
"feedNamePlaceholder": "Not your keys...",
"feedIcon": "Feed Icon",
"chooseIcon": "Select icon",
"feedIconCreateDescription": "Choose a custom icon for your new feed.",
"feedIconEditDescription": "Choose a custom icon for your feed.",
"editFeedLabel": "Edit {name}",
"moreFeeds": "More feeds",
"reach": "Reach",
"reachPlaceholder": "Select a reach",
"sort": "Sort",
Expand Down
11 changes: 11 additions & 0 deletions messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,11 +744,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "Guardar enlace"
},
"iconPicker": {
"title": "Elegir icono",
"searchPlaceholder": "Buscar un icono",
"emptyMessage": "No se encontraron iconos"
},
"customFeed": {
"createTitle": "Crear Feed",
"editTitle": "Editar Feed",
"feedName": "Nombre del Feed",
"feedNamePlaceholder": "No son tus llaves...",
"feedIcon": "Icono del Feed",
"chooseIcon": "Seleccionar icono",
"feedIconCreateDescription": "Elige un icono personalizado para tu nuevo feed.",
"feedIconEditDescription": "Elige un icono personalizado para tu feed.",
"editFeedLabel": "Editar {name}",
"moreFeeds": "Más feeds",
"reach": "Alcance",
"reachPlaceholder": "Selecciona un alcance",
"sort": "Ordenar",
Expand Down
11 changes: 11 additions & 0 deletions messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,11 +742,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "Enregistrer le lien"
},
"iconPicker": {
"title": "Choisir une icône",
"searchPlaceholder": "Rechercher une icône",
"emptyMessage": "Aucune icône trouvée"
},
"customFeed": {
"createTitle": "Creer un flux",
"editTitle": "Modifier le flux",
"feedName": "Nom du flux",
"feedNamePlaceholder": "Pas vos cles...",
"feedIcon": "Icône du flux",
"chooseIcon": "Sélectionner une icône",
"feedIconCreateDescription": "Choisissez une icône personnalisée pour votre nouveau flux.",
"feedIconEditDescription": "Choisissez une icône personnalisée pour votre flux.",
"editFeedLabel": "Modifier {name}",
"moreFeeds": "Plus de flux",
"reach": "Portee",
"reachPlaceholder": "Selectionner une portee",
"sort": "Trier",
Expand Down
11 changes: 11 additions & 0 deletions messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,11 +742,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "Salva link"
},
"iconPicker": {
"title": "Scegli icona",
"searchPlaceholder": "Cerca un'icona",
"emptyMessage": "Nessuna icona trovata"
},
"customFeed": {
"createTitle": "Crea Feed",
"editTitle": "Modifica Feed",
"feedName": "Nome del Feed",
"feedNamePlaceholder": "Non le tue chiavi...",
"feedIcon": "Icona del Feed",
"chooseIcon": "Seleziona icona",
"feedIconCreateDescription": "Scegli un'icona personalizzata per il tuo nuovo feed.",
"feedIconEditDescription": "Scegli un'icona personalizzata per il tuo feed.",
"editFeedLabel": "Modifica {name}",
"moreFeeds": "Altri feed",
"reach": "Portata",
"reachPlaceholder": "Seleziona una portata",
"sort": "Ordina",
Expand Down
11 changes: 11 additions & 0 deletions messages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,11 +742,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "リンクを保存"
},
"iconPicker": {
"title": "アイコンを選択",
"searchPlaceholder": "アイコンを検索",
"emptyMessage": "アイコンが見つかりません"
},
"customFeed": {
"createTitle": "フィードを作成",
"editTitle": "フィードを編集",
"feedName": "フィード名",
"feedNamePlaceholder": "あなたの鍵ではない...",
"feedIcon": "フィードアイコン",
"chooseIcon": "アイコンを選択",
"feedIconCreateDescription": "新しいフィード用のカスタムアイコンを選択してください。",
"feedIconEditDescription": "フィード用のカスタムアイコンを選択してください。",
"editFeedLabel": "{name}を編集",
"moreFeeds": "その他のフィード",
"reach": "範囲",
"reachPlaceholder": "範囲を選択",
"sort": "並べ替え",
Expand Down
11 changes: 11 additions & 0 deletions messages/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,11 +742,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "Salvar link"
},
"iconPicker": {
"title": "Escolher ícone",
"searchPlaceholder": "Buscar um ícone",
"emptyMessage": "Nenhum ícone encontrado"
},
"customFeed": {
"createTitle": "Criar Feed",
"editTitle": "Editar Feed",
"feedName": "Nome do Feed",
"feedNamePlaceholder": "Nao sao suas chaves...",
"feedIcon": "Ícone do Feed",
"chooseIcon": "Selecionar ícone",
"feedIconCreateDescription": "Escolha um ícone personalizado para seu novo feed.",
"feedIconEditDescription": "Escolha um ícone personalizado para seu feed.",
"editFeedLabel": "Editar {name}",
"moreFeeds": "Mais feeds",
"reach": "Alcance",
"reachPlaceholder": "Selecione um alcance",
"sort": "Ordenar",
Expand Down
11 changes: 11 additions & 0 deletions messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,11 +742,22 @@
"urlPlaceholder": "https://twitter.com/satoshi",
"saveButton": "保存链接"
},
"iconPicker": {
"title": "选择图标",
"searchPlaceholder": "搜索图标",
"emptyMessage": "未找到图标"
},
"customFeed": {
"createTitle": "创建信息流",
"editTitle": "编辑信息流",
"feedName": "信息流名称",
"feedNamePlaceholder": "不是你的密钥...",
"feedIcon": "信息流图标",
"chooseIcon": "选择图标",
"feedIconCreateDescription": "为你的新信息流选择一个自定义图标。",
"feedIconEditDescription": "为你的信息流选择一个自定义图标。",
"editFeedLabel": "编辑{name}",
"moreFeeds": "更多信息流",
"reach": "范围",
"reachPlaceholder": "选择范围",
"sort": "排序",
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"next": "16.2.6",
"next-intl": "4.12.0",
"next-themes": "0.4.6",
"pubky-app-specs": "0.6.2",
"pubky-app-specs": "0.7.0",
"qrcode.react": "4.2.0",
"radix-ui": "1.4.3",
"react": "19.2.6",
Expand Down
46 changes: 46 additions & 0 deletions src/components/atoms/DynamicLucideIcon/DynamicLucideIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render, screen } from '@testing-library/react';
import { Library } from 'lucide-react';
import { describe, expect, it } from 'vitest';
import { DynamicLucideIcon } from './DynamicLucideIcon';

describe('DynamicLucideIcon', () => {
it('renders a valid dynamic icon', async () => {
render(<DynamicLucideIcon name="mountain" data-testid="dynamic-icon" />);

expect(await screen.findByTestId('dynamic-icon')).toBeInTheDocument();
});

it('renders the default fallback for a missing icon', () => {
render(<DynamicLucideIcon data-testid="fallback-icon" />);

expect(screen.getByTestId('fallback-icon')).toHaveClass('lucide-activity');
});

it('renders a consumer-provided fallback for an invalid icon', () => {
render(
<DynamicLucideIcon
name="not-a-real-lucide-icon"
fallback={Library}
data-testid="fallback-icon"
className="size-6"
/>,
);

expect(screen.getByTestId('fallback-icon')).toHaveClass('lucide-library');
expect(screen.getByTestId('fallback-icon')).toHaveClass('size-6');
});

it('can omit the fallback while a consumer handles its own loading state', () => {
const { container } = render(<DynamicLucideIcon name="not-a-real-lucide-icon" fallback={null} />);

expect(container.firstChild).toBeNull();
});
});

describe('DynamicLucideIcon - Snapshots', () => {
it('matches snapshot for a consumer-provided fallback', () => {
const { container } = render(<DynamicLucideIcon name={null} fallback={Library} className="size-6" />);

expect(container.firstChild).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`DynamicLucideIcon - Snapshots > matches snapshot for a consumer-provided fallback 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-library size-6"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m16 6 4 14"
/>
<path
d="M12 6v14"
/>
<path
d="M8 8v12"
/>
<path
d="M4 4v16"
/>
</svg>
`;
22 changes: 22 additions & 0 deletions src/components/atoms/DynamicLucideIcon/DynamicLucideIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';

import { Activity, type LucideIcon, type LucideProps } from 'lucide-react';
import { DynamicIcon } from 'lucide-react/dynamic.js';
import { isLucideIconName } from '@/libs/utils/lucideIcons';

export interface DynamicLucideIconProps extends Omit<LucideProps, 'name'> {
name?: string | null;
fallback?: LucideIcon | null;
}

export function DynamicLucideIcon({ name, fallback, ...iconProps }: DynamicLucideIconProps) {
const FallbackIcon = fallback === undefined ? Activity : fallback;

if (!isLucideIconName(name)) {
return FallbackIcon ? <FallbackIcon {...iconProps} /> : null;
}

return (
<DynamicIcon name={name} fallback={() => (FallbackIcon ? <FallbackIcon {...iconProps} /> : null)} {...iconProps} />
);
}
Loading
Loading