fix(markdown): preview TOC dismiss-on-outside-click, popover z-index, code-block banner stickiness
Three preview (render) bugs where DSH's chat-optimized markdown primitives leak into the file-preview scroll world: - CodeBlock banner: DSH's bannerWrap is position:sticky (top:0, z-index:6) so a block keeps its copy action reachable inside a per-message scroll container. In the preview every fence shares one .editorMd scroll container, so the banner pins to the viewport top until the whole block scrolls past and reads as a floating header. Neutralize stickiness for non-mermaid fences in the preview (mermaid chrome replaces the banner and is excluded via data-mermaid-processed). - TOC popover z-index: .tocBar was z-index:3, below the code banner's 6, so the outline panel painted beneath a sticky fence header. Raise the bar to 7. - TOC dismissal: only the toggle re-click closed the popover. Add a document pointerdown listener that closes on an outside press (blank preview space), exempting the button and panel so the toggle still flips. Adds md-toc unit coverage for outside-click dismiss and toggle-preserving behavior; records the deviations in the TOC design doc.
This commit is contained in:
@@ -149,4 +149,44 @@ describe('MdToc', () => {
|
||||
expect(container.querySelector('[data-dsh-md-toc-panel]')).toBeNull()
|
||||
await unmount(root)
|
||||
})
|
||||
|
||||
it('closes the popover when a pointerdown lands outside the button and panel', async () => {
|
||||
const { container, root } = await mountToc([
|
||||
{ tag: 'h1', text: 'Title' },
|
||||
{ tag: 'h2', text: 'A' },
|
||||
{ tag: 'h2', text: 'B' },
|
||||
])
|
||||
const button = container.querySelector<HTMLButtonElement>('[data-dsh-md-toc]')!
|
||||
await act(async () => { button.click() })
|
||||
expect(container.querySelector('[data-dsh-md-toc-panel]')).not.toBeNull()
|
||||
// A press on a heading that sits in the scroll container (a sibling of the
|
||||
// TOC bar) is "blank space" to the outline: it must dismiss the popover.
|
||||
const heading = container.querySelector('h2')!
|
||||
await act(async () => {
|
||||
heading.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
||||
})
|
||||
expect(container.querySelector('[data-dsh-md-toc-panel]'), 'outside press must close the panel').toBeNull()
|
||||
await unmount(root)
|
||||
})
|
||||
|
||||
it('keeps the toggle working: a press inside the bar does not dismiss, a click re-closes', async () => {
|
||||
const { container, root } = await mountToc([
|
||||
{ tag: 'h1', text: 'Title' },
|
||||
{ tag: 'h2', text: 'A' },
|
||||
{ tag: 'h2', text: 'B' },
|
||||
])
|
||||
const button = container.querySelector<HTMLButtonElement>('[data-dsh-md-toc]')!
|
||||
await act(async () => { button.click() })
|
||||
expect(container.querySelector('[data-dsh-md-toc-panel]')).not.toBeNull()
|
||||
// A pointerdown on the toggle itself must not be swallowed by the dismiss
|
||||
// handler (the button re-enables pointer events inside the pointer-events:
|
||||
// none bar), so the subsequent click still toggles it closed.
|
||||
await act(async () => {
|
||||
button.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
||||
})
|
||||
expect(container.querySelector('[data-dsh-md-toc-panel]'), 'press on the toggle must not dismiss').not.toBeNull()
|
||||
await act(async () => { button.click() })
|
||||
expect(container.querySelector('[data-dsh-md-toc-panel]'), 're-clicking the toggle must close').toBeNull()
|
||||
await unmount(root)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user