mirror of
https://github.com/element-plus/element-plus.git
synced 2025-04-06 16:30:35 +08:00
fix(components): [global-config] (#11847)
* fix(components): [loading] * Remove inappropriate way of using injection in directives. * chore: rewrite implementation * fix(components): [global-config] * Fix global config injection in global components. * chore: fix format * chore: remove .only modifier * chore: fix failing tests
This commit is contained in:
parent
df600f2bed
commit
c2710d97d0
@ -1,4 +1,4 @@
|
||||
import { nextTick, reactive } from 'vue'
|
||||
import { defineComponent, nextTick, reactive } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { NOOP } from '@vue/shared'
|
||||
import { beforeEach, describe, expect, it, test, vi } from 'vitest'
|
||||
@ -14,61 +14,73 @@ const _mount = (
|
||||
payload = {},
|
||||
type: 'fn-cb' | 'fn-promise' | 'fn-arr' | 'fn-async' | 'arr' = 'fn-cb'
|
||||
) =>
|
||||
mount({
|
||||
setup() {
|
||||
const state = reactive({
|
||||
value: '',
|
||||
list: [
|
||||
{ value: 'Java', tag: 'java' },
|
||||
{ value: 'Go', tag: 'go' },
|
||||
{ value: 'JavaScript', tag: 'javascript' },
|
||||
{ value: 'Python', tag: 'python' },
|
||||
],
|
||||
payload,
|
||||
})
|
||||
mount(
|
||||
defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const state = reactive({
|
||||
value: '',
|
||||
list: [
|
||||
{ value: 'Java', tag: 'java' },
|
||||
{ value: 'Go', tag: 'go' },
|
||||
{ value: 'JavaScript', tag: 'javascript' },
|
||||
{ value: 'Python', tag: 'python' },
|
||||
],
|
||||
payload,
|
||||
})
|
||||
|
||||
function filterList(queryString: string) {
|
||||
return queryString
|
||||
? state.list.filter(
|
||||
(i) => i.value.indexOf(queryString.toLowerCase()) === 0
|
||||
)
|
||||
: state.list
|
||||
}
|
||||
|
||||
const querySearch = (() => {
|
||||
switch (type) {
|
||||
case 'fn-cb':
|
||||
return (
|
||||
queryString: string,
|
||||
cb: (arg: typeof state.list) => void
|
||||
) => {
|
||||
cb(filterList(queryString))
|
||||
}
|
||||
case 'fn-promise':
|
||||
return (queryString: string) =>
|
||||
Promise.resolve(filterList(queryString))
|
||||
case 'fn-async':
|
||||
return async (queryString: string) => {
|
||||
await Promise.resolve()
|
||||
return filterList(queryString)
|
||||
}
|
||||
case 'fn-arr':
|
||||
return (queryString: string) => filterList(queryString)
|
||||
case 'arr':
|
||||
return state.list
|
||||
function filterList(queryString: string) {
|
||||
return queryString
|
||||
? state.list.filter(
|
||||
(i) => i.value.indexOf(queryString.toLowerCase()) === 0
|
||||
)
|
||||
: state.list
|
||||
}
|
||||
})()
|
||||
|
||||
return () => (
|
||||
<Autocomplete
|
||||
ref="autocomplete"
|
||||
v-model={state.value}
|
||||
fetch-suggestions={querySearch}
|
||||
{...state.payload}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
const querySearch = (() => {
|
||||
switch (type) {
|
||||
case 'fn-cb':
|
||||
return (
|
||||
queryString: string,
|
||||
cb: (arg: typeof state.list) => void
|
||||
) => {
|
||||
cb(filterList(queryString))
|
||||
}
|
||||
case 'fn-promise':
|
||||
return (queryString: string) =>
|
||||
Promise.resolve(filterList(queryString))
|
||||
case 'fn-async':
|
||||
return async (queryString: string) => {
|
||||
await Promise.resolve()
|
||||
return filterList(queryString)
|
||||
}
|
||||
case 'fn-arr':
|
||||
return (queryString: string) => filterList(queryString)
|
||||
case 'arr':
|
||||
return state.list
|
||||
}
|
||||
})()
|
||||
const containerExposes = usePopperContainerId()
|
||||
|
||||
expose(containerExposes)
|
||||
|
||||
return () => (
|
||||
<Autocomplete
|
||||
ref="autocomplete"
|
||||
v-model={state.value}
|
||||
fetch-suggestions={querySearch}
|
||||
{...state.payload}
|
||||
/>
|
||||
)
|
||||
},
|
||||
}),
|
||||
{
|
||||
global: {
|
||||
provide: {
|
||||
namespace: 'el',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
describe('Autocomplete.vue', () => {
|
||||
beforeEach(() => {
|
||||
@ -318,24 +330,22 @@ describe('Autocomplete.vue', () => {
|
||||
describe('teleported API', () => {
|
||||
it('should mount on popper container', async () => {
|
||||
expect(document.body.innerHTML).toBe('')
|
||||
_mount()
|
||||
const { vm } = _mount()
|
||||
|
||||
await nextTick()
|
||||
const { selector } = usePopperContainerId()
|
||||
expect(document.body.querySelector(selector.value)?.innerHTML).not.toBe(
|
||||
''
|
||||
)
|
||||
const { selector } = vm
|
||||
expect(document.body.querySelector(selector)?.innerHTML).not.toBe('')
|
||||
})
|
||||
|
||||
it('should not mount on the popper container', async () => {
|
||||
expect(document.body.innerHTML).toBe('')
|
||||
_mount({
|
||||
const { vm } = _mount({
|
||||
teleported: false,
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
const { selector } = usePopperContainerId()
|
||||
expect(document.body.querySelector(selector.value)?.innerHTML).toBe('')
|
||||
const { selector } = vm
|
||||
expect(document.body.querySelector(selector)?.innerHTML).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -6,7 +6,10 @@ import Chinese from '@element-plus/locale/lang/zh-cn'
|
||||
import English from '@element-plus/locale/lang/en'
|
||||
import { ElButton, ElMessage } from '@element-plus/components'
|
||||
import { rAF } from '@element-plus/test-utils/tick'
|
||||
import { useGlobalConfig } from '../src/hooks/use-global-config'
|
||||
import {
|
||||
useGlobalComponentSettings,
|
||||
useGlobalConfig,
|
||||
} from '../src/hooks/use-global-config'
|
||||
import ConfigProvider from '../src/config-provider'
|
||||
|
||||
import type { PropType } from 'vue'
|
||||
@ -239,4 +242,30 @@ describe('config-provider', () => {
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe('global component configs', () => {
|
||||
it('should use global configured settings', () => {
|
||||
const namespace = 'test'
|
||||
const locale = Chinese
|
||||
const zIndex = 1000
|
||||
const block = 'button'
|
||||
const receiverRef = ref()
|
||||
const ReceiverComponent = defineComponent({
|
||||
setup() {
|
||||
receiverRef.value = useGlobalComponentSettings(block)
|
||||
},
|
||||
template: '<div></div>',
|
||||
})
|
||||
mount(() => (
|
||||
<ConfigProvider zIndex={zIndex} locale={locale} namespace={namespace}>
|
||||
<ReceiverComponent />
|
||||
</ConfigProvider>
|
||||
))
|
||||
|
||||
const vm = receiverRef.value
|
||||
expect(vm.ns.namespace).toBe(namespace)
|
||||
expect(vm.locale.locale).toBe(locale)
|
||||
expect(vm.zIndex.currentZIndex).toBeGreaterThanOrEqual(zIndex)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -2,8 +2,13 @@ import { computed, getCurrentInstance, inject, provide, ref, unref } from 'vue'
|
||||
import { debugWarn, keysOf } from '@element-plus/utils'
|
||||
import {
|
||||
SIZE_INJECTION_KEY,
|
||||
defaultInitialZIndex,
|
||||
defaultNamespace,
|
||||
localeContextKey,
|
||||
namespaceContextKey,
|
||||
useLocale,
|
||||
useNamespace,
|
||||
useZIndex,
|
||||
zIndexContextKey,
|
||||
} from '@element-plus/hooks'
|
||||
import { configProviderContextKey } from '../constants'
|
||||
@ -39,6 +44,27 @@ export function useGlobalConfig(
|
||||
}
|
||||
}
|
||||
|
||||
// for components like `ElMessage` `ElNotification` `ElMessageBox`.
|
||||
export function useGlobalComponentSettings(block: string) {
|
||||
const config = useGlobalConfig()
|
||||
|
||||
const ns = useNamespace(
|
||||
block,
|
||||
computed(() => config.value?.namespace || defaultNamespace)
|
||||
)
|
||||
|
||||
const locale = useLocale(computed(() => config.value?.locale))
|
||||
const zIndex = useZIndex(
|
||||
computed(() => config.value?.zIndex || defaultInitialZIndex)
|
||||
)
|
||||
|
||||
return {
|
||||
ns,
|
||||
locale,
|
||||
zIndex,
|
||||
}
|
||||
}
|
||||
|
||||
export const provideGlobalConfig = (
|
||||
config: MaybeRef<ConfigProviderContext>,
|
||||
app?: App,
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
Transition,
|
||||
createApp,
|
||||
createVNode,
|
||||
defineComponent,
|
||||
h,
|
||||
reactive,
|
||||
ref,
|
||||
@ -10,15 +11,16 @@ import {
|
||||
withCtx,
|
||||
withDirectives,
|
||||
} from 'vue'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { useNamespace, useZIndex } from '@element-plus/hooks'
|
||||
import { removeClass } from '@element-plus/utils'
|
||||
|
||||
import type { UseNamespaceReturn } from '@element-plus/hooks'
|
||||
import type { LoadingOptionsResolved } from './types'
|
||||
|
||||
export function createLoadingComponent(options: LoadingOptionsResolved) {
|
||||
let afterLeaveTimer: number
|
||||
|
||||
const ns = useNamespace('loading')
|
||||
// IMPORTANT NOTE: this is only a hacking way to expose the injections on an
|
||||
// instance, DO NOT FOLLOW this pattern in your own code.
|
||||
const afterLeaveFlag = ref(false)
|
||||
const data = reactive({
|
||||
...options,
|
||||
@ -33,6 +35,7 @@ export function createLoadingComponent(options: LoadingOptionsResolved) {
|
||||
|
||||
function destroySelf() {
|
||||
const target = data.parent
|
||||
const ns = (vm as any).ns as UseNamespaceReturn
|
||||
if (!target.vLoadingAddClassList) {
|
||||
let loadingNumber: number | string | null =
|
||||
target.getAttribute('loading-number')
|
||||
@ -71,9 +74,17 @@ export function createLoadingComponent(options: LoadingOptionsResolved) {
|
||||
destroySelf()
|
||||
}
|
||||
|
||||
const elLoadingComponent = {
|
||||
const elLoadingComponent = defineComponent({
|
||||
name: 'ElLoading',
|
||||
setup() {
|
||||
setup(_, { expose }) {
|
||||
const ns = useNamespace('loading')
|
||||
const zIndex = useZIndex()
|
||||
|
||||
expose({
|
||||
ns,
|
||||
zIndex,
|
||||
})
|
||||
|
||||
return () => {
|
||||
const svg = data.spinner || data.svg
|
||||
const spinner = h(
|
||||
@ -136,7 +147,7 @@ export function createLoadingComponent(options: LoadingOptionsResolved) {
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const loadingInstance = createApp(elLoadingComponent)
|
||||
const vm = loadingInstance.mount(document.createElement('div'))
|
||||
|
@ -3,8 +3,9 @@ import { nextTick } from 'vue'
|
||||
import { isString } from '@vue/shared'
|
||||
import { isClient } from '@vueuse/core'
|
||||
import { addClass, getStyle, removeClass } from '@element-plus/utils'
|
||||
import { useNamespace, useZIndex } from '@element-plus/hooks'
|
||||
import { createLoadingComponent } from './loading'
|
||||
|
||||
import type { UseNamespaceReturn, UseZIndexReturn } from '@element-plus/hooks'
|
||||
import type { LoadingInstance } from './loading'
|
||||
import type { LoadingOptionsResolved } from '..'
|
||||
import type { LoadingOptions } from './types'
|
||||
@ -93,7 +94,7 @@ const addStyle = async (
|
||||
parent: HTMLElement,
|
||||
instance: LoadingInstance
|
||||
) => {
|
||||
const { nextZIndex } = useZIndex()
|
||||
const { nextZIndex } = (instance.vm as any).zIndex as UseZIndexReturn
|
||||
|
||||
const maskStyle: CSSProperties = {}
|
||||
if (options.fullscreen) {
|
||||
@ -135,7 +136,7 @@ const addClassList = (
|
||||
parent: HTMLElement,
|
||||
instance: LoadingInstance
|
||||
) => {
|
||||
const ns = useNamespace('loading')
|
||||
const ns = (instance.vm as any).ns as UseNamespaceReturn
|
||||
|
||||
if (
|
||||
!['absolute', 'fixed', 'sticky'].includes(instance.originalPosition.value)
|
||||
|
@ -164,12 +164,9 @@ import { TrapFocus } from '@element-plus/directives'
|
||||
import {
|
||||
useDraggable,
|
||||
useId,
|
||||
useLocale,
|
||||
useLockscreen,
|
||||
useNamespace,
|
||||
useRestoreActive,
|
||||
useSameTarget,
|
||||
useZIndex,
|
||||
} from '@element-plus/hooks'
|
||||
import ElInput from '@element-plus/components/input'
|
||||
import { useFormSize } from '@element-plus/components/form'
|
||||
@ -181,8 +178,9 @@ import {
|
||||
} from '@element-plus/utils'
|
||||
import { ElIcon } from '@element-plus/components/icon'
|
||||
import ElFocusTrap from '@element-plus/components/focus-trap'
|
||||
import { useGlobalComponentSettings } from '@element-plus/components/config-provider'
|
||||
|
||||
import type { ComponentPublicInstance, PropType } from 'vue'
|
||||
import type { ComponentPublicInstance, DefineComponent, PropType } from 'vue'
|
||||
import type { ComponentSize } from '@element-plus/constants'
|
||||
import type {
|
||||
Action,
|
||||
@ -251,10 +249,12 @@ export default defineComponent({
|
||||
emits: ['vanish', 'action'],
|
||||
setup(props, { emit }) {
|
||||
// const popup = usePopup(props, doClose)
|
||||
const { t } = useLocale()
|
||||
const ns = useNamespace('message-box')
|
||||
const { locale, zIndex, ns } = useGlobalComponentSettings('message-box')
|
||||
|
||||
const { t } = locale
|
||||
const { nextZIndex } = zIndex
|
||||
|
||||
const visible = ref(false)
|
||||
const { nextZIndex } = useZIndex()
|
||||
// s represents state
|
||||
const state = reactive<MessageBoxState>({
|
||||
// autofocus element when open message-box
|
||||
@ -501,5 +501,5 @@ export default defineComponent({
|
||||
t,
|
||||
}
|
||||
},
|
||||
})
|
||||
}) as DefineComponent
|
||||
</script>
|
||||
|
@ -49,8 +49,8 @@ import { useEventListener, useResizeObserver, useTimeoutFn } from '@vueuse/core'
|
||||
import { TypeComponents, TypeComponentsMap } from '@element-plus/utils'
|
||||
import { EVENT_CODE } from '@element-plus/constants'
|
||||
import ElBadge from '@element-plus/components/badge'
|
||||
import { useGlobalComponentSettings } from '@element-plus/components/config-provider'
|
||||
import { ElIcon } from '@element-plus/components/icon'
|
||||
import { useNamespace, useZIndex } from '@element-plus/hooks'
|
||||
import { messageEmits, messageProps } from './message'
|
||||
import { getLastOffset, getOffsetOrSpace } from './instance'
|
||||
import type { BadgeProps } from '@element-plus/components/badge'
|
||||
@ -65,8 +65,8 @@ defineOptions({
|
||||
const props = defineProps(messageProps)
|
||||
defineEmits(messageEmits)
|
||||
|
||||
const ns = useNamespace('message')
|
||||
const { currentZIndex, nextZIndex } = useZIndex()
|
||||
const { ns, zIndex } = useGlobalComponentSettings('message')
|
||||
const { currentZIndex, nextZIndex } = zIndex
|
||||
|
||||
const messageRef = ref<HTMLDivElement>()
|
||||
const visible = ref(false)
|
||||
|
@ -3,7 +3,6 @@ import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, test, vi } from 'vitest'
|
||||
import { TypeComponentsMap } from '@element-plus/utils'
|
||||
import { EVENT_CODE } from '@element-plus/constants'
|
||||
import { useZIndex } from '@element-plus/hooks'
|
||||
import { notificationTypes } from '../src/notification'
|
||||
import Notification from '../src/notification.vue'
|
||||
|
||||
@ -40,10 +39,11 @@ describe('Notification.vue', () => {
|
||||
expect(wrapper.vm.visible).toBe(true)
|
||||
expect(wrapper.vm.iconComponent).toBeUndefined()
|
||||
expect(wrapper.vm.horizontalClass).toBe('right')
|
||||
expect(wrapper.vm.positionStyle).toEqual({
|
||||
top: '0px',
|
||||
zIndex: 0,
|
||||
})
|
||||
expect(wrapper.vm.positionStyle).toEqual(
|
||||
expect.objectContaining({
|
||||
top: '0px',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
test('should be able to render VNode', () => {
|
||||
@ -80,19 +80,15 @@ describe('Notification.vue', () => {
|
||||
expect(HTMLWrapper.find(`.${tagClass}`).exists()).toBe(false)
|
||||
})
|
||||
|
||||
test('should be able to render z-index style with zIndex flag', () => {
|
||||
const { nextZIndex } = useZIndex()
|
||||
const zIndex = nextZIndex()
|
||||
const wrapper = _mount({
|
||||
props: {
|
||||
zIndex,
|
||||
},
|
||||
})
|
||||
test('should be able to render z-index style with zIndex flag', async () => {
|
||||
const wrapper = _mount({})
|
||||
await nextTick()
|
||||
|
||||
expect(wrapper.vm.positionStyle).toEqual({
|
||||
top: '0px',
|
||||
zIndex,
|
||||
})
|
||||
expect(wrapper.vm.positionStyle).toEqual(
|
||||
expect.objectContaining({
|
||||
top: '0px',
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -43,7 +43,7 @@ import { useEventListener, useTimeoutFn } from '@vueuse/core'
|
||||
import { CloseComponents, TypeComponentsMap } from '@element-plus/utils'
|
||||
import { EVENT_CODE } from '@element-plus/constants'
|
||||
import { ElIcon } from '@element-plus/components/icon'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { useGlobalComponentSettings } from '@element-plus/components/config-provider'
|
||||
import { notificationEmits, notificationProps } from './notification'
|
||||
|
||||
import type { CSSProperties } from 'vue'
|
||||
@ -55,7 +55,9 @@ defineOptions({
|
||||
const props = defineProps(notificationProps)
|
||||
defineEmits(notificationEmits)
|
||||
|
||||
const ns = useNamespace('notification')
|
||||
const { ns, zIndex } = useGlobalComponentSettings('notification')
|
||||
const { nextZIndex, currentZIndex } = zIndex
|
||||
|
||||
const { Close } = CloseComponents
|
||||
|
||||
const visible = ref(false)
|
||||
@ -82,7 +84,7 @@ const verticalProperty = computed(() =>
|
||||
const positionStyle = computed<CSSProperties>(() => {
|
||||
return {
|
||||
[verticalProperty.value]: `${props.offset}px`,
|
||||
zIndex: props.zIndex,
|
||||
zIndex: currentZIndex.value,
|
||||
}
|
||||
})
|
||||
|
||||
@ -118,6 +120,7 @@ function onKeydown({ code }: KeyboardEvent) {
|
||||
// lifecycle
|
||||
onMounted(() => {
|
||||
startTimer()
|
||||
nextZIndex()
|
||||
visible.value = true
|
||||
})
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { createVNode, render } from 'vue'
|
||||
import { isClient } from '@vueuse/core'
|
||||
import { useZIndex } from '@element-plus/hooks'
|
||||
import { debugWarn, isElement, isString, isVNode } from '@element-plus/utils'
|
||||
import NotificationConstructor from './notification.vue'
|
||||
import { notificationTypes } from './notification'
|
||||
@ -45,12 +44,9 @@ const notify: NotifyFn & Partial<Notify> & { _context: AppContext | null } =
|
||||
})
|
||||
verticalOffset += GAP_SIZE
|
||||
|
||||
const { nextZIndex } = useZIndex()
|
||||
|
||||
const id = `notification_${seed++}`
|
||||
const userOnClose = options.onClose
|
||||
const props: Partial<NotificationProps> = {
|
||||
zIndex: nextZIndex(),
|
||||
...options,
|
||||
offset: verticalOffset,
|
||||
id,
|
||||
|
@ -50,10 +50,18 @@ const _mount = (template: string, data: any = () => ({}), otherObj?) =>
|
||||
},
|
||||
template,
|
||||
data,
|
||||
setup() {
|
||||
return usePopperContainerId()
|
||||
},
|
||||
...otherObj,
|
||||
},
|
||||
{
|
||||
attachTo: 'body',
|
||||
global: {
|
||||
provide: {
|
||||
namespace: 'el',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@ -1896,8 +1904,8 @@ describe('Select', () => {
|
||||
)
|
||||
|
||||
await nextTick()
|
||||
const { selector } = usePopperContainerId()
|
||||
expect(document.body.querySelector(selector.value).innerHTML).not.toBe('')
|
||||
const { selector } = wrapper.vm
|
||||
expect(document.body.querySelector(selector).innerHTML).not.toBe('')
|
||||
})
|
||||
|
||||
it('should not mount on the popper container', async () => {
|
||||
@ -1925,8 +1933,8 @@ describe('Select', () => {
|
||||
)
|
||||
|
||||
await nextTick()
|
||||
const { selector } = usePopperContainerId()
|
||||
expect(document.body.querySelector(selector.value).innerHTML).toBe('')
|
||||
const { selector } = wrapper.vm
|
||||
expect(document.body.querySelector(selector).innerHTML).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -13,6 +13,7 @@ describe('no injection value', () => {
|
||||
const idInjection = useIdInjection()
|
||||
return idInjection
|
||||
},
|
||||
template: '<div></div>',
|
||||
})
|
||||
|
||||
expect(wrapper.vm.prefix).toMatch(/^\d{0,4}$/)
|
||||
|
@ -67,4 +67,31 @@ describe('use-locale', () => {
|
||||
const t = buildTranslator(English)
|
||||
expect(t('el.popconfirm.someThing')).toBe('el.popconfirm.someThing')
|
||||
})
|
||||
|
||||
describe('overrides', () => {
|
||||
it('should be override correctly', () => {
|
||||
const override = computed(() => English)
|
||||
|
||||
const wrapper = mount(
|
||||
defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const { locale } = useLocale(override)
|
||||
expose({
|
||||
locale,
|
||||
})
|
||||
},
|
||||
template: '<div></div>',
|
||||
}),
|
||||
{
|
||||
global: {
|
||||
provide: {
|
||||
locale: Chinese,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(wrapper.vm.locale).toBe(override.value)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defineComponent, nextTick } from 'vue'
|
||||
import { computed, defineComponent, nextTick } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { provideGlobalConfig } from '@element-plus/components/config-provider'
|
||||
@ -81,4 +81,31 @@ describe('use-locale', () => {
|
||||
expect(style).toMatch('--ep-table-text-color: #409eff;')
|
||||
expect(style).not.toMatch('--ep-table-active-color:')
|
||||
})
|
||||
|
||||
it('overrides namespace', () => {
|
||||
const overrides = 'override'
|
||||
const { vm } = mount(
|
||||
defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const { namespace } = useNamespace(
|
||||
'ns',
|
||||
computed(() => overrides)
|
||||
)
|
||||
expose({
|
||||
namespace,
|
||||
})
|
||||
},
|
||||
template: '<div></div>',
|
||||
}),
|
||||
{
|
||||
global: {
|
||||
provide: {
|
||||
namespace: 'el',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(vm.namespace).toBe(overrides)
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { nextTick } from 'vue'
|
||||
import { defineComponent, nextTick } from 'vue'
|
||||
import { config, mount, shallowMount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import * as vueuse from '@vueuse/core'
|
||||
@ -17,12 +17,15 @@ vi.mock('@vueuse/core', () => {
|
||||
})
|
||||
|
||||
const mountComponent = () =>
|
||||
shallowMount({
|
||||
setup() {
|
||||
usePopperContainer()
|
||||
return () => <div>{AXIOM}</div>
|
||||
},
|
||||
})
|
||||
shallowMount(
|
||||
defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const exposes = usePopperContainer()
|
||||
expose(exposes)
|
||||
return () => <div>{AXIOM}</div>
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
describe('usePopperContainer', () => {
|
||||
afterEach(() => {
|
||||
@ -30,17 +33,17 @@ describe('usePopperContainer', () => {
|
||||
})
|
||||
|
||||
it('should append container to the DOM root', async () => {
|
||||
mountComponent()
|
||||
const { vm } = mountComponent()
|
||||
await nextTick()
|
||||
const { selector } = usePopperContainerId()
|
||||
const { selector } = vm
|
||||
expect(document.body.querySelector(selector.value)).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not append container to the DOM root', async () => {
|
||||
;(vueuse as any).isClient = false
|
||||
mountComponent()
|
||||
const { vm } = mountComponent()
|
||||
await nextTick()
|
||||
const { selector } = usePopperContainerId()
|
||||
const { selector } = vm
|
||||
expect(document.body.querySelector(selector.value)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
@ -44,7 +44,7 @@ export const buildLocaleContext = (
|
||||
export const localeContextKey: InjectionKey<Ref<Language | undefined>> =
|
||||
Symbol('localeContextKey')
|
||||
|
||||
export const useLocale = () => {
|
||||
const locale = inject(localeContextKey, ref())!
|
||||
export const useLocale = (localeOverrides?: Ref<Language | undefined>) => {
|
||||
const locale = localeOverrides || inject(localeContextKey, ref())!
|
||||
return buildLocaleContext(computed(() => locale.value || English))
|
||||
}
|
||||
|
@ -28,17 +28,20 @@ const _bem = (
|
||||
export const namespaceContextKey: InjectionKey<Ref<string | undefined>> =
|
||||
Symbol('localeContextKey')
|
||||
|
||||
export const useGetDerivedNamespace = () => {
|
||||
const derivedNamespace = inject(namespaceContextKey, ref(defaultNamespace))
|
||||
export const useGetDerivedNamespace = (namespaceOverrides?: Ref<string>) => {
|
||||
const derivedNamespace =
|
||||
namespaceOverrides || inject(namespaceContextKey, ref(defaultNamespace))
|
||||
const namespace = computed(() => {
|
||||
return unref(derivedNamespace) || defaultNamespace
|
||||
})
|
||||
return namespace
|
||||
}
|
||||
|
||||
export const useNamespace = (block: string) => {
|
||||
const namespace = useGetDerivedNamespace()
|
||||
|
||||
export const useNamespace = (
|
||||
block: string,
|
||||
namespaceOverrides?: Ref<string>
|
||||
) => {
|
||||
const namespace = useGetDerivedNamespace(namespaceOverrides)
|
||||
const b = (blockSuffix = '') =>
|
||||
_bem(namespace.value, block, blockSuffix, '', '')
|
||||
const e = (element?: string) =>
|
||||
|
@ -28,10 +28,10 @@ const createContainer = (id: string) => {
|
||||
}
|
||||
|
||||
export const usePopperContainer = () => {
|
||||
const { id, selector } = usePopperContainerId()
|
||||
onBeforeMount(() => {
|
||||
if (!isClient) return
|
||||
|
||||
const { id, selector } = usePopperContainerId()
|
||||
// This is for bypassing the error that when under testing env, we often encounter
|
||||
// document.body.innerHTML = '' situation
|
||||
// for this we need to disable the caching since it's not really needed
|
||||
@ -42,4 +42,9 @@ export const usePopperContainer = () => {
|
||||
cachedContainer = createContainer(id.value)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id,
|
||||
selector,
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import { isNumber } from '@element-plus/utils'
|
||||
import type { InjectionKey, Ref } from 'vue'
|
||||
|
||||
const zIndex = ref(0)
|
||||
const defaultInitialZIndex = 2000
|
||||
export const defaultInitialZIndex = 2000
|
||||
|
||||
export const zIndexContextKey: InjectionKey<Ref<number | undefined>> =
|
||||
Symbol('zIndexContextKey')
|
||||
|
||||
export const useZIndex = () => {
|
||||
const zIndexInjection = inject(zIndexContextKey, undefined)
|
||||
export const useZIndex = (zIndexOverrides?: Ref<number>) => {
|
||||
const zIndexInjection = zIndexOverrides || inject(zIndexContextKey, undefined)
|
||||
const initialZIndex = computed(() => {
|
||||
const zIndexFromInjection = unref(zIndexInjection)
|
||||
return isNumber(zIndexFromInjection)
|
||||
@ -30,3 +30,5 @@ export const useZIndex = () => {
|
||||
nextZIndex,
|
||||
}
|
||||
}
|
||||
|
||||
export type UseZIndexReturn = ReturnType<typeof useZIndex>
|
||||
|
Loading…
x
Reference in New Issue
Block a user