fix: stabilize mermaid diagram reload clicks
This commit is contained in:
@@ -59,6 +59,28 @@ describe('MermaidDiagram', () => {
|
||||
expect(screen.getByTestId('mermaid-diagram-dialog-viewport').querySelector('svg')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('keeps rendered SVG pointer events inside the Mermaid block', async () => {
|
||||
const onBlockPointer = vi.fn()
|
||||
render(
|
||||
<div onClick={onBlockPointer} onMouseDown={onBlockPointer}>
|
||||
<MermaidDiagram
|
||||
diagram={'flowchart LR\nA --> B'}
|
||||
source={'```mermaid\nflowchart LR\nA --> B\n```'}
|
||||
/>
|
||||
</div>,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('mermaid-diagram-viewport').querySelector('svg')).not.toBeNull()
|
||||
})
|
||||
|
||||
const viewport = screen.getByTestId('mermaid-diagram-viewport')
|
||||
fireEvent.mouseDown(viewport)
|
||||
fireEvent.click(viewport)
|
||||
|
||||
expect(onBlockPointer).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('tags Mermaid SVG style elements with the runtime CSP nonce', async () => {
|
||||
mermaidMock.render.mockResolvedValueOnce({
|
||||
svg: '<svg aria-label="Rendered Mermaid"><style>.node{fill:#000}</style><g><text>A to B</text></g></svg>',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ArrowsOut as Maximize2 } from '@phosphor-icons/react'
|
||||
import { useEffect, useId, useMemo, useState } from 'react'
|
||||
import { useEffect, useId, useMemo, useState, type SyntheticEvent } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
@@ -106,14 +106,27 @@ function MermaidSvgViewport({ ariaLabel, className, svg, testId }: MermaidSvgVie
|
||||
<SafeSvgDiv
|
||||
aria-label={ariaLabel}
|
||||
className={className}
|
||||
contentEditable={false}
|
||||
data-testid={testId}
|
||||
draggable={false}
|
||||
onClick={stopMermaidViewportEvent}
|
||||
onDoubleClick={stopMermaidViewportEvent}
|
||||
onMouseDown={stopMermaidViewportEvent}
|
||||
onMouseUp={stopMermaidViewportEvent}
|
||||
onPointerDown={stopMermaidViewportEvent}
|
||||
onPointerUp={stopMermaidViewportEvent}
|
||||
role="img"
|
||||
svg={svg}
|
||||
suppressContentEditableWarning
|
||||
tabIndex={0}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function stopMermaidViewportEvent(event: SyntheticEvent): void {
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
function MermaidLightbox({ svg }: { svg: string }) {
|
||||
return (
|
||||
<Dialog>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createFixtureVaultCopy, openFixtureVault, removeFixtureVaultCopy } from
|
||||
import { executeCommand, openCommandPalette } from './helpers'
|
||||
import { RUNTIME_STYLE_NONCE } from '../../src/lib/runtimeStyleNonce'
|
||||
import { APP_COMMAND_IDS } from '../../src/hooks/appCommandCatalog'
|
||||
import { triggerShortcutCommand } from './testBridge'
|
||||
import { triggerMenuCommand, triggerShortcutCommand } from './testBridge'
|
||||
|
||||
let tempVaultDir: string
|
||||
|
||||
@@ -192,6 +192,13 @@ async function expectRenderedDiagramCount(page: Page, count: number): Promise<vo
|
||||
await expect(page.locator('[data-testid="mermaid-diagram-viewport"] svg')).toHaveCount(count, { timeout: 15_000 })
|
||||
}
|
||||
|
||||
async function reloadVault(page: Page): Promise<void> {
|
||||
await triggerMenuCommand(page, APP_COMMAND_IDS.vaultReload)
|
||||
await expect(page.getByText(/Vault reloaded \(\d+ entries\)/).last()).toBeVisible({
|
||||
timeout: 5_000,
|
||||
})
|
||||
}
|
||||
|
||||
async function countLargeBlackFilledShapes(page: Page, diagramIndex: number): Promise<number> {
|
||||
return page.locator('[data-testid="mermaid-diagram-viewport"] svg').nth(diagramIndex).evaluate((svg) => {
|
||||
const shapes = Array.from(svg.querySelectorAll<SVGGraphicsElement>('rect,path,polygon'))
|
||||
@@ -342,6 +349,21 @@ test('Mermaid diagrams stay mounted after property edits refresh frontmatter', a
|
||||
expect(pageErrors).toEqual([])
|
||||
})
|
||||
|
||||
test('Mermaid diagram clicks stay stable while a vault reload settles', async ({ page }) => {
|
||||
const pageErrors: string[] = []
|
||||
page.on('pageerror', error => pageErrors.push(error.message))
|
||||
|
||||
await openNote(page, 'Mermaid Reported')
|
||||
await expectRenderedDiagramCount(page, 1)
|
||||
await reloadVault(page)
|
||||
await mermaidSvg(page, 0).click()
|
||||
|
||||
await expectRenderedDiagramCount(page, 1)
|
||||
await expect(page.locator('#tolaria-fatal-render-error')).toHaveCount(0)
|
||||
await expect(mermaidSvg(page, 0)).toContainText('Linked to a planned shift?')
|
||||
expect(pageErrors).toEqual([])
|
||||
})
|
||||
|
||||
test('Mermaid diagrams render, fall back, and round-trip through raw mode', async ({ page }) => {
|
||||
await openNote(page, 'Note B')
|
||||
await toggleRawMode(page, '.cm-content')
|
||||
|
||||
Reference in New Issue
Block a user