diff --git a/.gitignore b/.gitignore index 571c06f..ad08752 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ node_modules/ !website/public/hmze-logo.png !docs/workflow-diagram.svg .playwright-mcp/ +.venv/ +.linkedin/ *:Zone.Identifier website/public/CONTRIBUTING.html website/public/CONTRIBUTING.de.html diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js index f5b3a95..15f58d4 100644 --- a/website/src/components/anchor-modal.js +++ b/website/src/components/anchor-modal.js @@ -356,10 +356,10 @@ export async function loadAnchorContent(anchorId) { } export function showAnchorDetails(anchorId) { - const modal = document.getElementById('anchor-modal') - if (modal) { - modal.dataset.currentAnchor = anchorId - } + // Share-link race: handleRoute may invoke us before initApp's createModal() + // microtask runs. createModal is idempotent. + const modal = document.getElementById('anchor-modal') || createModal() + modal.dataset.currentAnchor = anchorId openModal() return loadAnchorContent(anchorId) } diff --git a/website/src/components/anchor-modal.test.js b/website/src/components/anchor-modal.test.js index 5fe5c47..165fc28 100644 --- a/website/src/components/anchor-modal.test.js +++ b/website/src/components/anchor-modal.test.js @@ -155,6 +155,22 @@ describe('anchor-modal', () => { const content = document.getElementById('modal-content') expect(content.innerHTML).toContain('Failed to load') }) + + it('should not crash when modal element is missing (share-link race, #470)', async () => { + const existing = document.getElementById('anchor-modal') + if (existing) existing.remove() + + global.fetch.mockResolvedValue({ + ok: true, + text: async () => '= Test Anchor\n\nTest content', + }) + + await expect(showAnchorDetails('test-anchor')).resolves.not.toThrow() + + const modal = document.getElementById('anchor-modal') + expect(modal).not.toBeNull() + expect(modal.classList.contains('hidden')).toBe(false) + }) }) describe('umbrella anchors', () => {