mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
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:
@@ -77,18 +77,19 @@ In this case you should call `ElMessage(options)`. We have also registered metho
|
||||
|
||||
## Options
|
||||
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
| ------------------------ | ------------------------------------------------------------------------------ | ------------------ | -------------------------- | ------- |
|
||||
| message | message text | string / VNode | — | — |
|
||||
| type | message type | string | success/warning/info/error | info |
|
||||
| icon | custom icon component, overrides `type` | string / Component | — | — |
|
||||
| dangerouslyUseHTMLString | whether `message` is treated as HTML string | boolean | — | false |
|
||||
| custom-class | custom class name for Message | string | — | — |
|
||||
| duration | display duration, millisecond. If set to 0, it will not turn off automatically | number | — | 3000 |
|
||||
| show-close | whether to show a close button | boolean | — | false |
|
||||
| center | whether to center the text | boolean | — | false |
|
||||
| on-close | callback function when closed with the message instance as the parameter | function | — | — |
|
||||
| offset | set the distance to the top of viewport | number | — | 20 |
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
| ------------------------ | ------------------------------------------------------------------------------ | -------------------- | -------------------------- | ------------- |
|
||||
| message | message text | string / VNode | — | — |
|
||||
| type | message type | string | success/warning/info/error | info |
|
||||
| icon-class | custom icon's class, overrides `type` | string | — | — |
|
||||
| dangerouslyUseHTMLString | whether `message` is treated as HTML string | boolean | — | false |
|
||||
| custom-class | custom class name for Message | string | — | — |
|
||||
| duration | display duration, millisecond. If set to 0, it will not turn off automatically | number | — | 3000 |
|
||||
| show-close | whether to show a close button | boolean | — | false |
|
||||
| center | whether to center the text | boolean | — | false |
|
||||
| on-close | callback function when closed with the message instance as the parameter | function | — | — |
|
||||
| offset | set the distance to the top of viewport | number | — | 20 |
|
||||
| appendTo | set the root element for the message | string / HTMLElement | - | document.body |
|
||||
|
||||
## Methods
|
||||
|
||||
|
||||
@@ -85,20 +85,21 @@ In this case you should call `ElNotification(options)`. We have also registered
|
||||
|
||||
## Options
|
||||
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------- | --------- |
|
||||
| title | title | string | — | — |
|
||||
| message | description text | string/Vue.VNode | — | — |
|
||||
| dangerouslyUseHTMLString | whether `message` is treated as HTML string | boolean | — | false |
|
||||
| type | notification type | string | success/warning/info/error | — |
|
||||
| icon | custom icon component. It will be overridden by `type` | string / Component | — | — |
|
||||
| customClass | custom class name for Notification | string | — | — |
|
||||
| duration | duration before close. It will not automatically close if set 0 | number | — | 4500 |
|
||||
| position | custom position | string | top-right/top-left/bottom-right/bottom-left | top-right |
|
||||
| showClose | whether to show a close button | boolean | — | true |
|
||||
| onClose | callback function when closed | function | — | — |
|
||||
| onClick | callback function when notification clicked | function | — | — |
|
||||
| offset | offset from the top edge of the screen. Every Notification instance of the same moment should have the same offset | number | — | 0 |
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------------- | ------------- |
|
||||
| title | title | string | — | — |
|
||||
| message | description text | string/Vue.VNode | — | — |
|
||||
| dangerouslyUseHTMLString | whether `message` is treated as HTML string | boolean | — | false |
|
||||
| type | notification type | string | success/warning/info/error | — |
|
||||
| iconClass | custom icon's class. It will be overridden by `type` | string | — | — |
|
||||
| customClass | custom class name for Notification | string | — | — |
|
||||
| duration | duration before close. It will not automatically close if set 0 | number | — | 4500 |
|
||||
| position | custom position | string | top-right/top-left/bottom-right/bottom-left | top-right |
|
||||
| showClose | whether to show a close button | boolean | — | true |
|
||||
| onClose | callback function when closed | function | — | — |
|
||||
| onClick | callback function when notification clicked | function | — | — |
|
||||
| offset | offset from the top edge of the screen. Every Notification instance of the same moment should have the same offset | number | — | 0 |
|
||||
| appendTo | set the root element for the notification | string / HTMLElement | - | document.body |
|
||||
|
||||
## Methods
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user