diff --git a/frontend/src/components/DraggableDroppableTagRow.test.tsx b/frontend/src/components/DraggableDroppableTagRow.test.tsx index 43a32aa..cdddba6 100644 --- a/frontend/src/components/DraggableDroppableTagRow.test.tsx +++ b/frontend/src/components/DraggableDroppableTagRow.test.tsx @@ -1,9 +1,25 @@ +import { screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import DraggableDroppableTagRow from '@/components/DraggableDroppableTagRow' import { buildTag } from '@/test/factories' import { renderWithProviders } from '@/test/render' +import type { CSSProperties, ReactNode } from 'react' + +vi.mock ('framer-motion', async importOriginal => { + const original = await importOriginal () + return { + ...original, + motion: { + ...original.motion, + div: ({ children, style, layoutId }: { + children?: ReactNode + style?: CSSProperties + layoutId?: string }) => ( +
{children}
) } } +}) + const dndKit = vi.hoisted (() => ({ useDraggable: vi.fn (), useDroppable: vi.fn (), @@ -97,4 +113,32 @@ describe ('DraggableDroppableTagRow', () => { }), ) }) + + it.each ([false, true]) ( + 'separates same-id internal and external animation identities (sp=%s)', + sp => { + const internal = buildTag ({ id: 7, name: 'internal_collision' }) + const external = buildTag ({ + id: 7, name: 'nico:external_collision', category: 'nico' }) + + renderWithProviders ( + <> + {[internal, external].map (item => ( + ))} + ) + + const prefix = sp ? 'tag-sp-' : 'tag-' + for (const item of [internal, external]) + { + expect (screen.getByRole ('link', { name: item.name }) + .closest ('[data-layout-id]')).toHaveAttribute ( + 'data-layout-id', `${ prefix }${ item.category }-7`) + } + }) }) diff --git a/frontend/src/components/DraggableDroppableTagRow.tsx b/frontend/src/components/DraggableDroppableTagRow.tsx index 7c74006..4cc2751 100644 --- a/frontend/src/components/DraggableDroppableTagRow.tsx +++ b/frontend/src/components/DraggableDroppableTagRow.tsx @@ -137,7 +137,7 @@ const DraggableDroppableTagRow: FC = ({ transition={{ layout: layoutTransition }} layoutId={animationMode === 'off' ? undefined - : `tag-${ sp ? 'sp-' : '' }${ tag.id }`}> + : `tag-${ sp ? 'sp-' : '' }${ tag.category }-${ tag.id }`}> )} ) diff --git a/frontend/src/components/TagSidebar.test.tsx b/frontend/src/components/TagSidebar.test.tsx new file mode 100644 index 0000000..809c354 --- /dev/null +++ b/frontend/src/components/TagSidebar.test.tsx @@ -0,0 +1,47 @@ +import { screen } from '@testing-library/react' +import { describe, expect, it, vi } from 'vitest' + +import TagSidebar from '@/components/TagSidebar' +import { buildPost, buildTag } from '@/test/factories' +import { renderWithProviders } from '@/test/render' + +import type { ReactNode } from 'react' + +vi.mock ('@/components/TagSearch', () => ({ default: () => null })) +vi.mock ('@/components/layout/SidebarComponent', () => ({ + default: ({ children }: { children: ReactNode }) => <>{children} })) + +vi.mock ('framer-motion', async importOriginal => { + const original = await importOriginal () + return { + ...original, + motion: { + ...original.motion, + div: ({ children, layoutId }: { + children?: ReactNode + layoutId?: string }) => ( +
{children}
) } } +}) + +describe ('TagSidebar', () => { + it ('keeps same-id internal and external tags with distinct identities', () => { + const errors = vi.spyOn (console, 'error').mockImplementation (() => {}) + const internal = buildTag ({ id: 7, name: 'internal_collision' }) + const external = buildTag ({ + id: 7, name: 'nico:external_collision', category: 'nico' }) + + renderWithProviders ( + ) + + for (const item of [internal, external]) + { + const link = screen.getByRole ('link', { name: item.name }) + expect (link).toBeInTheDocument () + expect (link.closest ('[data-layout-id]')).toHaveAttribute ( + 'data-layout-id', `tag-${ item.category }-7`) + } + + expect (errors.mock.calls.filter (args => + args.some (value => String (value).includes ('same key')))).toEqual ([]) + }) +}) diff --git a/frontend/src/components/TagSidebar.tsx b/frontend/src/components/TagSidebar.tsx index 26f21ab..8344881 100644 --- a/frontend/src/components/TagSidebar.tsx +++ b/frontend/src/components/TagSidebar.tsx @@ -84,7 +84,8 @@ const TagSidebar: FC = ({ posts, onClick }) => {
    {CATEGORIES.flatMap (cat => cat in tags ? ( tags[cat].map (tag => ( -
  • +
  • {animationsOff ? (
    @@ -94,7 +95,7 @@ const TagSidebar: FC = ({ posts, onClick }) => { + layoutId={`tag-${ cat }-${ tag.id }`}> )}
  • ))) : [])}