feat(components): [message-box] add append-to option (#10071)

This commit is contained in:
Danny Hebert 2022-10-18 09:47:26 -04:00 committed by GitHub
parent 01ce124940
commit 04820a4dcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 2 deletions

View File

@ -191,3 +191,4 @@ The corresponding methods are: `ElMessageBox`, `ElMessageBox.alert`, `ElMessageB
| draggable | whether MessageBox is draggable | boolean | — | false |
| round-button | whether to use round button | boolean | — | false |
| button-size | custom size of confirm and cancel buttons | string | small / default / large | default |
| append-to | set the root element for the message box | string \| HTMLElement | — | — |

View File

@ -1,7 +1,7 @@
// @ts-nocheck
import { markRaw } from 'vue'
import { mount } from '@vue/test-utils'
import { afterEach, describe, expect, it, test } from 'vitest'
import { afterEach, describe, expect, it, test, vi } from 'vitest'
import { rAF } from '@element-plus/test-utils/tick'
import { triggerNativeCompositeClick } from '@element-plus/test-utils/composite-click'
import { QuestionFilled as QuestionFilledIcon } from '@element-plus/icons-vue'
@ -11,6 +11,10 @@ import { ElMessageBox } from '..'
const selector = '.el-overlay'
const QuestionFilled = markRaw(QuestionFilledIcon)
vi.mock('@element-plus/utils/error', () => ({
debugWarn: vi.fn(),
}))
const _mount = (invoker: () => void) => {
return mount(
{
@ -28,6 +32,7 @@ const _mount = (invoker: () => void) => {
describe('MessageBox', () => {
afterEach(async () => {
MessageBox.close()
document.body.innerHTML = ''
await rAF()
})
@ -275,6 +280,52 @@ describe('MessageBox', () => {
})
})
describe('append to', () => {
it('should append to body if parameter is not provided', () => {
MessageBox({
title: 'append to test',
message: 'append to test',
})
const msgbox: HTMLElement = document.querySelector(`body > ${selector}`)
expect(msgbox).toBeDefined()
})
it('should append to body if element does not exist', () => {
MessageBox({
title: 'append to test',
message: 'append to test',
appendTo: '.not-existing-selector',
})
const msgbox: HTMLElement = document.querySelector(`body > ${selector}`)
expect(msgbox).toBeDefined()
})
it('should append to HtmlElement provided', () => {
const htmlElement = document.createElement('div')
document.body.appendChild(htmlElement)
MessageBox({
title: 'append to test',
message: 'append to test',
appendTo: htmlElement,
})
const msgbox: HTMLElement = htmlElement.querySelector(selector)
expect(msgbox).toBeDefined()
})
it('should append to selector provided', () => {
const htmlElement = document.createElement('div')
htmlElement.className = 'custom-html-element'
document.body.appendChild(htmlElement)
MessageBox({
title: 'append to test',
message: 'append to test',
appendTo: '.custom-html-element',
})
const msgbox: HTMLElement = htmlElement.querySelector(selector)
expect(msgbox).toBeDefined()
})
})
describe('accessibility', () => {
test('title attribute should set aria-label', async () => {
const title = 'Hello World'

View File

@ -170,6 +170,9 @@ export interface ElMessageBoxOptions {
/** Custom size of confirm and cancel buttons */
buttonSize?: ComponentSize
/** Custom element to append the message box to */
appendTo?: HTMLElement | string
}
export type ElMessageBoxShortcutMethod = ((

View File

@ -1,7 +1,9 @@
import { createVNode, render } from 'vue'
import { isClient } from '@vueuse/core'
import {
debugWarn,
hasOwn,
isElement,
isFunction,
isObject,
isString,
@ -33,6 +35,28 @@ const messageInstance = new Map<
}
>()
const getAppendToElement = (props: any): HTMLElement => {
let appendTo: HTMLElement | null = document.body
if (props.appendTo) {
if (isString(props.appendTo)) {
appendTo = document.querySelector<HTMLElement>(props.appendTo)
}
if (isElement(props.appendTo)) {
appendTo = props.appendTo
}
// should fallback to default value with a warning
if (!isElement(appendTo)) {
debugWarn(
'ElMessageBox',
'the appendTo option is not an HTMLElement. Falling back to document.body.'
)
appendTo = document.body
}
}
return appendTo
}
const initInstance = (
props: any,
container: HTMLElement,
@ -51,7 +75,7 @@ const initInstance = (
)
vnode.appContext = appContext
render(vnode, container)
document.body.appendChild(container.firstElementChild!)
getAppendToElement(props).appendChild(container.firstElementChild!)
return vnode.component
}