diff --git a/packages/notification/__tests__/notification.spec.ts b/packages/notification/__tests__/notification.spec.ts index 388ed87dd9..719686d9f1 100644 --- a/packages/notification/__tests__/notification.spec.ts +++ b/packages/notification/__tests__/notification.spec.ts @@ -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 - 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() }) diff --git a/packages/notification/__tests__/notify.spec.ts b/packages/notification/__tests__/notify.spec.ts index 1c400211aa..4323a1e1f5 100644 --- a/packages/notification/__tests__/notify.spec.ts +++ b/packages/notification/__tests__/notify.spec.ts @@ -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() + } + }) }) diff --git a/packages/notification/index.ts b/packages/notification/index.ts index 395e0de457..37476281fd 100644 --- a/packages/notification/index.ts +++ b/packages/notification/index.ts @@ -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 } diff --git a/packages/notification/src/index.vue b/packages/notification/src/index.vue index d923ce8602..7a09e62309 100644 --- a/packages/notification/src/index.vue +++ b/packages/notification/src/index.vue @@ -1,7 +1,7 @@ + + diff --git a/packages/notification/src/notification.constants.ts b/packages/notification/src/notification.d.ts similarity index 84% rename from packages/notification/src/notification.constants.ts rename to packages/notification/src/notification.d.ts index 8786a4e1bb..140f866df9 100644 --- a/packages/notification/src/notification.constants.ts +++ b/packages/notification/src/notification.d.ts @@ -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 diff --git a/packages/notification/src/notify.ts b/packages/notification/src/notify.ts index 135fc1cd7b..d9705dfd65 100644 --- a/packages/notification/src/notify.ts +++ b/packages/notification/src/notify.ts @@ -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 } } }