fix(components): [popper] fix arrow overflow issue (#20049)

* fix(components): [popper] fix arrow overflow issue

Add popperArrowProp to popperContentProps to ensure usePopper
correctly applies arrow options, preventing the arrow from
overflowing the popper container edges.

* fix(components): [popper] change arrowOffset to ref for reactivity

* feat(components): [tooltip] add arrow-offset property

- Allow users to control the padding of the tooltip arrow
- Prevents the arrow from touching the popper's edge

Reference: https://popper.js.org/docs/v2/modifiers/arrow/#padding

* fix(components): [popper] add reactive watch for arrowOffset prop

* feat(components): [popper] remove unused arrowOffset prop

Removes unused arrowOffset prop and related logic
- Removes arrowOffset from PopperContentInjectionContext
- Removes arrowOffset prop watching and state management
- Cleans up related test cases

* docs(components): [tooltip] update version requirement

update version requirement for arrow-offset prop

* docs(components): [tooltip] update `arrow-offset` version

* Update docs/en-US/component/tooltip.md

Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com>

---------

Co-authored-by: sea <45450994+warmthsea@users.noreply.github.com>
Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com>
This commit is contained in:
Xiao
2025-05-07 18:02:28 +08:00
committed by GitHub
parent bb5c767b7e
commit 8d2f08108b
9 changed files with 10 additions and 38 deletions

View File

@@ -173,6 +173,7 @@ tooltip/append-to
| offset | offset of the Tooltip | ^[number] | 12 |
| transition | animation name | ^[string] | — |
| popper-options | [popper.js](https://popper.js.org/docs/v2/) parameters | ^[object]refer to [popper.js](https://popper.js.org/docs/v2/) doc | {} |
| arrow-offset ^(2.9.10) | Controls the offset (padding) of the tooltips arrow relative to the popper. | ^[number] | 5 |
| show-after | delay of appearance, in millisecond | ^[number] | 0 |
| show-arrow | whether the tooltip content has an arrow | ^[boolean] | true |
| hide-after | delay of disappear, in millisecond | ^[number] | 200 |

View File

@@ -9,7 +9,6 @@ import type { PopperArrowInstance } from '../src/arrow'
const popperContentInjection = {
arrowRef: ref(null),
arrowOffset: ref(0),
}
const mountArrow = () =>
@@ -32,22 +31,10 @@ describe('<ElPopperArrow />', () => {
afterEach(() => {
wrapper?.unmount()
popperContentInjection.arrowRef.value = null
popperContentInjection.arrowOffset.value = 0
})
it('should set the arrowRef after mounted', async () => {
expect(popperContentInjection.arrowRef.value).toBe(wrapper.vm.arrowRef)
expect(popperContentInjection.arrowOffset.value).toBe(0)
})
it('should update the offset after props changed', async () => {
expect(popperContentInjection.arrowOffset.value).toBe(0)
await wrapper.setProps({
arrowOffset: 10,
})
expect(popperContentInjection.arrowOffset.value).toBe(10)
})
it('should unset arrowRef before unmount', async () => {

View File

@@ -8,30 +8,21 @@
</template>
<script lang="ts" setup>
import { inject, onBeforeUnmount, watch } from 'vue'
import { inject, onBeforeUnmount } from 'vue'
import { useNamespace } from '@element-plus/hooks'
import { POPPER_CONTENT_INJECTION_KEY } from './constants'
import { popperArrowProps } from './arrow'
defineOptions({
name: 'ElPopperArrow',
inheritAttrs: false,
})
const props = defineProps(popperArrowProps)
const ns = useNamespace('popper')
const { arrowOffset, arrowRef, arrowStyle } = inject(
const { arrowRef, arrowStyle } = inject(
POPPER_CONTENT_INJECTION_KEY,
undefined
)!
watch(
() => props.arrowOffset,
(val) => {
arrowOffset.value = val
}
)
onBeforeUnmount(() => {
arrowRef.value = undefined
})

View File

@@ -17,7 +17,7 @@ export const usePopperContent = (props: PopperContentProps) => {
)!
const arrowRef = ref<HTMLElement>()
const arrowOffset = ref<number>()
const arrowOffset = computed(() => props.arrowOffset)
const eventListenerModifier = computed(() => {
return {

View File

@@ -20,7 +20,6 @@ export type ElPopperInjectionContext = {
export type ElPopperContentInjectionContext = {
arrowRef: Ref<HTMLElement | undefined>
arrowOffset: Ref<number | undefined>
arrowStyle: ComputedRef<CSSProperties>
}

View File

@@ -1,6 +1,7 @@
import { placements } from '@popperjs/core'
import { buildProps, definePropType } from '@element-plus/utils'
import { useAriaProps } from '@element-plus/hooks'
import { popperArrowProps } from './arrow'
import type { PopperEffect } from './popper'
import type { ExtractPropTypes, StyleValue } from 'vue'
@@ -66,6 +67,7 @@ export type PopperCoreConfigProps = ExtractPropTypes<
export const popperContentProps = buildProps({
...popperCoreConfigProps,
...popperArrowProps,
id: String,
style: {
type: definePropType<StyleValue>([String, Array, Object]),

View File

@@ -25,15 +25,7 @@
</template>
<script lang="ts" setup>
import {
inject,
onBeforeUnmount,
onMounted,
provide,
ref,
unref,
watch,
} from 'vue'
import { inject, onBeforeUnmount, onMounted, provide, unref, watch } from 'vue'
import { isNil } from 'lodash-unified'
import { NOOP, isElement } from '@element-plus/utils'
import ElFocusTrap from '@element-plus/components/focus-trap'
@@ -84,12 +76,10 @@ const {
})
const formItemContext = inject(formItemContextKey, undefined)
const arrowOffset = ref<number>()
provide(POPPER_CONTENT_INJECTION_KEY, {
arrowStyle,
arrowRef,
arrowOffset,
})
if (formItemContext) {

View File

@@ -21,6 +21,7 @@
:offset="offset"
:placement="placement"
:popper-options="popperOptions"
:arrow-offset="arrowOffset"
:strategy="strategy"
:effect="effect"
:enterable="enterable"

View File

@@ -26,6 +26,7 @@
:popper-style="popperStyle"
:placement="placement"
:popper-options="popperOptions"
:arrow-offset="arrowOffset"
:pure="pure"
:raw-content="rawContent"
:reference-el="referenceEl"
@@ -42,7 +43,7 @@
<span v-if="rawContent" v-html="content" />
<span v-else>{{ content }}</span>
</slot>
<el-popper-arrow v-if="showArrow" :arrow-offset="arrowOffset" />
<el-popper-arrow v-if="showArrow" />
</el-tooltip-content>
</el-popper>
</template>