Skip to content

Commit

Permalink
Update useLinkInterception to fix clickHandler (#2493)
Browse files Browse the repository at this point in the history
* Update useLinkInterception to fix clickHandler

* chore: add changeset

Co-authored-by: Josh Black <josh@josh.black>
Co-authored-by: Josh Black <joshblack@github.com>
  • Loading branch information
3 people committed Oct 31, 2022
1 parent 6a30812 commit 7ea92ab
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/silly-coins-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': patch
---

Address scenario in useLinkInterception where onLinkClick was not being called when the DOM was updated outside of React
7 changes: 6 additions & 1 deletion src/drafts/MarkdownEditor/MarkdownEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,14 @@ describe('MarkdownEditor', () => {
it('forces links to open in a new tab', async () => {
// eslint-disable-next-line github/unescaped-html-literal
const html = '<a href="https://example.com">Link</a>'
const user = userEvent.setup()
const windowOpenSpy = jest.spyOn(window, 'open')
windowOpenSpy.mockImplementation(jest.fn())
const {getPreview} = await render(<UncontrolledEditor onRenderPreview={async () => html} viewMode="preview" />)
const link = await waitFor(() => within(getPreview()).getByText('Link'))
expect(link).toHaveAttribute('target', '_blank')

await user.click(link)
expect(windowOpenSpy).toHaveBeenCalledWith('https://example.com/', '_blank')
})
})
})
Expand Down
9 changes: 7 additions & 2 deletions src/drafts/MarkdownViewer/MarkdownViewer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,18 @@ text after list`
})

describe('link interception', () => {
it('makes all links open in a new tab when enabled', () => {
it('makes all links open in a new tab when enabled', async () => {
const user = userEvent.setup()
const windowOpenSpy = jest.spyOn(window, 'open')
windowOpenSpy.mockImplementation(jest.fn())

const {getByRole} = render(
// eslint-disable-next-line github/unescaped-html-literal
<MarkdownViewer dangerousRenderedHTML={{__html: '<a href="https://example.com">link</a>'}} openLinksInNewTab />
)
const link = getByRole('link') as HTMLAnchorElement
expect(link.target).toBe('_blank')
await user.click(link)
expect(windowOpenSpy).toHaveBeenCalledWith('https://example.com/', '_blank')
})

it('calls onLinkClick on link click', async () => {
Expand Down
26 changes: 12 additions & 14 deletions src/drafts/MarkdownViewer/_useLinkInterception.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,22 @@ type UseLinkInterceptionSettings = {
* Updates all links in the container to open a new tab and call `onLinkClick` on click.
*/
export const useLinkInterception = ({htmlContainer, onLinkClick, openLinksInNewTab}: UseLinkInterceptionSettings) => {
useEffect(function transformLinks() {
const links = htmlContainer.querySelectorAll('a')
useEffect(() => {
const clickHandler = (event: MouseEvent) => {
const link = (event.target as Element).closest('a')
if (!link) return

const cleanupFns = Array.from(links).map(link => {
const clickHandler = onLinkClick
if (clickHandler) link.addEventListener('click', clickHandler)
onLinkClick?.(event)

const prevTarget = link.target
if (openLinksInNewTab) link.setAttribute('target', '_blank')

return () => {
if (clickHandler) link.removeEventListener('click', clickHandler)
link.setAttribute('target', prevTarget)
if (!event.defaultPrevented && openLinksInNewTab && link.href) {
window.open(link.href, '_blank')
}
})
}

htmlContainer.addEventListener('click', clickHandler)

return () => {
for (const fn of cleanupFns) fn()
htmlContainer.removeEventListener('click', clickHandler)
}
})
}, [htmlContainer, onLinkClick, openLinksInNewTab])
}

0 comments on commit 7ea92ab

Please sign in to comment.