diff --git a/src/components/BreadcrumbBar.tsx b/src/components/BreadcrumbBar.tsx index ee3af859..af15c0d5 100644 --- a/src/components/BreadcrumbBar.tsx +++ b/src/components/BreadcrumbBar.tsx @@ -1,6 +1,7 @@ import { memo } from 'react' import type { VaultEntry, NoteStatus } from '../types' import { cn } from '@/lib/utils' +import { isEmoji } from '../utils/emoji' import { MagnifyingGlass, GitBranch, @@ -179,7 +180,10 @@ export const BreadcrumbBar = memo(function BreadcrumbBar({
{entry.isA || 'Note'} - {entry.title} + + {entry.icon && isEmoji(entry.icon) && {entry.icon}} + {entry.title} + · {wordCount.toLocaleString()} words {noteStatus === 'pendingSave' && ( diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx index 89fcd8b1..cf61bf11 100644 --- a/src/components/TabBar.tsx +++ b/src/components/TabBar.tsx @@ -5,6 +5,7 @@ import { cn } from '@/lib/utils' import { X } from 'lucide-react' import { Plus, Columns, ArrowsOutSimple, ArrowLeft, ArrowRight } from '@phosphor-icons/react' import { computeTabMaxWidth } from '@/utils/tabLayout' +import { isEmoji } from '../utils/emoji' interface Tab { entry: VaultEntry @@ -242,6 +243,7 @@ function TabItem({ tab, isActive, isEditing, noteStatus, isDragging, showDropBef ) : ( { e.stopPropagation(); onDoubleClick() }}> + {tab.entry.icon && isEmoji(tab.entry.icon) && {tab.entry.icon}} {tab.entry.title} )} diff --git a/src/components/note-list/PinnedCard.tsx b/src/components/note-list/PinnedCard.tsx index db9bc09e..a7971fea 100644 --- a/src/components/note-list/PinnedCard.tsx +++ b/src/components/note-list/PinnedCard.tsx @@ -2,6 +2,7 @@ import type { VaultEntry } from '../../types' import { getTypeColor, getTypeLightColor } from '../../utils/typeColors' import { getTypeIcon } from '../NoteItem' import { relativeDate, getDisplayDate } from '../../utils/noteListHelpers' +import { isEmoji } from '../../utils/emoji' export function PinnedCard({ entry, typeEntryMap, onClickNote, showDate }: { entry: VaultEntry @@ -17,7 +18,10 @@ export function PinnedCard({ entry, typeEntryMap, onClickNote, showDate }: {
onClickNote(entry, e)}> {/* eslint-disable-next-line react-hooks/static-components */} -
{entry.title}
+
+ {entry.icon && isEmoji(entry.icon) && {entry.icon}} + {entry.title} +
{entry.snippet}
{showDate &&
{relativeDate(getDisplayDate(entry))}
}
diff --git a/tests/smoke/emoji-icon-everywhere.spec.ts b/tests/smoke/emoji-icon-everywhere.spec.ts new file mode 100644 index 00000000..746247e6 --- /dev/null +++ b/tests/smoke/emoji-icon-everywhere.spec.ts @@ -0,0 +1,65 @@ +import { test, expect } from '@playwright/test' + +test.describe('Emoji icon shown everywhere title appears', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/') + await page.waitForLoadState('networkidle') + await page.waitForTimeout(2500) + }) + + test('emoji icon appears in tab bar, breadcrumb, and note list after setting it', async ({ page }) => { + // Open a note + const noteItem = page.locator('[data-testid="note-list-container"] .cursor-pointer').first() + await noteItem.waitFor({ timeout: 5000 }) + await noteItem.click() + await page.waitForTimeout(500) + + // Remove any existing icon first for a clean state + const iconDisplay = page.locator('[data-testid="note-icon-display"]') + if (await iconDisplay.isVisible()) { + await iconDisplay.click() + await page.locator('[data-testid="note-icon-remove"]').click() + await page.waitForTimeout(300) + } + + // Set an emoji icon + const iconArea = page.locator('[data-testid="note-icon-area"]') + await iconArea.hover() + await page.locator('[data-testid="note-icon-add"]').click() + const firstEmoji = page.locator('[data-testid="emoji-option"]').first() + const emojiText = await firstEmoji.textContent() + expect(emojiText).toBeTruthy() + await firstEmoji.click() + await page.waitForTimeout(500) + + // Verify emoji in note list item + const noteListText = await noteItem.textContent() + expect(noteListText).toContain(emojiText!) + + // Verify emoji in the tab (active tab has the truncate span with title) + const tabArea = page.locator('.group .truncate').first() + const tabText = await tabArea.textContent() + expect(tabText).toContain(emojiText!) + }) + + test('note without emoji shows no emoji span in tab or note list', async ({ page }) => { + // Open a note + const noteItem = page.locator('[data-testid="note-list-container"] .cursor-pointer').first() + await noteItem.waitFor({ timeout: 5000 }) + await noteItem.click() + await page.waitForTimeout(500) + + // Remove icon if present + const iconDisplay = page.locator('[data-testid="note-icon-display"]') + if (await iconDisplay.isVisible()) { + await iconDisplay.click() + await page.locator('[data-testid="note-icon-remove"]').click() + await page.waitForTimeout(300) + } + + // The note list item title row should not contain an emoji span (mr-1) + const titleRow = noteItem.locator('.truncate.text-foreground').first() + const emojiSpans = titleRow.locator('.mr-1') + await expect(emojiSpans).toHaveCount(0) + }) +})