mirror of
https://github.com/tenrok/vue-form-wizard.git
synced 2026-06-19 12:40:33 +03:00
Build
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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,24 +410,24 @@ 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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('with vue-router', ()=> {
|
describe('with vue-router', ()=> {
|
||||||
it('renders correct tab contents', () => {
|
it('renders correct tab contents', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user