Fix detection of anchor click events inside svg (#23272)
## Bug The `nodeName` of an anchor inside an SVG equals the lowercase `a` instead of the html anchor's uppercase `A`. This behavior can be seen in this plain js demo: https://jsfiddle.net/L2p8f4ve/28/ fixes #23252 ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. ## Documentation / Examples - [ ] Make sure the linting passes Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
This commit is contained in:
parent
6090101581
commit
b39e49eca9
5 changed files with 76 additions and 8 deletions
|
@ -86,7 +86,10 @@ function linkClicked(
|
|||
): void {
|
||||
const { nodeName } = e.currentTarget
|
||||
|
||||
if (nodeName === 'A' && (isModifiedEvent(e) || !isLocalURL(href))) {
|
||||
// anchors inside an svg have a lowercase nodeName
|
||||
const isAnchorNodeName = nodeName.toUpperCase() === 'A'
|
||||
|
||||
if (isAnchorNodeName && (isModifiedEvent(e) || !isLocalURL(href))) {
|
||||
// ignore click for browser’s default behavior
|
||||
return
|
||||
}
|
||||
|
|
|
@ -78,14 +78,33 @@ export default class extends Component {
|
|||
</Link>
|
||||
<Link href="/nav/on-click?count=1">
|
||||
<a id="on-click-link" style={linkStyle}>
|
||||
A element with onClick
|
||||
An element with onClick
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/nav/about">
|
||||
<a id="target-link" target="_blank">
|
||||
A element with target
|
||||
An element with target
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<svg
|
||||
width={24}
|
||||
height={24}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<Link href="/nav/about">
|
||||
<a id="in-svg-link" style={{ display: 'block' }}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0Zm4.5 18a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z"
|
||||
fill="#335BF1"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
</svg>
|
||||
|
||||
<button
|
||||
onClick={() => this.visitQueryStringPage()}
|
||||
style={linkStyle}
|
||||
|
|
|
@ -180,13 +180,35 @@ describe('Client Navigation', () => {
|
|||
it('should not navigate if the <a/> tag has a target', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const counterText = await browser
|
||||
await browser
|
||||
.elementByCss('#increase')
|
||||
.click()
|
||||
.elementByCss('#target-link')
|
||||
.click()
|
||||
.elementByCss('#counter')
|
||||
.text()
|
||||
|
||||
await waitFor(1000)
|
||||
|
||||
const counterText = await browser.elementByCss('#counter').text()
|
||||
|
||||
expect(counterText).toBe('Counter: 1')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should not navigate if the click-event is modified', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
await browser.elementByCss('#increase').click()
|
||||
|
||||
const key = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||
|
||||
await browser.keydown(key)
|
||||
|
||||
await browser.elementByCss('#in-svg-link').click()
|
||||
|
||||
await browser.keyup(key)
|
||||
await waitFor(1000)
|
||||
|
||||
const counterText = await browser.elementByCss('#counter').text()
|
||||
|
||||
expect(counterText).toBe('Counter: 1')
|
||||
await browser.close()
|
||||
|
@ -413,7 +435,6 @@ describe('Client Navigation', () => {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('resets scroll at the correct time', () => {
|
||||
it('should reset scroll before the new page runs its lifecycles (<Link />)', async () => {
|
||||
let browser
|
||||
|
|
|
@ -29,7 +29,16 @@ export class BrowserInterface {
|
|||
elementById(selector: string): BrowserInterface {
|
||||
return this
|
||||
}
|
||||
click(): BrowserInterface {
|
||||
click(opts?: { modifierKey?: boolean }): BrowserInterface {
|
||||
return this
|
||||
}
|
||||
keydown(key: string): BrowserInterface {
|
||||
return this
|
||||
}
|
||||
keyup(key: string): BrowserInterface {
|
||||
return this
|
||||
}
|
||||
focusPage(): BrowserInterface {
|
||||
return this
|
||||
}
|
||||
type(text: string): BrowserInterface {
|
||||
|
|
|
@ -165,6 +165,10 @@ class Playwright extends BrowserInterface {
|
|||
return this.chain(async () => context.clearCookies())
|
||||
}
|
||||
|
||||
focusPage() {
|
||||
return this.chain(() => page.bringToFront())
|
||||
}
|
||||
|
||||
private wrapElement(el: ElementHandle, selector: string) {
|
||||
;(el as any).selector = selector
|
||||
;(el as any).text = () => el.innerText()
|
||||
|
@ -237,6 +241,18 @@ class Playwright extends BrowserInterface {
|
|||
return this.eval(`!!document.querySelector('${selector}')`) as any
|
||||
}
|
||||
|
||||
keydown(key: string): BrowserInterface {
|
||||
return this.chain((el) => {
|
||||
return page.keyboard.down(key).then(() => el)
|
||||
})
|
||||
}
|
||||
|
||||
keyup(key: string): BrowserInterface {
|
||||
return this.chain((el) => {
|
||||
return page.keyboard.up(key).then(() => el)
|
||||
})
|
||||
}
|
||||
|
||||
click() {
|
||||
return this.chain((el) => {
|
||||
return el.click().then(() => el)
|
||||
|
|
Loading…
Reference in a new issue