feat(components): [el-message] & [el-notification] appendTo option added (#4012)

* feat(components): [el-message] & [el-notification] appendTo option added

* fix(components): unit test and replaced warn with debugWarn
This commit is contained in:
CodeSpikeX
2021-10-30 19:09:36 +05:00
committed by GitHub
parent 41981d8789
commit 7fe4e0b026
8 changed files with 126 additions and 30 deletions

View File

@@ -87,4 +87,32 @@ describe('Message on command', () => {
expect(Message.info).toBeInstanceOf(Function)
expect(Message.error).toBeInstanceOf(Function)
})
test('it should appendTo specified HTMLElement', async () => {
const htmlElement = document.createElement('div')
const handle = Message({
appendTo: htmlElement,
})
await rAF()
expect(htmlElement.querySelector(selector)).toBeTruthy()
handle.close()
await rAF()
await nextTick()
expect(htmlElement.querySelector(selector)).toBeFalsy()
})
test('it should appendTo specified selector', async () => {
const htmlElement = document.createElement('div')
htmlElement.classList.add('message-manager')
document.body.appendChild(htmlElement)
const handle = Message({
appendTo: '.message-manager',
})
await rAF()
expect(htmlElement.querySelector(selector)).toBeTruthy()
handle.close()
await rAF()
await nextTick()
expect(htmlElement.querySelector(selector)).toBeFalsy()
})
})

View File

@@ -2,6 +2,7 @@ import { createVNode, render } from 'vue'
import { isVNode } from '@element-plus/utils/util'
import PopupManager from '@element-plus/utils/popup-manager'
import isServer from '@element-plus/utils/isServer'
import { debugWarn } from '@element-plus/utils/error'
import MessageConstructor from './message.vue'
import { messageTypes } from './message'
@@ -38,6 +39,21 @@ const message: MessageFn & Partial<Message> = function (options = {}) {
},
}
let appendTo: HTMLElement | null = document.body
if (options.appendTo instanceof HTMLElement) {
appendTo = options.appendTo
} else if (typeof options.appendTo === 'string') {
appendTo = document.querySelector(options.appendTo)
}
// should fallback to default value with a warning
if (!(appendTo instanceof HTMLElement)) {
debugWarn(
'ElMessage',
'the appendTo option is not an HTMLElement. Falling back to document.body.'
)
appendTo = document.body
}
const container = document.createElement('div')
container.className = `container_${id}`
@@ -60,7 +76,7 @@ const message: MessageFn & Partial<Message> = function (options = {}) {
render(vm, container)
// instances will remove this item when close function gets called. So we do not need to worry about it.
instances.push({ vm })
document.body.appendChild(container.firstElementChild!)
appendTo.appendChild(container.firstElementChild!)
return {
// instead of calling the onClose function directly, setting this value so that we can have the full lifecycle

View File

@@ -62,7 +62,9 @@ export const messageEmits = {
}
export type MessageEmits = typeof messageEmits
export type MessageOptions = Omit<MessageProps, 'id'>
export type MessageOptions = Omit<MessageProps, 'id'> & {
appendTo?: HTMLElement | string
}
export type MessageOptionsTyped = Omit<MessageOptions, 'type'>
export interface MessageHandle {

View File

@@ -80,4 +80,33 @@ describe('Notification on command', () => {
expect(document.querySelector(`.el-icon-${type}`)).toBeDefined()
}
})
test('it should appendTo specified HTMLElement', async () => {
const htmlElement = document.createElement('div')
const handle = Notification({
appendTo: htmlElement,
})
await rAF()
expect(htmlElement.querySelector(selector)).toBeDefined()
handle.close()
await rAF()
await nextTick()
expect(htmlElement.querySelector(selector)).toBeNull()
})
test('it should appendTo specified selector', async () => {
const htmlElement = document.createElement('div')
htmlElement.classList.add('notification-manager')
document.body.appendChild(htmlElement)
const handle = Notification({
appendTo: '.notification-manager',
})
await rAF()
expect(htmlElement.querySelector(selector)).toBeDefined()
handle.close()
await rAF()
await nextTick()
expect(htmlElement.querySelector(selector)).toBeNull()
})
})

View File

@@ -76,7 +76,9 @@ export const notificationEmits = {
}
export type NotificationEmits = typeof notificationEmits
export type NotificationOptions = Omit<NotificationProps, 'id'>
export type NotificationOptions = Omit<NotificationProps, 'id'> & {
appendTo?: HTMLElement | string
}
export type NotificationOptionsTyped = Omit<NotificationOptions, 'type'>
export interface NotificationHandle {

View File

@@ -2,6 +2,7 @@ import { createVNode, render } from 'vue'
import isServer from '@element-plus/utils/isServer'
import PopupManager from '@element-plus/utils/popup-manager'
import { isVNode } from '@element-plus/utils/util'
import { debugWarn } from '@element-plus/utils/error'
import NotificationConstructor from './notification.vue'
import { notificationTypes } from './notification'
@@ -57,6 +58,22 @@ const notify: NotifyFn & Partial<Notify> = function (options = {}) {
},
}
let appendTo: HTMLElement | null = document.body
if (options.appendTo instanceof HTMLElement) {
appendTo = options.appendTo
} else if (typeof options.appendTo === 'string') {
appendTo = document.querySelector(options.appendTo)
}
// should fallback to default value with a warning
if (!(appendTo instanceof HTMLElement)) {
debugWarn(
'ElNotification',
'the appendTo option is not an HTMLElement. Falling back to document.body.'
)
appendTo = document.body
}
const container = document.createElement('div')
const vm = createVNode(
@@ -77,7 +94,7 @@ const notify: NotifyFn & Partial<Notify> = function (options = {}) {
// instances will remove this item when close function gets called. So we do not need to worry about it.
render(vm, container)
notifications[position].push({ vm })
document.body.appendChild(container.firstElementChild!)
appendTo.appendChild(container.firstElementChild!)
return {
// instead of calling the onClose function directly, setting this value so that we can have the full lifecycle