+
+
+
+
+
diff --git a/packages/loading/__tests__/Loading.spec.js b/packages/loading/__tests__/Loading.spec.js
new file mode 100644
index 000000000..7912924a7
--- /dev/null
+++ b/packages/loading/__tests__/Loading.spec.js
@@ -0,0 +1,293 @@
+// import { getStyle } from '../../../src/utils/dom'
+// import { createVue, destroyVM } from '../util'
+// import Vue from 'vue'
+// import LoadingRaw from 'packages/loading'
+// const Loading = LoadingRaw.service
+
+// import { mount } from '@vue/test-utils'
+import Loading from '../index'
+
+const LoadingService = Loading.service
+
+describe('Loading', () => {
+ let loadingInstance, loadingInstance2
+ afterEach((done) => {
+ if (loadingInstance) {
+ loadingInstance.close()
+ loadingInstance.ctx.$el &&
+ loadingInstance.ctx.$el.parentNode &&
+ loadingInstance.ctx.$el.parentNode.removeChild(loadingInstance.ctx.$el)
+ }
+ if (loadingInstance2) {
+ loadingInstance2.close()
+ loadingInstance2.ctx.$el &&
+ loadingInstance2.ctx.$el.parentNode &&
+ loadingInstance2.ctx.$el.parentNode.removeChild(
+ loadingInstance2.ctx.$el
+ )
+ }
+ loadingInstance = loadingInstance2 = null
+ setTimeout(() => {
+ done()
+ }, 100)
+ })
+
+ // describe('as a directive', () => {
+ // it('create', done => {
+ // vm = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // loading: true
+ // }
+ // }
+ // })
+ // Vue.nextTick(() => {
+ // const mask = vm.$el.querySelector('.el-loading-mask')
+ // expect(mask).to.exist
+ // vm.loading = false
+ // setTimeout(() => {
+ // expect(mask.style.display).to.equal('none')
+ // done()
+ // }, 100)
+ // })
+ // })
+ //
+ // it('unbind', done => {
+ // const vm1 = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // show: true,
+ // loading: true
+ // }
+ // }
+ // })
+ // const vm2 = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // show: true,
+ // loading: true
+ // }
+ // }
+ // })
+ // Vue.nextTick(() => {
+ // vm1.loading = false
+ // vm2.loading = false
+ // Vue.nextTick(() => {
+ // vm1.show = false
+ // vm2.show = false
+ // Vue.nextTick(() => {
+ // expect(document.querySelector('.el-loading-mask')).to.not.exist
+ // done()
+ // })
+ // })
+ // })
+ // })
+ //
+ // it('body', done => {
+ // vm = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // loading: true
+ // }
+ // }
+ // }, true)
+ // Vue.nextTick(() => {
+ // const mask = document.querySelector('.el-loading-mask')
+ // expect(mask.parentNode === document.body).to.true
+ // vm.loading = false
+ // document.body.removeChild(mask)
+ // document.body.removeChild(vm.$el)
+ // done()
+ // })
+ // })
+ //
+ // it('fullscreen', done => {
+ // vm = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // loading: true
+ // }
+ // }
+ // }, true)
+ // Vue.nextTick(() => {
+ // const mask = document.querySelector('.el-loading-mask')
+ // expect(mask.parentNode === document.body).to.true
+ // expect(mask.classList.contains('is-fullscreen')).to.true
+ // vm.loading = false
+ // document.body.removeChild(mask)
+ // document.body.removeChild(vm.$el)
+ // done()
+ // })
+ // })
+ //
+ // it('lock', done => {
+ // vm = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // loading: true
+ // }
+ // }
+ // }, true)
+ // Vue.nextTick(() => {
+ // expect(getStyle(document.body, 'overflow')).to.equal('hidden')
+ // vm.loading = false
+ // document.body.removeChild(document.querySelector('.el-loading-mask'))
+ // document.body.removeChild(vm.$el)
+ // done()
+ // })
+ // })
+ //
+ // it('text', done => {
+ // vm = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // loading: true
+ // }
+ // }
+ // }, true)
+ // Vue.nextTick(() => {
+ // const mask = document.querySelector('.el-loading-mask')
+ // const text = mask.querySelector('.el-loading-text')
+ // expect(text).to.exist
+ // expect(text.textContent).to.equal('拼命加载中')
+ // done()
+ // })
+ // })
+ //
+ // it('customClass', done => {
+ // vm = createVue({
+ // template: `
+ //
+ // `,
+ //
+ // data() {
+ // return {
+ // loading: true
+ // }
+ // }
+ // }, true)
+ // Vue.nextTick(() => {
+ // const mask = document.querySelector('.el-loading-mask')
+ // expect(mask.classList.contains('loading-custom-class')).to.true
+ // done()
+ // })
+ // })
+ // })
+
+ describe('as a service', () => {
+ it('create', () => {
+ loadingInstance = LoadingService()
+ expect(document.querySelector('.el-loading-mask')).toBeTruthy()
+ loadingInstance.close()
+ })
+
+ it('close', () => {
+ loadingInstance = LoadingService()
+ loadingInstance.close()
+ expect(loadingInstance.ctx.visible).toBe(false)
+ })
+
+ it('target', () => {
+ const container = document.createElement('div')
+ container.classList.add('loading-container')
+ document.body.appendChild(container)
+ loadingInstance = Loading.service({ target: container })
+ const mask = document.querySelector('.el-loading-mask')
+ expect(mask).toBeTruthy()
+ expect(mask.parentNode).toEqual(container)
+ })
+
+ it('body', () => {
+ const container = document.createElement('div')
+ container.classList.add('loading-container')
+ document.body.appendChild(container)
+ loadingInstance = Loading.service({ target: container, body: true })
+ const mask = document.querySelector('.el-loading-mask')
+ expect(mask).toBeTruthy()
+ expect(mask.parentNode).toEqual(document.body)
+ loadingInstance.close()
+ })
+
+ it('fullscreen', () => {
+ loadingInstance = LoadingService({
+ fullscreen: true
+ })
+ const mask = document.querySelector('.el-loading-mask')
+ expect(mask.parentNode).toEqual(document.body)
+ expect(mask.classList.contains('is-fullscreen')).toBe(true)
+ })
+
+ it('fullscreen singleton', (done) => {
+ loadingInstance = LoadingService({ fullScreen: true })
+ setTimeout(() => {
+ loadingInstance2 = LoadingService({ fullScreen: true })
+
+ setTimeout(() => {
+ let masks = document.querySelectorAll('.el-loading-mask')
+ expect(masks.length).toEqual(1)
+ loadingInstance2.close()
+
+ setTimeout(() => {
+ masks = document.querySelectorAll('.el-loading-mask')
+ expect(masks.length).toEqual(0)
+ done()
+ }, 350)
+ }, 50)
+ }, 50)
+ })
+
+ it('lock', () => {
+ loadingInstance = LoadingService({
+ lock: true
+ })
+ expect(
+ document.body.classList.contains('el-loading-parent--hidden')
+ ).toBe(true)
+ })
+
+ it('text', () => {
+ loadingInstance = LoadingService({
+ text: 'Loading...'
+ })
+ const text = document.querySelector('.el-loading-text')
+ expect(text).toBeTruthy()
+ expect(text.textContent).toEqual('Loading...')
+ })
+
+ it('customClass', () => {
+ loadingInstance = LoadingService({
+ customClass: 'el-loading-custom-class'
+ })
+ const customClass = document.querySelector('.el-loading-custom-class')
+ expect(customClass).toBeTruthy()
+ })
+ })
+})
diff --git a/packages/loading/src/directive.js b/packages/loading/directive.js
similarity index 100%
rename from packages/loading/src/directive.js
rename to packages/loading/directive.js
diff --git a/packages/loading/index.js b/packages/loading/index.js
index 8aef49abf..120d9291d 100644
--- a/packages/loading/index.js
+++ b/packages/loading/index.js
@@ -1,11 +1,11 @@
-import directive from './src/directive'
-import service from './src/index'
+// import directive from './src/directive'
+import service from './service'
export default {
install(app) {
- app.use(directive)
+ // app.use(directive)
app.config.globalProperties.$loading = service
},
- directive,
+ // directive,
service
}
diff --git a/packages/loading/src/index.js b/packages/loading/service.js
similarity index 58%
rename from packages/loading/src/index.js
rename to packages/loading/service.js
index 593dfc139..5ae7f825d 100644
--- a/packages/loading/src/index.js
+++ b/packages/loading/service.js
@@ -1,73 +1,20 @@
-import { nextTick } from 'vue'
-import loadingVue from './loading.vue'
+import loadingVue from './Loading.vue'
+import { createComponent, unmountComponent } from 'element-ui/src/use/component'
import { addClass, removeClass, getStyle } from 'element-ui/src/utils/dom'
import { PopupManager } from 'element-ui/src/utils/popup'
-import afterLeave from 'element-ui/src/utils/after-leave'
import merge from 'element-ui/src/utils/merge'
-const LoadingConstructor = { extends: loadingVue }
-
const defaults = {
- text: null,
- fullscreen: true,
+ target: null,
body: false,
+ fullscreen: true,
lock: false,
+ text: null,
+ spinner: null,
+ background: null,
customClass: ''
}
-
let fullscreenLoading
-
-LoadingConstructor.prototype.originalPosition = ''
-LoadingConstructor.prototype.originalOverflow = ''
-
-LoadingConstructor.prototype.close = function () {
- if (this.fullscreen) {
- fullscreenLoading = undefined
- }
- afterLeave(
- this,
- (_) => {
- const target = this.fullscreen || this.body ? document.body : this.target
- removeClass(target, 'el-loading-parent--relative')
- removeClass(target, 'el-loading-parent--hidden')
- if (this.$el && this.$el.parentNode) {
- this.$el.parentNode.removeChild(this.$el)
- }
- this.$destroy()
- },
- 300
- )
- this.visible = false
-}
-
-const addStyle = (options, parent, instance) => {
- const maskStyle = {}
- if (options.fullscreen) {
- instance.originalPosition = getStyle(document.body, 'position')
- instance.originalOverflow = getStyle(document.body, 'overflow')
- maskStyle.zIndex = PopupManager.nextZIndex()
- } else if (options.body) {
- instance.originalPosition = getStyle(document.body, 'position')
- ;['top', 'left'].forEach((property) => {
- const scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'
- maskStyle[property] =
- options.target.getBoundingClientRect()[property] +
- document.body[scroll] +
- document.documentElement[scroll] +
- 'px'
- })
- ;['height', 'width'].forEach((property) => {
- maskStyle[property] =
- options.target.getBoundingClientRect()[property] + 'px'
- })
- } else {
- instance.originalPosition = getStyle(parent, 'position')
- }
- Object.keys(maskStyle).forEach((property) => {
- instance.$el.style[property] = maskStyle[property]
- })
-}
-
const Loading = (options = {}) => {
// if (Vue.prototype.$isServer) return
options = merge({}, defaults, options)
@@ -85,29 +32,71 @@ const Loading = (options = {}) => {
}
const parent = options.body ? document.body : options.target
- const instance = new LoadingConstructor({
- el: document.createElement('div'),
- data: options
+ const instance = createComponent(loadingVue, {
+ ...options,
+ visible: true,
+ onAfterLeave() {
+ if (options.fullscreen) {
+ fullscreenLoading = undefined
+ }
+ const target =
+ options.fullscreen || options.body ? document.body : options.target
+ removeClass(target, 'el-loading-parent--relative')
+ removeClass(target, 'el-loading-parent--hidden')
+ unmountComponent(instance)
+ }
})
-
- addStyle(options, parent, instance)
+ addStyle(options, parent, instance.ctx)
if (
instance.originalPosition !== 'absolute' &&
instance.originalPosition !== 'fixed'
) {
addClass(parent, 'el-loading-parent--relative')
}
+
if (options.fullscreen && options.lock) {
addClass(parent, 'el-loading-parent--hidden')
}
- parent.appendChild(instance.$el)
- nextTick(() => {
- instance.visible = true
- })
+
+ parent.appendChild(instance.ctx.$el)
+
if (options.fullscreen) {
fullscreenLoading = instance
}
+
+ instance.close = close
+
return instance
}
+const close = function () {
+ this.ctx.close()
+}
+
+const addStyle = (options, parent, ctx) => {
+ const maskStyle = {}
+ if (options.fullscreen) {
+ ctx.originalPosition = getStyle(document.body, 'position')
+ ctx.originalOverflow = getStyle(document.body, 'overflow')
+ maskStyle.zIndex = PopupManager.nextZIndex()
+ } else if (options.body) {
+ ctx.originalPosition = getStyle(document.body, 'position')
+ ;['top', 'left'].forEach((property) => {
+ const scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'
+ maskStyle[property] =
+ options.target.getBoundingClientRect()[property] + // document.body[scroll] +
+ document.documentElement[scroll] +
+ 'px'
+ })
+ ;['height', 'width'].forEach((property) => {
+ maskStyle[property] =
+ options.target.getBoundingClientRect()[property] + 'px'
+ })
+ } else {
+ ctx.originalPosition = getStyle(parent, 'position')
+ }
+ Object.keys(maskStyle).forEach((property) => {
+ ctx.$el.style[property] = maskStyle[property]
+ })
+}
export default Loading
diff --git a/packages/loading/src/loading.vue b/packages/loading/src/loading.vue
deleted file mode 100644
index d92f91f9f..000000000
--- a/packages/loading/src/loading.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/notification/__tests__/Notification.spec.js b/packages/notification/__tests__/Notification.spec.js
new file mode 100644
index 000000000..22a80823f
--- /dev/null
+++ b/packages/notification/__tests__/Notification.spec.js
@@ -0,0 +1,103 @@
+import { mount } from '@vue/test-utils'
+import Notification from '../Notification.vue'
+
+describe('Notification', () => {
+ afterEach(() => {
+ const el = document.querySelector('.el-notification')
+ if (!el) return
+ if (el.parentNode) {
+ el.parentNode.removeChild(el)
+ }
+ if (el.__vue__) {
+ el.__vue__.$destroy()
+ }
+ })
+
+ test('automatically close', async (done) => {
+ const notification = await mount(Notification, {
+ props: {
+ message: '玻璃蜡烛',
+ duration: 500
+ }
+ })
+ expect(notification.find('.el-notification').exists()).toBe(true)
+ setTimeout(() => {
+ // 此处2.x的用例测试是否存在,看实现close效果为display:none
+ // expect(notification.find('.el-notification').exists()).toBe(false)
+ expect(notification.find('.el-notification').element.style.display).toBe('none')
+ done()
+ }, 1000)
+ })
+
+ test('manually close', async (done) => {
+ const notification = await mount(Notification, {
+ props: {
+ message: '苍白母马'
+ }
+ })
+ notification.find('.el-notification__closeBtn').trigger('click')
+ setTimeout(() => {
+ expect(notification.find('.el-notification').element.style.display).toBe('none')
+ done()
+ }, 500)
+ })
+
+ test('create', () => {
+ const notification = mount(Notification, {
+ props: {
+ title: '狮子',
+ message: '狮鹫',
+ duration: 0
+ }
+ })
+ const group = notification.find('.el-notification__group')
+ const title = group.find('.el-notification__title')
+ const message = group.find('.el-notification__content p')
+ expect(notification.find('.el-notification').exists()).toBe(true)
+ expect(title.text()).toBe('狮子')
+ expect(message.text()).toBe('狮鹫')
+ })
+
+ test('html string as message', () => {
+ const notification = mount(Notification, {
+ props: {
+ title: '狮子',
+ message: '
狮鹫',
+ dangerouslyUseHTMLString: true,
+ duration: 0
+ }
+ })
+ const message = notification.find('.el-notification__content strong')
+ expect(message.text()).toBe('狮鹫')
+ })
+
+ // 怀疑2.x版本以下用例的功能未实现
+ // create by vnode
+ // alias by vnode
+ // invoke with type
+
+ test('reset timer', async (done) => {
+ const notification = await mount(Notification, {
+ props: {
+ message: '芳香总管',
+ duration: 1000
+ }
+ })
+ notification.find('.el-notification').trigger('mouseenter')
+ setTimeout(() => {
+ notification.find('.el-notification').trigger('mouseleave')
+ expect(notification.find('.el-notification').exists()).toBe(true)
+ done()
+ }, 700)
+ })
+
+ test('no close button', async () => {
+ const notification = await mount(Notification, {
+ props: {
+ message: 'Hello',
+ showClose: false
+ }
+ })
+ expect(notification.find('.el-notification__closeBtn').exists()).toBe(false)
+ })
+})
diff --git a/packages/pagination/Pagination.js b/packages/pagination/Pagination.js
new file mode 100644
index 000000000..f2f550b64
--- /dev/null
+++ b/packages/pagination/Pagination.js
@@ -0,0 +1,293 @@
+import { ref, computed, watch, h, toRefs, Fragment } from 'vue'
+import Pager from './components/Pager'
+import Prev from './components/Prev.vue'
+import Next from './components/Next'
+import Jumper from './components/Jumper'
+import Total from './components/Total'
+
+const useLayout = (layout) => {
+ const template = []
+ const rightWrapper = []
+ const components = layout.value.split(',').map((item) => item.trim())
+ const findIndex = components.findIndex((item) => item === '->')
+ for (let i = 0; i < components.length; i++) {
+ if (findIndex >= 0) {
+ if (i === findIndex) continue
+ if (i < findIndex) {
+ template.push(components[i])
+ } else {
+ rightWrapper.push(components[i])
+ }
+ } else {
+ template.push(components[i])
+ }
+ }
+
+ return {
+ template,
+ rightWrapper
+ }
+}
+
+const getValidCurrentPage = (value, pageCount) => {
+ value = parseInt(value, 10)
+
+ const havePageCount = typeof pageCount === 'number'
+
+ let resetValue
+ if (!havePageCount) {
+ if (isNaN(value) || value < 1) resetValue = 1
+ } else {
+ if (value < 1) {
+ resetValue = 1
+ } else if (value > pageCount) {
+ resetValue = pageCount
+ }
+ }
+
+ if ((resetValue === undefined && isNaN(value)) || resetValue === 0) {
+ resetValue = 1
+ }
+
+ return resetValue ?? value
+}
+
+const useInternalCurrentPage = ({ currentPage, emit, emitted }) => {
+ const innerCurrentPage = ref(null)
+ return computed({
+ get() {
+ return innerCurrentPage.value ?? currentPage?.value ?? 1
+ },
+ set(v) {
+ emit('update:currentPage', v)
+ emit('currentChange', v)
+ if (currentPage) {
+ watch(currentPage, () => {
+ emitted.value = true
+ })
+ }
+ if (emitted.value) innerCurrentPage.value = null
+ else innerCurrentPage.value = v
+ emitted.value = false
+ }
+ })
+}
+
+const userInternalPageSize = ({ pageSize, emit, emitted }) => {
+ const innerPageSize = ref(null)
+ return computed({
+ get() {
+ return innerPageSize.value ?? pageSize?.value
+ },
+ set(v) {
+ emit('update:pageSize', v)
+ emit('sizeChange', v)
+ innerPageSize.value = v
+ if (pageSize) {
+ watch(pageSize, () => {
+ emitted.value = true
+ })
+ }
+ if (emitted.value) innerPageSize.value = null
+ emitted.value = false
+ }
+ })
+}
+
+const useInternalPageCount = ({ pageCount, total, pageSize }) => {
+ const internalPageCount = computed(() => {
+ if (!total && !pageCount) return 5
+ if (typeof total?.value === 'number') {
+ return Math.max(1, Math.ceil(total.value / pageSize.value))
+ } else if (typeof pageCount?.value === 'number') {
+ return Math.max(1, pageCount.value)
+ }
+ })
+
+ return {
+ internalPageCount
+ }
+}
+
+export default {
+ name: 'ElPagination',
+
+ props: {
+ pageSize: {
+ type: Number,
+ default: 10
+ },
+
+ small: Boolean,
+
+ total: Number,
+
+ pageCount: Number,
+
+ pagerCount: {
+ type: Number,
+ validator(value) {
+ return (
+ (value || 0) === value && value > 4 && value < 22 && value % 2 === 1
+ )
+ },
+ default: 7
+ },
+
+ currentPage: Number,
+
+ layout: {
+ default: 'prev, pager, next, jumper, ->, total'
+ },
+
+ pageSizes: {
+ type: Array,
+ default() {
+ return [10, 20, 30, 40, 50, 100]
+ }
+ },
+
+ popperClass: String,
+
+ prevText: String,
+
+ nextText: String,
+
+ background: Boolean,
+
+ disabled: Boolean,
+
+ hideOnSinglePage: Boolean
+ },
+
+ emits: [
+ 'update:currentPage',
+ 'update:pageSize',
+ 'currentChange',
+ 'sizeChange',
+ 'prevClick',
+ 'nextClick'
+ ],
+
+ setup(props, { emit, slots }) {
+ const currentPageEmitted = ref(false)
+ const pageSizeEmitted = ref(false)
+ const {
+ layout,
+ small,
+ background,
+ currentPage,
+ total,
+ pageCount,
+ pageSize,
+ disabled,
+ hideOnSinglePage
+ } = toRefs(props)
+
+ if (!layout.value) return null
+
+ const internalCurrentPage = useInternalCurrentPage({
+ currentPage,
+ emit,
+ emitted: currentPageEmitted
+ })
+ const internalPageSize = userInternalPageSize({
+ pageSize,
+ emit,
+ emitted: pageSizeEmitted
+ })
+ const { internalPageCount } = useInternalPageCount({
+ pageCount,
+ total,
+ pageSize: internalPageSize
+ })
+
+ if (hideOnSinglePage && internalPageCount.value === 1) return null
+
+ const { template, rightWrapper } = useLayout(layout)
+
+ return () => {
+ const templateComponent = {
+ prev: h(Prev, {
+ currentPage: internalCurrentPage.value,
+ disabled: disabled.value,
+ prevText: props.prevText,
+ prev() {
+ if (disabled.value) return
+ internalCurrentPage.value = getValidCurrentPage(
+ internalCurrentPage.value - 1,
+ internalPageCount.value
+ )
+ emit('prevClick', internalCurrentPage.value)
+ }
+ }),
+ jumper: h(Jumper, {
+ currentPage: internalCurrentPage.value,
+ pageCount: internalPageCount.value,
+ handleChange(val) {
+ internalCurrentPage.value = getValidCurrentPage(
+ val,
+ internalPageCount.value
+ )
+ },
+ disabled: disabled.value
+ }),
+ pager: h(Pager, {
+ currentPage: internalCurrentPage.value,
+ 'onUpdate:currentPage'(val) {
+ internalCurrentPage.value = val
+ },
+ pageCount: internalPageCount.value,
+ disabled: disabled.value
+ }),
+ next: h(Next, {
+ currentPage: internalCurrentPage.value,
+ pageCount: internalPageCount.value,
+ disabled: disabled.value,
+ nextText: props.nextText,
+ next() {
+ if (disabled.value) return
+ internalCurrentPage.value = getValidCurrentPage(
+ internalCurrentPage.value + 1,
+ internalPageCount.value
+ )
+ emit('nextClick', internalCurrentPage.value)
+ }
+ }),
+ // sizes:
,
+ slot: h(Fragment, slots.default?.() ?? ''),
+ total: h(Total, {
+ total: total?.value
+ })
+ }
+
+ return (
+
+ {rightWrapper.length ? (
+
+ {rightWrapper.map((item) => {
+ const Comp = templateComponent[item]
+ return
+ })}
+
+ ) : (
+ ''
+ )}
+
+ {template.map((item) => {
+ const Comp = templateComponent[item]
+ return
+ })}
+
+ )
+ }
+ }
+}
diff --git a/packages/pagination/__tests__/Pagination.spec.js b/packages/pagination/__tests__/Pagination.spec.js
new file mode 100644
index 000000000..f782dbedb
--- /dev/null
+++ b/packages/pagination/__tests__/Pagination.spec.js
@@ -0,0 +1,394 @@
+import { mount } from '@vue/test-utils'
+import { ref, nextTick, h } from 'vue'
+import Pager from '../components/Pager'
+import Prev from '../components/Prev'
+import Next from '../components/Next'
+import Total from '../components/Total'
+import Pagination from '../Pagination'
+import Jumper from '../components/Jumper'
+import Input from 'element-ui/packages/input'
+
+describe('components', () => {
+ describe('Pager', () => {
+ it('normal render', () => {
+ const wrapper = mount(Pager)
+ expect(wrapper.find('ul').exists()).toBeTruthy()
+ expect(wrapper.findAll('li.number').length).toBe(5)
+ })
+ it('show next more', async () => {
+ const wrapper = mount(Pager, {
+ props: {
+ pageCount: 100
+ }
+ })
+
+ expect(wrapper.find('.btn-quickprev').exists()).toBeFalsy()
+ expect(wrapper.find('.btn-quicknext').exists()).toBeTruthy()
+ await wrapper.find('.btn-quicknext').trigger('click')
+ expect(wrapper.emitted()['update:currentPage'][0]).toEqual([6])
+ })
+ it('show prev more', async () => {
+ const wrapper = mount(Pager, {
+ props: {
+ currentPage: 97,
+ pageCount: 100
+ }
+ })
+
+ await nextTick()
+ expect(wrapper.find('.btn-quickprev').exists()).toBeTruthy()
+ expect(wrapper.find('.btn-quicknext').exists()).toBeFalsy()
+ await wrapper.find('.btn-quickprev').trigger('click')
+ expect(wrapper.emitted()['update:currentPage'][0]).toEqual([92])
+ })
+ it('show both more', async () => {
+ const wrapper = mount(Pager, {
+ props: {
+ currentPage: 50,
+ pageCount: 100
+ }
+ })
+
+ await nextTick()
+ expect(wrapper.find('.btn-quickprev').exists()).toBeTruthy()
+ expect(wrapper.find('.btn-quicknext').exists()).toBeTruthy()
+ })
+ it('click event', async () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pager, {
+ props: {
+ currentPage,
+ pageCount: 100,
+ 'onUpdate:currentPage'(val) {
+ currentPage.value = val
+ }
+ }
+ })
+
+ await nextTick()
+ expect(currentPage.value).toBe(1)
+ expect(wrapper.find('.btn-quickprev').exists()).toBeFalsy()
+ expect(wrapper.find('.btn-quicknext').exists()).toBeTruthy()
+ await wrapper.findAll('.number')[6].trigger('click')
+ expect(wrapper.find('.btn-quickprev').exists()).toBeTruthy()
+ expect(wrapper.find('.btn-quicknext').exists()).toBeFalsy()
+ expect(currentPage.value).toBe(100)
+ })
+ })
+
+ describe('Prev', () => {
+ it('normal render', () => {
+ const wrapper = mount(Prev)
+
+ expect(wrapper.find('.btn-prev').exists()).toBeTruthy()
+ })
+ it('prevText', () => {
+ const wrapper = mount(Prev, {
+ props: {
+ prevText: '1'
+ }
+ })
+
+ expect(wrapper.text()).toBe('1')
+ })
+ it('click event', async () => {
+ const prev = jest.fn()
+ const wrapper = mount(Prev, {
+ props: {
+ currentPage: 10,
+ prev
+ }
+ })
+
+ await wrapper.trigger('click')
+ expect(prev).toBeCalled()
+ })
+ it('disabled', async () => {
+ // disabled has two situations:
+ // first: disabled set true
+ const prev = jest.fn()
+ const wrapper = mount(Prev, {
+ props: {
+ disabled: true,
+ currentPage: 10,
+ prev
+ }
+ })
+ // second: currentPage equal 1
+ const wrapper2 = mount(Prev, {
+ props: {
+ prev
+ }
+ })
+
+ await wrapper.trigger('click')
+ expect(prev).not.toBeCalled()
+ await wrapper2.trigger('click')
+ expect(prev).not.toBeCalled()
+ })
+ })
+
+ describe('Next', () => {
+ it('normal render', () => {
+ const wrapper = mount(Next)
+
+ expect(wrapper.find('.btn-next').exists()).toBeTruthy()
+ })
+ it('nextText', () => {
+ const wrapper = mount(Next, {
+ props: {
+ nextText: '1'
+ }
+ })
+
+ expect(wrapper.text()).toBe('1')
+ })
+ it('click event', async () => {
+ const next = jest.fn()
+ const wrapper = mount(Next, {
+ props: {
+ currentPage: 10,
+ next
+ }
+ })
+
+ await wrapper.trigger('click')
+ expect(next).toBeCalled()
+ })
+ it('disabled', async () => {
+ // disabled has three situations:
+ // first: disabled set true
+ const next = jest.fn()
+ const wrapper = mount(Next, {
+ props: {
+ disabled: true,
+ currentPage: 10,
+ next
+ }
+ })
+ // second: currentPage equal pageCount
+ const wrapper2 = mount(Next, {
+ props: {
+ currentPage: 3,
+ pageCount: 3,
+ next
+ }
+ })
+ // third: pageCount equal 0 (why not <= 1? because second situation include this)
+ const wrapper3 = mount(Next, {
+ props: {
+ pageCount: 0,
+ next
+ }
+ })
+
+ await wrapper.trigger('click')
+ expect(next).not.toBeCalled()
+ await wrapper2.trigger('click')
+ expect(next).not.toBeCalled()
+ await wrapper3.trigger('click')
+ expect(next).not.toBeCalled()
+ })
+ })
+
+ describe('Jumper', () => {
+ it('handleChange', async () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Jumper, {
+ props: {
+ currentPage,
+ handleChange(val) {
+ currentPage.value = Number(val)
+ }
+ },
+ global: {
+ components: {
+ Input
+ }
+ }
+ })
+
+ await wrapper.find('input').setValue(3)
+ await wrapper.trigger('keydown', 13)
+ expect(currentPage.value).toBe(3)
+ })
+ })
+
+ describe('Total', () => {
+ it('total prop is number', () => {
+ const wrapper = mount(Total, {
+ props: {
+ total: 5
+ }
+ })
+
+ expect(wrapper.find('.el-pagination__total').exists()).toBeTruthy()
+ })
+ it('total prop is not number', () => {
+ const wrapper = mount(Total, {
+ props: {
+ total: undefined
+ }
+ })
+
+ expect(wrapper.find('.el-pagination__total').exists()).toBeFalsy()
+ })
+ })
+
+ describe('Size', () => {
+ it.todo('normal render')
+ it.todo('popperClass')
+ })
+})
+
+describe('Pagination', () => {
+ describe('props', () => {
+ it('layout', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, slot, next'
+ },
+ slots: {
+ default: h('div', { class: 'slot' }, 'slot')
+ }
+ })
+
+ expect(wrapper.find('.btn-prev').exists()).toBeTruthy()
+ expect(wrapper.find('.slot').exists()).toBeTruthy()
+ expect(wrapper.find('.btn-next').exists()).toBeTruthy()
+ })
+ it('no currentPage', async () => {
+ const wrapper = mount(Pagination)
+
+ expect(wrapper.find('.active').text()).toBe('1')
+ await wrapper.findAll('.number')[1].trigger('click')
+ expect(wrapper.find('.active').text()).toBe('2')
+ })
+ it('total', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ total: 30
+ }
+ })
+
+ expect(wrapper.findAll('.number').length).toBe(3)
+ expect(wrapper.find('.el-pagination__total').text()).toContain('30')
+ })
+ it('pageSize', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ total: 10,
+ pageSize: 2
+ }
+ })
+
+ expect(wrapper.findAll('.number').length).toBe(5)
+ })
+ it('currentChange', async () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ currentPage,
+ onCurrentChange(val) {
+ currentPage.value = val
+ }
+ }
+ })
+
+ await wrapper.findAll('.number')[3].trigger('click')
+ expect(currentPage.value).toBe(4)
+ })
+ it('hide-on-single-page', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ hideOnSinglePage: true,
+ pageCount: 1
+ }
+ })
+ const wrapper2 = mount(Pagination, {
+ props: {
+ hideOnSinglePage: true,
+ pageCount: 5
+ }
+ })
+
+ expect(wrapper.find('.el-pagination').exists()).toBeFalsy()
+ expect(wrapper2.find('.el-pagination').exists()).toBeTruthy()
+ })
+ it.todo('sizeChange')
+ it.todo('pageSizes')
+ })
+
+ describe('ability', () => {
+ it('change current-page by Jumper', async () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager, jumper',
+ currentPage,
+ 'onUpdate:currentPage'(val) {
+ currentPage.value = val
+ }
+ }
+ })
+ await wrapper.find('input').setValue(3)
+ await wrapper.trigger('keydown', 13)
+ expect(currentPage.value).toBe(3)
+ await wrapper.find('input').setValue(10)
+ await wrapper.trigger('keydown', 13)
+ expect(currentPage.value).toBe(5)
+ })
+ it('change current-page by Prev', async () => {
+ const onPrevClick = jest.fn()
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, pager',
+ currentPage: 2,
+ onPrevClick
+ }
+ })
+
+ await wrapper.findComponent(Prev).trigger('click')
+ expect(onPrevClick).toBeCalled()
+ })
+ it('cannot change current-page by Prev', async () => {
+ const onPrevClick = jest.fn()
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, pager',
+ currentPage: 1,
+ onPrevClick
+ }
+ })
+
+ await wrapper.findComponent(Prev).trigger('click')
+ expect(onPrevClick).not.toBeCalled()
+ })
+ it('change current-page by Next', async () => {
+ const onNextClick = jest.fn()
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager, next',
+ currentPage: 4,
+ onNextClick
+ }
+ })
+
+ await wrapper.findComponent(Next).trigger('click')
+ expect(onNextClick).toBeCalled()
+ })
+ it('cannot change current-page by Next', async () => {
+ const onNextClick = jest.fn()
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager, next',
+ currentPage: 5,
+ onNextClick
+ }
+ })
+
+ await wrapper.findComponent(Next).trigger('click')
+ expect(onNextClick).not.toBeCalled()
+ })
+ })
+})
diff --git a/packages/pagination/components/Jumper.vue b/packages/pagination/components/Jumper.vue
new file mode 100644
index 000000000..87f2a643b
--- /dev/null
+++ b/packages/pagination/components/Jumper.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
diff --git a/packages/pagination/components/Next.vue b/packages/pagination/components/Next.vue
new file mode 100644
index 000000000..f28b67066
--- /dev/null
+++ b/packages/pagination/components/Next.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
diff --git a/packages/pagination/components/Pager.vue b/packages/pagination/components/Pager.vue
new file mode 100644
index 000000000..c5adda05f
--- /dev/null
+++ b/packages/pagination/components/Pager.vue
@@ -0,0 +1,205 @@
+
+
+
+
+
diff --git a/packages/pagination/components/Prev.vue b/packages/pagination/components/Prev.vue
new file mode 100644
index 000000000..1a3cb2ee6
--- /dev/null
+++ b/packages/pagination/components/Prev.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
diff --git a/packages/pagination/components/Total.js b/packages/pagination/components/Total.js
new file mode 100644
index 000000000..05fd0add0
--- /dev/null
+++ b/packages/pagination/components/Total.js
@@ -0,0 +1,13 @@
+import { useLocale } from 'element-ui/src/use/locale'
+import { h } from 'vue'
+
+const Total = (props) => {
+ return typeof props.total === 'number'
+ ? h(
+ 'span',
+ { class: 'el-pagination__total' },
+ useLocale()('el.pagination.total', { total: props.total })
+ )
+ : ''
+}
+export default Total
diff --git a/packages/pagination/index.js b/packages/pagination/index.js
index 7f71d90f3..01451bc8c 100644
--- a/packages/pagination/index.js
+++ b/packages/pagination/index.js
@@ -1,4 +1,4 @@
-import Pagination from './src/pagination'
+import Pagination from './Pagination'
/* istanbul ignore next */
Pagination.install = function (app) {
diff --git a/packages/pagination/src/pager.vue b/packages/pagination/src/pager.vue
deleted file mode 100644
index 7b9e16bfe..000000000
--- a/packages/pagination/src/pager.vue
+++ /dev/null
@@ -1,174 +0,0 @@
-
-
-
-
-
diff --git a/packages/pagination/src/pagination.js b/packages/pagination/src/pagination.js
deleted file mode 100644
index 55e94901a..000000000
--- a/packages/pagination/src/pagination.js
+++ /dev/null
@@ -1,428 +0,0 @@
-import Pager from './pager.vue'
-import ElSelect from 'element-ui/packages/select'
-import ElOption from 'element-ui/packages/option'
-import ElInput from 'element-ui/packages/input'
-import Locale from 'element-ui/src/mixins/locale'
-import { valueEquals } from 'element-ui/src/utils/util'
-
-export default {
- name: 'ElPagination',
-
- props: {
- pageSize: {
- type: Number,
- default: 10
- },
-
- small: Boolean,
-
- total: Number,
-
- pageCount: Number,
-
- pagerCount: {
- type: Number,
- validator(value) {
- return (
- (value | 0) === value && value > 4 && value < 22 && value % 2 === 1
- )
- },
- default: 7
- },
-
- currentPage: {
- type: Number,
- default: 1
- },
-
- layout: {
- default: 'prev, pager, next, jumper, ->, total'
- },
-
- pageSizes: {
- type: Array,
- default() {
- return [10, 20, 30, 40, 50, 100]
- }
- },
-
- popperClass: String,
-
- prevText: String,
-
- nextText: String,
-
- background: Boolean,
-
- disabled: Boolean,
-
- hideOnSinglePage: Boolean
- },
-
- data() {
- return {
- internalCurrentPage: 1,
- internalPageSize: 0,
- lastEmittedPage: -1,
- userChangePageSize: false
- }
- },
-
- render(h) {
- const layout = this.layout
- if (!layout) return null
- if (
- this.hideOnSinglePage &&
- (!this.internalPageCount || this.internalPageCount === 1)
- )
- return null
-
- const template = (
-
- )
- const TEMPLATE_MAP = {
- prev:
,
- jumper:
,
- pager: (
-
- ),
- next:
,
- sizes:
,
- slot:
{this.$slots.default ? this.$slots.default : ''},
- total:
- }
- const components = layout.split(',').map((item) => item.trim())
- const rightWrapper =
- let haveRightWrapper = false
-
- template.children = template.children || []
- rightWrapper.children = rightWrapper.children || []
- components.forEach((compo) => {
- if (compo === '->') {
- haveRightWrapper = true
- return
- }
-
- if (!haveRightWrapper) {
- template.children.push(TEMPLATE_MAP[compo])
- } else {
- rightWrapper.children.push(TEMPLATE_MAP[compo])
- }
- })
-
- if (haveRightWrapper) {
- template.children.unshift(rightWrapper)
- }
-
- return template
- },
-
- components: {
- Prev: {
- render(h) {
- return (
-
- )
- }
- },
-
- Next: {
- render(h) {
- return (
-
- )
- }
- },
-
- Sizes: {
- mixins: [Locale],
-
- props: {
- pageSizes: Array
- },
-
- watch: {
- pageSizes: {
- immediate: true,
- handler(newVal, oldVal) {
- if (valueEquals(newVal, oldVal)) return
- if (Array.isArray(newVal)) {
- this.$parent.internalPageSize =
- newVal.indexOf(this.$parent.pageSize) > -1
- ? this.$parent.pageSize
- : this.pageSizes[0]
- }
- }
- }
- },
-
- render(h) {
- return (
-
- )
- },
-
- components: {
- ElSelect,
- ElOption
- },
-
- methods: {
- handleChange(val) {
- if (val !== this.$parent.internalPageSize) {
- this.$parent.internalPageSize = val = parseInt(val, 10)
- this.$parent.userChangePageSize = true
- this.$parent.$emit('update:pageSize', val)
- this.$parent.$emit('size-change', val)
- }
- }
- }
- },
-
- Jumper: {
- mixins: [Locale],
-
- components: { ElInput },
-
- data() {
- return {
- userInput: null
- }
- },
-
- watch: {
- '$parent.internalCurrentPage'() {
- this.userInput = null
- }
- },
-
- methods: {
- handleKeyup({ keyCode, target }) {
- // Chrome, Safari, Firefox triggers change event on Enter
- // Hack for IE: https://github.com/ElemeFE/element/issues/11710
- // Drop this method when we no longer supports IE
- if (keyCode === 13) {
- this.handleChange(target.value)
- }
- },
- handleInput(value) {
- this.userInput = value
- },
- handleChange(value) {
- this.$parent.internalCurrentPage = this.$parent.getValidCurrentPage(
- value
- )
- this.$parent.emitChange()
- this.userInput = null
- }
- },
-
- render(h) {
- return (
-
- )
- }
- },
-
- Total: {
- mixins: [Locale],
-
- render(h) {
- return typeof this.$parent.total === 'number' ? (
-
- ) : (
- ''
- )
- }
- },
-
- Pager
- },
-
- methods: {
- handleCurrentChange(val) {
- this.internalCurrentPage = this.getValidCurrentPage(val)
- this.userChangePageSize = true
- this.emitChange()
- },
-
- prev() {
- if (this.disabled) return
- const newVal = this.internalCurrentPage - 1
- this.internalCurrentPage = this.getValidCurrentPage(newVal)
- this.$emit('prev-click', this.internalCurrentPage)
- this.emitChange()
- },
-
- next() {
- if (this.disabled) return
- const newVal = this.internalCurrentPage + 1
- this.internalCurrentPage = this.getValidCurrentPage(newVal)
- this.$emit('next-click', this.internalCurrentPage)
- this.emitChange()
- },
-
- getValidCurrentPage(value) {
- value = parseInt(value, 10)
-
- const havePageCount = typeof this.internalPageCount === 'number'
-
- let resetValue
- if (!havePageCount) {
- if (isNaN(value) || value < 1) resetValue = 1
- } else {
- if (value < 1) {
- resetValue = 1
- } else if (value > this.internalPageCount) {
- resetValue = this.internalPageCount
- }
- }
-
- if (resetValue === undefined && isNaN(value)) {
- resetValue = 1
- } else if (resetValue === 0) {
- resetValue = 1
- }
-
- return resetValue === undefined ? value : resetValue
- },
-
- emitChange() {
- this.$nextTick(() => {
- if (
- this.internalCurrentPage !== this.lastEmittedPage ||
- this.userChangePageSize
- ) {
- this.$emit('current-change', this.internalCurrentPage)
- this.lastEmittedPage = this.internalCurrentPage
- this.userChangePageSize = false
- }
- })
- }
- },
-
- computed: {
- internalPageCount() {
- if (typeof this.total === 'number') {
- return Math.max(1, Math.ceil(this.total / this.internalPageSize))
- } else if (typeof this.pageCount === 'number') {
- return Math.max(1, this.pageCount)
- }
- return null
- }
- },
-
- watch: {
- currentPage: {
- immediate: true,
- handler(val) {
- this.internalCurrentPage = this.getValidCurrentPage(val)
- }
- },
-
- pageSize: {
- immediate: true,
- handler(val) {
- this.internalPageSize = isNaN(val) ? 10 : val
- }
- },
-
- internalCurrentPage: {
- immediate: true,
- handler(newVal) {
- this.$emit('update:currentPage', newVal)
- this.lastEmittedPage = -1
- }
- },
-
- internalPageCount(newVal) {
- /* istanbul ignore if */
- const oldPage = this.internalCurrentPage
- if (newVal > 0 && oldPage === 0) {
- this.internalCurrentPage = 1
- } else if (oldPage > newVal) {
- this.internalCurrentPage = newVal === 0 ? 1 : newVal
- this.userChangePageSize && this.emitChange()
- }
- this.userChangePageSize = false
- }
- }
-}
diff --git a/packages/popconfirm/src/main.vue b/packages/popconfirm/Popconfirm.vue
similarity index 79%
rename from packages/popconfirm/src/main.vue
rename to packages/popconfirm/Popconfirm.vue
index 8708b0bf3..748e237c4 100644
--- a/packages/popconfirm/src/main.vue
+++ b/packages/popconfirm/Popconfirm.vue
@@ -19,11 +19,14 @@
-