fix(components): [overlay] namespace issue (#11881)

* Fix namespace issue on global components overlay.
* Closes #11877
This commit is contained in:
Jeremy 2023-03-07 22:51:31 +08:00 committed by GitHub
parent b29d0b9869
commit 3eda7fc603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 5 deletions

View File

@ -5,6 +5,7 @@
:z-index="zIndex" :z-index="zIndex"
:overlay-class="[ns.is('message-box'), modalClass]" :overlay-class="[ns.is('message-box'), modalClass]"
:mask="modal" :mask="modal"
is-global
> >
<div <div
role="dialog" role="dialog"
@ -471,7 +472,7 @@ export default defineComponent({
// locks the screen to prevent scroll // locks the screen to prevent scroll
if (props.lockScroll) { if (props.lockScroll) {
useLockscreen(visible) useLockscreen(visible, { ns })
} }
// restore to prev active element. // restore to prev active element.

View File

@ -1,6 +1,8 @@
import { nextTick, ref } from 'vue' import { nextTick, ref } from 'vue'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { ElMessageBox } from '@element-plus/components/message-box'
import ConfigProvider from '@element-plus/components/config-provider'
import Overlay from '../src/overlay' import Overlay from '../src/overlay'
const AXIOM = 'Rem is the best girl' const AXIOM = 'Rem is the best girl'
@ -42,4 +44,26 @@ describe('Overlay.vue', () => {
expect(wrapper.find(selector).exists()).toBe(true) expect(wrapper.find(selector).exists()).toBe(true)
}) })
test('global', async () => {
const testNamespace = 'test'
const callout = () => {
ElMessageBox.prompt('Title', 'Description')
}
const wrapper = mount(() => {
return (
<ConfigProvider namespace={testNamespace}>
<button onClick={callout}>{AXIOM}</button>
</ConfigProvider>
)
})
expect(document.body.querySelector(`.${testNamespace}-overlay`)).toBeNull()
await wrapper.find('button').trigger('click')
await nextTick()
expect(
document.body.querySelector(`.${testNamespace}-overlay`)
).toBeDefined()
})
}) })

View File

@ -1,6 +1,7 @@
import { createVNode, defineComponent, h, renderSlot } from 'vue' import { createVNode, defineComponent, h, renderSlot } from 'vue'
import { PatchFlags, buildProps, definePropType } from '@element-plus/utils' import { PatchFlags, buildProps, definePropType } from '@element-plus/utils'
import { useNamespace, useSameTarget } from '@element-plus/hooks' import { useNamespace, useSameTarget } from '@element-plus/hooks'
import { useGlobalComponentSettings } from '@element-plus/components/config-provider'
import type { CSSProperties, ExtractPropTypes } from 'vue' import type { CSSProperties, ExtractPropTypes } from 'vue'
import type { ZIndexProperty } from 'csstype' import type { ZIndexProperty } from 'csstype'
@ -24,6 +25,9 @@ export const overlayProps = buildProps({
zIndex: { zIndex: {
type: definePropType<ZIndexProperty>([String, Number]), type: definePropType<ZIndexProperty>([String, Number]),
}, },
isGlobal: {
type: Boolean,
},
} as const) } as const)
export type OverlayProps = ExtractPropTypes<typeof overlayProps> export type OverlayProps = ExtractPropTypes<typeof overlayProps>
@ -32,6 +36,8 @@ export const overlayEmits = {
} }
export type OverlayEmits = typeof overlayEmits export type OverlayEmits = typeof overlayEmits
const BLOCK = 'overlay'
export default defineComponent({ export default defineComponent({
name: 'ElOverlay', name: 'ElOverlay',
@ -39,7 +45,11 @@ export default defineComponent({
emits: overlayEmits, emits: overlayEmits,
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const ns = useNamespace('overlay') // No reactivity on this prop because when its rendering with a global
// component, this will be a constant flag.
const ns = props.isGlobal
? useGlobalComponentSettings(BLOCK).ns
: useNamespace(BLOCK)
const onMaskClick = (e: MouseEvent) => { const onMaskClick = (e: MouseEvent) => {
emit('click', e) emit('click', e)

View File

@ -1,9 +1,10 @@
import { defineComponent, nextTick, onMounted, ref } from 'vue' import { computed, defineComponent, nextTick, onMounted, ref } from 'vue'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { hasClass } from '@element-plus/utils' import { hasClass } from '@element-plus/utils'
import { useLockscreen } from '../use-lockscreen' import { useLockscreen } from '../use-lockscreen'
import { useNamespace } from '../use-namespace'
const kls = 'el-popup-parent--hidden' const kls = 'el-popup-parent--hidden'
@ -52,4 +53,31 @@ describe('useLockscreen', () => {
expect(hasClass(document.body, kls)).toBe(false) expect(hasClass(document.body, kls)).toBe(false)
}, 250) }, 250)
}) })
it('should render a different namespace than the given one', async () => {
const namespace = 'test'
const wrapper = mount({
setup() {
const ns = useNamespace(
'lock',
computed(() => namespace)
)
const trigger = ref(false)
useLockscreen(trigger, { ns })
onMounted(() => {
trigger.value = true
})
return () => () => undefined
},
})
mount(wrapper)
await nextTick()
expect(hasClass(document.body, `${namespace}-lock-parent--hidden`)).toBe(
true
)
wrapper.unmount()
})
}) })

View File

@ -12,13 +12,22 @@ import {
import { useNamespace } from '../use-namespace' import { useNamespace } from '../use-namespace'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import type { UseNamespaceReturn } from '../use-namespace'
export type UseLockScreenOptions = {
ns?: UseNamespaceReturn
// shouldLock?: MaybeRef<boolean>
}
/** /**
* Hook that monitoring the ref value to lock or unlock the screen. * Hook that monitoring the ref value to lock or unlock the screen.
* When the trigger became true, it assumes modal is now opened and vice versa. * When the trigger became true, it assumes modal is now opened and vice versa.
* @param trigger {Ref<boolean>} * @param trigger {Ref<boolean>}
*/ */
export const useLockscreen = (trigger: Ref<boolean>) => { export const useLockscreen = (
trigger: Ref<boolean>,
options: UseLockScreenOptions = {}
) => {
if (!isRef(trigger)) { if (!isRef(trigger)) {
throwError( throwError(
'[useLockscreen]', '[useLockscreen]',
@ -26,7 +35,7 @@ export const useLockscreen = (trigger: Ref<boolean>) => {
) )
} }
const ns = useNamespace('popup') const ns = options.ns || useNamespace('popup')
const hiddenCls = computed(() => ns.bm('parent', 'hidden')) const hiddenCls = computed(() => ns.bm('parent', 'hidden'))