fix(components): [popconfirm] unable to capture focus (#22310)

* fix(components): [popconfirm] unable to capture focus

* fix: test

* chore: revert event
This commit is contained in:
qiang
2025-09-26 20:58:19 +08:00
committed by GitHub
parent 80850b4cb3
commit 4684320916
9 changed files with 51 additions and 61 deletions

View File

@@ -55,7 +55,6 @@ popconfirm/trigger-event
| ---------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------- |
| title | Title | ^[string] | — |
| effect ^(2.11.2) | Tooltip theme, built-in theme: `dark` / `light` | ^[enum]`'dark' \| 'light'` / ^[string] | light |
| close-on-press-escape ^(2.11.2) | whether the popConfirm can be closed by pressing ESC | ^[boolean] | true |
| confirm-button-text | Confirm button text | ^[string] | — |
| cancel-button-text | Cancel button text | ^[string] | — |
| confirm-button-type | Confirm button type | ^[enum]`'primary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'text'` | primary |

View File

@@ -43,14 +43,11 @@ describe('Popconfirm.vue', () => {
})
it('should close the Popconfirm when pressing Escape', async () => {
const onCancel = vi.fn()
const wrapper = mount({
setup() {
return () => (
<Popconfirm
closeOnPressEscape={false}
onCancel={onCancel}
hide-after={0}
v-slots={{
reference: () => <div class="reference">{AXIOM}</div>,
}}
@@ -65,13 +62,11 @@ describe('Popconfirm.vue', () => {
triggerEvent(document.body, 'keydown', EVENT_CODE.esc)
await nextTick()
expect(onCancel).toHaveBeenCalledTimes(0)
await rAF()
await wrapper.setProps({ closeOnPressEscape: true })
triggerEvent(document.body, 'keydown', EVENT_CODE.esc)
await nextTick()
expect(onCancel).toHaveBeenCalledTimes(1)
expect(document.querySelector(selector)!.getAttribute('style')).toContain(
'display: none'
)
})
describe('teleported API', () => {

View File

@@ -82,13 +82,6 @@ export const popconfirmProps = buildProps({
type: [String, Number],
default: 150,
},
/**
* @description whether the popConfirm can be closed by pressing ESC
*/
closeOnPressEscape: {
type: Boolean,
default: true,
},
} as const)
export const popconfirmEmits = {
@@ -99,8 +92,7 @@ export const popconfirmEmits = {
/**
* @description triggers when click cancel button
*/
cancel: (e: MouseEvent | KeyboardEvent) =>
e instanceof MouseEvent || e instanceof KeyboardEvent,
cancel: (e: MouseEvent) => e instanceof MouseEvent,
}
export type PopconfirmEmits = typeof popconfirmEmits

View File

@@ -10,42 +10,42 @@
:fallback-placements="['bottom', 'top', 'right', 'left']"
:hide-after="hideAfter"
:persistent="persistent"
loop
@show="showPopper"
>
<template #content>
<el-focus-trap loop trapped @release-requested="onCloseRequested">
<div :class="ns.b()">
<div :class="ns.e('main')">
<el-icon
v-if="!hideIcon && icon"
:class="ns.e('icon')"
:style="{ color: iconColor }"
>
<component :is="icon" />
</el-icon>
{{ title }}
</div>
<div :class="ns.e('action')">
<slot name="actions" :confirm="confirm" :cancel="cancel">
<el-button
size="small"
:type="cancelButtonType === 'text' ? '' : cancelButtonType"
:text="cancelButtonType === 'text'"
@click="cancel"
>
{{ finalCancelButtonText }}
</el-button>
<el-button
size="small"
:type="confirmButtonType === 'text' ? '' : confirmButtonType"
:text="confirmButtonType === 'text'"
@click="confirm"
>
{{ finalConfirmButtonText }}
</el-button>
</slot>
</div>
<div ref="rootRef" tabindex="-1" :class="ns.b()">
<div :class="ns.e('main')">
<el-icon
v-if="!hideIcon && icon"
:class="ns.e('icon')"
:style="{ color: iconColor }"
>
<component :is="icon" />
</el-icon>
{{ title }}
</div>
</el-focus-trap>
<div :class="ns.e('action')">
<slot name="actions" :confirm="confirm" :cancel="cancel">
<el-button
size="small"
:type="cancelButtonType === 'text' ? '' : cancelButtonType"
:text="cancelButtonType === 'text'"
@click="cancel"
>
{{ finalCancelButtonText }}
</el-button>
<el-button
size="small"
:type="confirmButtonType === 'text' ? '' : confirmButtonType"
:text="confirmButtonType === 'text'"
@click="confirm"
>
{{ finalConfirmButtonText }}
</el-button>
</slot>
</div>
</div>
</template>
<template v-if="$slots.reference">
<slot name="reference" />
@@ -61,7 +61,6 @@ import ElTooltip from '@element-plus/components/tooltip'
import { useLocale, useNamespace } from '@element-plus/hooks'
import { addUnit } from '@element-plus/utils'
import { popconfirmEmits, popconfirmProps } from './popconfirm'
import ElFocusTrap from '@element-plus/components/focus-trap'
import type { TooltipInstance } from '@element-plus/components/tooltip'
@@ -75,10 +74,16 @@ const emit = defineEmits(popconfirmEmits)
const { t } = useLocale()
const ns = useNamespace('popconfirm')
const tooltipRef = ref<TooltipInstance>()
const rootRef = ref<HTMLElement>()
const popperRef = computed(() => {
return unref(tooltipRef)?.popperRef
})
const showPopper = () => {
rootRef.value?.focus?.()
}
const hidePopper = () => {
tooltipRef.value?.onClose?.()
}
@@ -93,17 +98,11 @@ const confirm = (e: MouseEvent) => {
emit('confirm', e)
hidePopper()
}
const cancel = (e: MouseEvent | KeyboardEvent) => {
const cancel = (e: MouseEvent) => {
emit('cancel', e)
hidePopper()
}
const onCloseRequested = (event: KeyboardEvent) => {
if (props.closeOnPressEscape) {
cancel(event)
}
}
const finalConfirmButtonText = computed(
() => props.confirmButtonText || t('el.popconfirm.confirmButtonText')
)

View File

@@ -113,6 +113,7 @@ export const popperContentProps = buildProps({
virtualTriggering: Boolean,
zIndex: Number,
...useAriaProps(['ariaLabel']),
loop: Boolean,
} as const)
export type PopperContentProps = ExtractPropTypes<typeof popperContentProps>
export type PopperContentPropsPublic = __ExtractPublicPropTypes<

View File

@@ -9,6 +9,7 @@
@mouseleave="(e) => $emit('mouseleave', e)"
>
<el-focus-trap
:loop="loop"
:trapped="trapped"
:trap-on-focus-in="true"
:focus-trap-el="contentRef"

View File

@@ -33,6 +33,7 @@
:trigger-target-el="triggerTargetEl"
:visible="shouldShow"
:z-index="zIndex"
:loop="loop"
@mouseenter="onContentEnter"
@mouseleave="onContentLeave"
@blur="onBlur"

View File

@@ -39,6 +39,7 @@
:virtual-triggering="virtualTriggering"
:z-index="zIndex"
:append-to="appendTo"
:loop="loop"
>
<slot name="content">
<span v-if="rawContent" v-html="content" />

View File

@@ -2,6 +2,7 @@
@use 'common/var' as *;
@include b(popconfirm) {
outline: none;
@include e(main) {
display: flex;
align-items: center;