mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
address PR issue
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { h } from 'vue'
|
||||
import { mount, VueWrapper } from '@vue/test-utils'
|
||||
import { h, ComponentPublicInstance, nextTick } from 'vue'
|
||||
import * as domExports from '../../utils/dom'
|
||||
import { eventKeys } from '../../utils/aria'
|
||||
import Notification from '../src/index.vue'
|
||||
@@ -102,48 +102,17 @@ describe('Notification.vue', () => {
|
||||
})
|
||||
|
||||
describe('Notification.type', () => {
|
||||
test('should be able to render success notification', () => {
|
||||
const type = 'success'
|
||||
const wrapper = _mount({
|
||||
props: {
|
||||
type,
|
||||
},
|
||||
})
|
||||
test('should be able to render typed notification', () => {
|
||||
let wrapper: VueWrapper<ComponentPublicInstance>
|
||||
|
||||
expect(wrapper.find(`.el-icon-${type}`).exists()).toBe(true)
|
||||
})
|
||||
|
||||
test('should be able to render warning notification', () => {
|
||||
const type = 'warning'
|
||||
const wrapper = _mount({
|
||||
props: {
|
||||
type,
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.find(`.el-icon-${type}`).exists()).toBe(true)
|
||||
})
|
||||
|
||||
test('should be able to render info notification', () => {
|
||||
const type = 'info'
|
||||
const wrapper = _mount({
|
||||
props: {
|
||||
type,
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.find(`.el-icon-${type}`).exists()).toBe(true)
|
||||
})
|
||||
|
||||
test('should be able to render error notification', () => {
|
||||
const type = 'error'
|
||||
const wrapper = _mount({
|
||||
props: {
|
||||
type,
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.find(`.el-icon-${type}`).exists()).toBe(true)
|
||||
for (const type of ['success', 'warning', 'info', 'error']) {
|
||||
wrapper = _mount({
|
||||
props: {
|
||||
type,
|
||||
},
|
||||
})
|
||||
expect(wrapper.find('.el-notification__icon').classes()).toContain(`el-icon-${type}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('should not be able to render invalid type icon', () => {
|
||||
@@ -154,7 +123,7 @@ describe('Notification.vue', () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.find(`.el-icon-${type}`).exists()).toBe(false)
|
||||
expect(wrapper.find('.el-notification__icon').classes()).not.toContain(`el-icon-${type}`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -170,7 +139,8 @@ describe('Notification.vue', () => {
|
||||
|
||||
const closeBtn = wrapper.find('.el-notification__closeBtn')
|
||||
expect(closeBtn.exists()).toBe(true)
|
||||
await closeBtn.trigger('click')
|
||||
wrapper.vm.destroyElement()
|
||||
|
||||
expect(onClose).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { isVNode } from 'vue'
|
||||
import Notification, { close, closeAll } from '../src/notify'
|
||||
import type { NotificationVM } from '../src/notification.constants'
|
||||
import type { INotificationHandle } from '../src/notification'
|
||||
|
||||
jest.useFakeTimers()
|
||||
|
||||
@@ -11,11 +10,12 @@ describe('Notification on command', () => {
|
||||
closeAll()
|
||||
})
|
||||
|
||||
test('it should get component instance when calling notification constructor', async () => {
|
||||
const vm = Notification()
|
||||
expect(isVNode(vm)).toBe(true)
|
||||
test('it should get component handle', async () => {
|
||||
const handle = Notification()
|
||||
expect(document.querySelector(selector)).toBeDefined()
|
||||
jest.runAllTicks()
|
||||
handle.close()
|
||||
expect(document.querySelector(selector)).toBeNull()
|
||||
})
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('Notification on command', () => {
|
||||
})
|
||||
|
||||
test('it should close all notifications', () => {
|
||||
const notifications: NotificationVM[] = []
|
||||
const notifications: INotificationHandle[] = []
|
||||
const onClose = jest.fn()
|
||||
for (let i = 0; i < 4; i++) {
|
||||
notifications.push(Notification({
|
||||
@@ -43,4 +43,10 @@ describe('Notification on command', () => {
|
||||
expect(document.querySelectorAll(selector).length).toBe(0)
|
||||
})
|
||||
|
||||
test('it should be able to render all types notification', () => {
|
||||
for (const type of ['success', 'warning', 'error', 'info'] as const) {
|
||||
Notification[type]()
|
||||
expect(document.querySelector(`.el-icon-${type}`)).toBeDefined()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { App } from 'vue'
|
||||
import Notification from './src/index.vue'
|
||||
import Notify from './src/notify'
|
||||
export default (app: App): void => {
|
||||
app.component(Notification.name, Notification)
|
||||
app.config.globalProperties.$notify = Notify
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<transition name="el-notification-fade">
|
||||
<div
|
||||
v-if="visible"
|
||||
v-show="visible"
|
||||
:id="id"
|
||||
:class="['el-notification', customClass, horizontalClass]"
|
||||
:style="positionStyle"
|
||||
@@ -10,8 +10,15 @@
|
||||
@mouseleave="startTimer()"
|
||||
@click="click"
|
||||
>
|
||||
<i v-if="type || iconClass" class="el-notification__icon" :class="[typeClass, iconClass]"></i>
|
||||
<div class="el-notification__group" :class="{ 'is-with-icon': typeClass || iconClass }">
|
||||
<i
|
||||
v-if="type || iconClass"
|
||||
class="el-notification__icon"
|
||||
:class="[typeClass, iconClass]"
|
||||
></i>
|
||||
<div
|
||||
class="el-notification__group"
|
||||
:class="{ 'is-with-icon': typeClass || iconClass }"
|
||||
>
|
||||
<h2 class="el-notification__title" v-text="title"></h2>
|
||||
<div v-show="message" class="el-notification__content">
|
||||
<slot>
|
||||
@@ -21,13 +28,17 @@
|
||||
<p v-else v-html="message"></p>
|
||||
</slot>
|
||||
</div>
|
||||
<div v-if="showClose" class="el-notification__closeBtn el-icon-close" @click.stop="close"></div>
|
||||
<div
|
||||
v-if="showClose"
|
||||
class="el-notification__closeBtn el-icon-close"
|
||||
@click.stop="close"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, PropType } from 'vue'
|
||||
import { defineComponent, computed, ref, PropType, nextTick } from 'vue'
|
||||
// notificationVM is an alias of vue.VNode
|
||||
import type { NotificationVM } from './notification.constants'
|
||||
import { eventKeys } from '../../utils/aria'
|
||||
@@ -92,7 +103,7 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
const visible = ref(true)
|
||||
const visible = ref(false)
|
||||
const closed = ref(false)
|
||||
const timer = ref(null)
|
||||
|
||||
@@ -122,6 +133,7 @@ export default defineComponent({
|
||||
}
|
||||
}, this.duration)
|
||||
}
|
||||
this.visible = true
|
||||
on(document, 'keydown', this.keydown)
|
||||
},
|
||||
beforeUnmount() {
|
||||
@@ -129,8 +141,9 @@ export default defineComponent({
|
||||
},
|
||||
methods: {
|
||||
destroyElement() {
|
||||
this.visible = false
|
||||
off(this.$el, 'transitionend', this.destroyElement)
|
||||
this.$el.parentNode.removeChild(this.$el)
|
||||
this.onClose()
|
||||
},
|
||||
// start counting down to destroy notification instance
|
||||
startTimer() {
|
||||
@@ -154,7 +167,6 @@ export default defineComponent({
|
||||
close() {
|
||||
this.closed = true
|
||||
this.timer = null
|
||||
this.onClose()
|
||||
},
|
||||
keydown({ keyCode }: KeyboardEvent) {
|
||||
if (keyCode === eventKeys.delete || keyCode === eventKeys.backspace) {
|
||||
@@ -171,3 +183,16 @@ export default defineComponent({
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Due to the code */
|
||||
/* TODO: remove these code */
|
||||
.el-notification-fade-enter-active.right {
|
||||
right: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
.el-notification-fade-enter-active.left {
|
||||
left: 0;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { VNode } from 'vue'
|
||||
|
||||
export type INotification = (options?: INotificationOptions) => NotificationVM
|
||||
|
||||
export interface INotificationHandle {
|
||||
close: () => void
|
||||
}
|
||||
|
||||
export type INotification = (options?: INotificationOptions) => INotificationHandle
|
||||
|
||||
export type INotificationOptions = {
|
||||
customClass?: string
|
||||
@@ -10,8 +15,8 @@ export type INotificationOptions = {
|
||||
id?: string
|
||||
message?: string | VNode
|
||||
zIndex?: number
|
||||
onClose?: () => unknown
|
||||
onClick?: () => unknown
|
||||
onClose?: () => void
|
||||
onClick?: () => void
|
||||
offset?: number // defaults 0
|
||||
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' // default top-right
|
||||
showClose?: boolean
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createVNode, render } from 'vue'
|
||||
import NotificationConstructor from './index.vue'
|
||||
import type { INotificationOptions, INotification, NotificationQueue, NotificationVM } from './notification.constants'
|
||||
import type { INotificationOptions, INotification, NotificationQueue, NotificationVM } from './notification'
|
||||
import isServer from '../../utils/isServer'
|
||||
import PopupManager from '../../utils/popup-manager'
|
||||
import { isVNode } from '../../utils/util'
|
||||
@@ -43,19 +43,27 @@ const Notification: INotification = function(options = {}) {
|
||||
container.className = `container_${id}`
|
||||
container.style.zIndex = String()
|
||||
|
||||
vm = createVNode(NotificationConstructor, options, isVNode(options.message) ? {
|
||||
default: () => options.message,
|
||||
}: null)
|
||||
vm = createVNode(
|
||||
NotificationConstructor,
|
||||
options,
|
||||
isVNode(options.message)
|
||||
? {
|
||||
default: () => options.message,
|
||||
}
|
||||
: null,
|
||||
)
|
||||
render(vm, container)
|
||||
notifications.push({ vm, $el: container })
|
||||
document.body.appendChild(container.firstElementChild)
|
||||
document.body.appendChild(container)
|
||||
|
||||
return vm
|
||||
return {
|
||||
close: options.onClose,
|
||||
}
|
||||
};
|
||||
|
||||
(['success', 'warning', 'info', 'error'] as const).forEach(type => {
|
||||
Object.assign(Notification, {
|
||||
type: options => {
|
||||
[type]: (options: NotificationVM | INotificationOptions | string = {}) => {
|
||||
if (typeof options === 'string' || isVNode(options)) {
|
||||
options = {
|
||||
message: options,
|
||||
@@ -82,6 +90,7 @@ export function close(
|
||||
const { vm, $el } = notifications[idx]
|
||||
if (!vm) return
|
||||
userOnClose?.(vm)
|
||||
|
||||
const removedHeight = vm.el.offsetHeight
|
||||
render(null, $el)
|
||||
|
||||
@@ -92,14 +101,16 @@ export function close(
|
||||
for (let i = idx; i < len; i++) {
|
||||
if (notifications[i].vm.component.props.position === position) {
|
||||
const verticalPos = vm.props.position.split('-')[0]
|
||||
notifications[i].vm.el.style[verticalPos] =
|
||||
parseInt(
|
||||
notifications[i].vm.el.style[verticalPos],
|
||||
10,
|
||||
) -
|
||||
removedHeight -
|
||||
16 +
|
||||
'px'
|
||||
const pos = parseInt(
|
||||
notifications[i].vm.el.style[verticalPos],
|
||||
10,
|
||||
) -
|
||||
removedHeight -
|
||||
16
|
||||
|
||||
notifications[i].vm.component.props.offset = pos
|
||||
render(notifications[i].vm, notifications[i].$el)
|
||||
// .vm.el.style[verticalPos] = pos
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user