2
0
mirror of https://github.com/tenrok/vue-form-wizard.git synced 2026-06-19 16:00:32 +03:00
This commit is contained in:
cristij
2018-01-16 19:25:25 +02:00
parent 7c31d65125
commit e3a85e4e16
4 changed files with 114 additions and 88 deletions
+6 -1
View File
@@ -8,7 +8,12 @@
@on-change="handleChange" @on-change="handleChange"
:start-index.sync="activeIndex" :start-index.sync="activeIndex"
color="#e74c3c"> color="#e74c3c">
<tab-content v-for="tab in tabs" :title="tab" :key="tab">{{tab}}</tab-content> <tab-content v-for="(tab, index) in tabs"
:beforeChange="validateAsync"
:title="tab"
:key="tab">
{{tab}}
</tab-content>
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
<router-view></router-view> <router-view></router-view>
</transition> </transition>
+56 -47
View File
@@ -75,7 +75,7 @@
<script> <script>
import WizardButton from './WizardButton.vue' import WizardButton from './WizardButton.vue'
import WizardStep from './WizardStep.vue' import WizardStep from './WizardStep.vue'
import {isPromise, findElementAndFocus, getFocusedTabIndex} from './helpers' import {findElementAndFocus, getFocusedTabIndex, isPromiseLike} from './helpers'
export default { export default {
name: 'form-wizard', name: 'form-wizard',
@@ -270,30 +270,34 @@
tab.checked = true tab.checked = true
}) })
}, },
navigateToTab (index) { async navigateToTab (index) {
let validate = index > this.activeTabIndex let validate = index > this.activeTabIndex
if (index <= this.maxStep) { if (index <= this.maxStep) {
let cb = () => { let navigateFunction = () => {
if (validate && index - this.activeTabIndex > 1) { if (validate && index - this.activeTabIndex > 1) {
// validate all steps recursively until destination index // validate all steps recursively until destination index
this.changeTab(this.activeTabIndex, this.activeTabIndex + 1) this.changeTab(this.activeTabIndex, this.activeTabIndex + 1)
this.beforeTabChange(this.activeTabIndex, cb) this.beforeTabChange(this.activeTabIndex)
} else { } else {
this.changeTab(this.activeTabIndex, index) this.changeTab(this.activeTabIndex, index)
this.afterTabChange(this.activeTabIndex) this.afterTabChange(this.activeTabIndex)
} }
} }
if (validate) { if (validate) {
this.beforeTabChange(this.activeTabIndex, cb) let validationResult = await this.beforeTabChange(this.activeTabIndex)
if (validationResult === true) {
navigateFunction()
}
} else { } else {
this.setValidationError(null) this.setValidationError(null)
cb() navigateFunction()
} }
} }
return index <= this.maxStep return index <= this.maxStep
}, },
nextTab () { async nextTab () {
let cb = () => { let validationResult = await this.beforeTabChange(this.activeTabIndex)
if (validationResult === true) {
if (this.activeTabIndex < this.tabCount - 1) { if (this.activeTabIndex < this.tabCount - 1) {
this.changeTab(this.activeTabIndex, this.activeTabIndex + 1) this.changeTab(this.activeTabIndex, this.activeTabIndex + 1)
this.afterTabChange(this.activeTabIndex) this.afterTabChange(this.activeTabIndex)
@@ -301,19 +305,21 @@
this.$emit('on-complete') this.$emit('on-complete')
} }
} }
this.beforeTabChange(this.activeTabIndex, cb)
}, },
prevTab () { async prevTab () {
let cb = () => { let navigateFunction = () => {
if (this.activeTabIndex > 0) { if (this.activeTabIndex > 0) {
this.setValidationError(null) this.setValidationError(null)
this.changeTab(this.activeTabIndex, this.activeTabIndex - 1) this.changeTab(this.activeTabIndex, this.activeTabIndex - 1)
} }
} }
if (this.validateOnBack) { if (this.validateOnBack) {
this.beforeTabChange(this.activeTabIndex, cb) let validationResult = await this.beforeTabChange(this.activeTabIndex)
if (validationResult === true) {
navigateFunction()
}
} else { } else {
cb() navigateFunction()
} }
}, },
focusNextTab () { focusNextTab () {
@@ -340,44 +346,47 @@
this.tabs[this.activeTabIndex].validationError = error this.tabs[this.activeTabIndex].validationError = error
this.$emit('on-error', error) this.$emit('on-error', error)
}, },
validateBeforeChange (promiseFn, callback) { async validateBeforeChange (promiseFn) {
this.setValidationError(null) this.setValidationError(null)
// we have a promise // we have a promise
if (isPromise(promiseFn)) { this.setLoading(true)
this.setLoading(true) return promiseFn.then(result => {
promiseFn.then((res) => { this.setLoading(false)
this.setLoading(false) let validationResult = result === true
let validationResult = res === true return this.executeBeforeChange(validationResult)
this.executeBeforeChange(validationResult, callback) }).catch(error => {
}).catch((error) => { this.setLoading(false)
this.setLoading(false) this.setValidationError(error)
this.setValidationError(error) return Promise.resolve(false)
}) })
// we have a simple function
} else {
let validationResult = promiseFn === true
this.executeBeforeChange(validationResult, callback)
}
}, },
executeBeforeChange (validationResult, callback) { executeBeforeChange (validationResult) {
this.$emit('on-validate', validationResult, this.activeTabIndex) return new Promise((resolve, reject) => {
if (validationResult) { this.$emit('on-validate', validationResult, this.activeTabIndex)
callback() if (!validationResult) {
} else { this.tabs[this.activeTabIndex].validationError = 'error'
this.tabs[this.activeTabIndex].validationError = 'error' }
} resolve(validationResult)
})
}, },
beforeTabChange (index, callback) { beforeTabChange (index) {
if (this.loading) { return new Promise((resolve, reject) => {
return if (this.loading) {
} resolve(false)
let oldTab = this.tabs[index] }
if (oldTab && oldTab.beforeChange !== undefined) { let oldTab = this.tabs[index]
let tabChangeRes = oldTab.beforeChange() if (oldTab && oldTab.beforeChange !== undefined) {
this.validateBeforeChange(tabChangeRes, callback) let beforeChangePromise = oldTab.beforeChange()
} else { if (!isPromiseLike(beforeChangePromise)) {
callback() beforeChangePromise = Promise.resolve(beforeChangePromise)
} }
this.validateBeforeChange(beforeChangePromise).then((res) => {
resolve(res)
})
} else {
resolve(true)
}
})
}, },
afterTabChange (index) { afterTabChange (index) {
if (this.loading) { if (this.loading) {
+13
View File
@@ -1,15 +1,28 @@
export function getFocusedElementId () { export function getFocusedElementId () {
return document.activeElement.id return document.activeElement.id
} }
export function getFocusedTabIndex (tabs = []) { export function getFocusedTabIndex (tabs = []) {
let activeId = getFocusedElementId() let activeId = getFocusedElementId()
let tabIndex = tabs.findIndex(tab => tab.tabId === activeId) let tabIndex = tabs.findIndex(tab => tab.tabId === activeId)
return tabIndex return tabIndex
} }
export function findElementAndFocus (elemId) { export function findElementAndFocus (elemId) {
let elem = document.getElementById(elemId) let elem = document.getElementById(elemId)
elem.focus() elem.focus()
} }
export function isPromise (func) { export function isPromise (func) {
return func.then && typeof func.then === 'function' return func.then && typeof func.then === 'function'
} }
export const promisify = func => (...args) =>
new Promise((resolve, reject) =>
func(...args, (err, result) => (err ? reject(err) : resolve(result)))
)
export const isPromiseLike = obj =>
obj !== null &&
(typeof obj === 'object' || typeof obj === 'function') &&
typeof obj.then === 'function'
+38 -39
View File
@@ -174,13 +174,13 @@ describe('FormWizard.vue', () => {
window.console = originalConsole window.console = originalConsole
}) })
it('resets wizard', () => { it('resets wizard', async () => {
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
let tabs = wizard.findAll(WizardTab) let tabs = wizard.findAll(WizardTab)
expect(tabs.length).to.equal(3) expect(tabs.length).to.equal(3)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
const lastTab = tabs.at(2) const lastTab = tabs.at(2)
expect(lastTab.vm.active).to.equal(true) expect(lastTab.vm.active).to.equal(true)
expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1) expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1)
@@ -248,29 +248,29 @@ describe('FormWizard.vue', () => {
expect(formWizard.vm.activeTabIndex).to.equal(startIndex) expect(formWizard.vm.activeTabIndex).to.equal(startIndex)
}) })
it('navigates to a visited tab', () => { it('navigates to a visited tab', async () => {
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
let tabs = wizard.findAll(WizardTab) let tabs = wizard.findAll(WizardTab)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.navigateToTab(0) await wizardInstance.vm.navigateToTab(0)
const firstTab = tabs.at(0) const firstTab = tabs.at(0)
expect(firstTab.vm.active).to.equal(true) expect(firstTab.vm.active).to.equal(true)
expect(wizardInstance.vm.activeTabIndex).to.equal(0) expect(wizardInstance.vm.activeTabIndex).to.equal(0)
expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1) expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1)
wizardInstance.vm.navigateToTab(2) await wizardInstance.vm.navigateToTab(2)
expect(wizardInstance.vm.activeTabIndex).to.equal(2) expect(wizardInstance.vm.activeTabIndex).to.equal(2)
expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1) expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1)
}) })
it('active tab is prev when current active tab is removed', (done) => { it('active tab is prev when current active tab is removed', async (done) => {
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
// navigate to last tab // navigate to last tab
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
const tabs = wizard.findAll(WizardTab) const tabs = wizard.findAll(WizardTab)
expect(tabs.length).to.equal(3) expect(tabs.length).to.equal(3)
wizard.setData({showLastStep: false}) wizard.setData({showLastStep: false})
@@ -282,11 +282,11 @@ describe('FormWizard.vue', () => {
}) })
}) })
it('with arrow keys on visited tabs', () => { it('with arrow keys on visited tabs', async () => {
const wizard = mount(threeStepWizard, {localVue, attachToDocument: true}) const wizard = mount(threeStepWizard, {localVue, attachToDocument: true})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizard.trigger('tab') wizard.trigger('tab')
wizard.trigger('keyup.right') wizard.trigger('keyup.right')
wizard.trigger('keyup.left') wizard.trigger('keyup.left')
@@ -333,57 +333,56 @@ describe('FormWizard.vue', () => {
} }
}) })
it('simple method', () => { it('simple method', async () => {
threeStepWizard.methods.validationMethod = sinon.stub() threeStepWizard.methods.validationMethod = sinon.stub()
threeStepWizard.methods.validationMethod.returns(true) threeStepWizard.methods.validationMethod.returns(true)
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
expect(threeStepWizard.methods.validationMethod.called).to.equal(true) expect(threeStepWizard.methods.validationMethod.called).to.equal(true)
expect(wizardInstance.vm.activeTabIndex).to.equal(1) expect(wizardInstance.vm.activeTabIndex).to.equal(1)
}) })
it('simple method on back navigation', () => { it('simple method on back navigation', async () => {
threeStepWizard.methods.secondValidationMethod = sinon.stub() threeStepWizard.methods.secondValidationMethod = sinon.stub()
threeStepWizard.methods.secondValidationMethod.returns(true) threeStepWizard.methods.secondValidationMethod.returns(true)
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.prevTab() await wizardInstance.vm.prevTab()
expect(threeStepWizard.methods.secondValidationMethod.called).to.equal(true) expect(threeStepWizard.methods.secondValidationMethod.called).to.equal(true)
}) })
it('falsy method', () => { it('falsy method', async() => {
threeStepWizard.methods.validationMethod = sinon.stub() threeStepWizard.methods.validationMethod = sinon.stub()
threeStepWizard.methods.validationMethod.returns(false) threeStepWizard.methods.validationMethod.returns(false)
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
expect(threeStepWizard.methods.validationMethod.called).to.equal(true) expect(threeStepWizard.methods.validationMethod.called).to.equal(true)
expect(wizardInstance.vm.activeTabIndex).to.equal(0) expect(wizardInstance.vm.activeTabIndex).to.equal(0)
}) })
it('promise', (done) => { it('promise', async() => {
threeStepWizard.methods.validationMethod = sinon.stub() threeStepWizard.methods.validationMethod = sinon.stub()
threeStepWizard.methods.validationMethod.returns(Promise.resolve(true)) threeStepWizard.methods.validationMethod.returns(Promise.resolve(true))
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
expect(threeStepWizard.methods.validationMethod.called).to.equal(true) expect(threeStepWizard.methods.validationMethod.called).to.equal(true)
Vue.nextTick(() => { expect(wizardInstance.vm.activeTabIndex).to.equal(1)
expect(wizardInstance.vm.activeTabIndex).to.equal(1)
done()
})
}) })
it('failing promise', (done) => { it('failing promise', async() => {
threeStepWizard.methods.validationMethod = sinon.stub() threeStepWizard.methods.validationMethod = sinon.stub()
threeStepWizard.methods.validationMethod.returns(Promise.reject(false)) threeStepWizard.methods.validationMethod.returns(Promise.reject(false))
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() try {
expect(threeStepWizard.methods.validationMethod.called).to.equal(true) await wizardInstance.vm.nextTab()
Vue.nextTick(() => { } catch(e) {
expect(wizardInstance.vm.activeTabIndex).to.equal(0) expect(threeStepWizard.methods.validationMethod.called).to.equal(true)
done() Vue.nextTick(() => {
}) expect(wizardInstance.vm.activeTabIndex).to.equal(0)
})
}
}) })
}) })
describe('after change', () => { describe('after change', () => {
@@ -411,22 +410,22 @@ describe('FormWizard.vue', () => {
} }
} }
}) })
it('simple method on after change', () => { it('simple method on after change', async() => {
threeStepWizard.methods.validationMethod = sinon.stub() threeStepWizard.methods.validationMethod = sinon.stub()
threeStepWizard.methods.validationMethod.returns(null) threeStepWizard.methods.validationMethod.returns(null)
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
expect(threeStepWizard.methods.validationMethod.should.have.been.called) expect(threeStepWizard.methods.validationMethod.should.have.been.called)
expect(wizardInstance.vm.activeTabIndex).to.equal(1) expect(wizardInstance.vm.activeTabIndex).to.equal(1)
}) })
it('simple method on back navigation after change', () => { it('simple method on back navigation after change', async () => {
threeStepWizard.methods.validationMethod = sinon.stub() threeStepWizard.methods.validationMethod = sinon.stub()
threeStepWizard.methods.validationMethod.returns(null) threeStepWizard.methods.validationMethod.returns(null)
const wizard = mount(threeStepWizard, {localVue}) const wizard = mount(threeStepWizard, {localVue})
const wizardInstance = wizard.find(FormWizard) const wizardInstance = wizard.find(FormWizard)
wizardInstance.vm.nextTab() await wizardInstance.vm.nextTab()
wizardInstance.vm.prevTab() await wizardInstance.vm.prevTab()
expect(threeStepWizard.methods.validationMethod.should.have.been.called) expect(threeStepWizard.methods.validationMethod.should.have.been.called)
}) })
}) })