Inisital Asset Commit
This commit is contained in:
231
assets/plugins/bs-stepper/css/bs-stepper-custom.css
Normal file
231
assets/plugins/bs-stepper/css/bs-stepper-custom.css
Normal file
@@ -0,0 +1,231 @@
|
||||
.bs-stepper {
|
||||
background-color: #FFF;
|
||||
box-shadow: 0 4px 24px 0 rgba(34, 41, 47, .1);
|
||||
border-radius: .5rem
|
||||
}
|
||||
.bs-stepper .bs-stepper-header {
|
||||
padding: 1.5rem;
|
||||
-webkit-flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
border-bottom: 1px solid rgba(34, 41, 47, .08);
|
||||
margin: 0
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .line {
|
||||
-webkit-box-flex: 0;
|
||||
-webkit-flex: 0;
|
||||
-ms-flex: 0;
|
||||
flex: 0;
|
||||
min-width: auto;
|
||||
min-height: auto;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0 1.75rem;
|
||||
color: #6E6B7B;
|
||||
font-size: 1.5rem
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step {
|
||||
margin-bottom: .25rem;
|
||||
margin-top: .25rem
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger {
|
||||
-webkit-flex-wrap: nowrap;
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
padding: 0;
|
||||
font-weight: 400
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-box {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
padding: .5em 0;
|
||||
font-weight: 500;
|
||||
color: #BABFC7;
|
||||
background-color: rgba(186, 191, 199, .12);
|
||||
border-radius: .35rem
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-label {
|
||||
text-align: left;
|
||||
margin: .5rem 0 0 1rem
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-label .bs-stepper-title {
|
||||
display: inherit;
|
||||
color: #6E6B7B;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
margin-bottom: 0
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-content .content:not(.active), .bs-stepper.vertical .bs-stepper-header .line {
|
||||
display: none
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-label .bs-stepper-subtitle {
|
||||
font-weight: 400;
|
||||
font-size: .85rem;
|
||||
color: #B9B9C3
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger:hover {
|
||||
background-color: transparent
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step.active .step-trigger .bs-stepper-box {
|
||||
background-color: #7367F0;
|
||||
color: #FFF;
|
||||
box-shadow: 0 3px 6px 0 rgba(115, 103, 240, .4)
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step.active .step-trigger .bs-stepper-label .bs-stepper-title {
|
||||
color: #7367F0
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step.crossed .step-trigger .bs-stepper-box {
|
||||
background-color: rgba(115, 103, 240, .12);
|
||||
color: #7367F0!important
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step.crossed .step-trigger .bs-stepper-label .bs-stepper-title {
|
||||
color: #B9B9C3
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step.crossed+.line {
|
||||
color: #7367F0
|
||||
}
|
||||
.bs-stepper .bs-stepper-content {
|
||||
padding: 1.5rem
|
||||
}
|
||||
.bs-stepper .bs-stepper-content .content {
|
||||
margin-left: 0
|
||||
}
|
||||
.bs-stepper .bs-stepper-content .content .content-header {
|
||||
margin-bottom: 1rem
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-header {
|
||||
border-right: 1px solid #EBE9F1;
|
||||
border-bottom: none
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-header .step .step-trigger {
|
||||
padding: 1rem 0
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-content {
|
||||
width: 100%;
|
||||
padding-top: 2.5rem
|
||||
}
|
||||
.bs-stepper.vertical.wizard-icons .step {
|
||||
text-align: center
|
||||
}
|
||||
.bs-stepper.wizard-modern {
|
||||
background-color: transparent;
|
||||
box-shadow: none
|
||||
}
|
||||
.bs-stepper.wizard-modern .bs-stepper-header {
|
||||
border: none
|
||||
}
|
||||
.bs-stepper.wizard-modern .bs-stepper-content {
|
||||
background-color: #FFF;
|
||||
border-radius: .5rem;
|
||||
box-shadow: 0 4px 24px 0 rgba(34, 41, 47, .1)
|
||||
}
|
||||
.horizontal-wizard, .modern-horizontal-wizard, .modern-vertical-wizard, .vertical-wizard {
|
||||
margin-bottom: 2.2rem
|
||||
}
|
||||
.dark-layout .bs-stepper {
|
||||
background-color: #283046;
|
||||
box-shadow: 0 4px 24px 0 rgba(34, 41, 47, .24)
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header {
|
||||
border-bottom: 1px solid rgba(59, 66, 83, .08)
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .line {
|
||||
color: #B4B7BD
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-box {
|
||||
color: #BABFC7
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-label .bs-stepper-title {
|
||||
color: #B4B7BD
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .step .step-trigger .bs-stepper-label .bs-stepper-subtitle {
|
||||
color: #676D7D
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .step.active .step-trigger .bs-stepper-box {
|
||||
background-color: #7367F0;
|
||||
color: #FFF;
|
||||
box-shadow: 0 3px 6px 0 rgba(115, 103, 240, .4)
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .step.active .step-trigger .bs-stepper-label .bs-stepper-title {
|
||||
color: #7367F0
|
||||
}
|
||||
.dark-layout .bs-stepper .bs-stepper-header .step.crossed .step-trigger .bs-stepper-label, .dark-layout .bs-stepper .bs-stepper-header .step.crossed .step-trigger .bs-stepper-title {
|
||||
color: #676D7D
|
||||
}
|
||||
.dark-layout .bs-stepper.vertical .bs-stepper-header {
|
||||
border-right-color: #3B4253
|
||||
}
|
||||
.dark-layout .bs-stepper.wizard-modern {
|
||||
background-color: transparent;
|
||||
box-shadow: none
|
||||
}
|
||||
.dark-layout .bs-stepper.wizard-modern .bs-stepper-header {
|
||||
border: none
|
||||
}
|
||||
.dark-layout .bs-stepper.wizard-modern .bs-stepper-content {
|
||||
background-color: #283046;
|
||||
box-shadow: 0 4px 24px 0 rgba(34, 41, 47, .24)
|
||||
}
|
||||
html[data-textdirection=rtl] .btn-next, html[data-textdirection=rtl] .btn-prev {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex
|
||||
}
|
||||
html[data-textdirection=rtl] .btn-next i, html[data-textdirection=rtl] .btn-next svg, html[data-textdirection=rtl] .btn-prev i, html[data-textdirection=rtl] .btn-prev svg {
|
||||
-webkit-transform: rotate(-180deg);
|
||||
-ms-transform: rotate(-180deg);
|
||||
transform: rotate(-180deg)
|
||||
}
|
||||
@media (max-width:992px) {
|
||||
.bs-stepper .bs-stepper-header {
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .step .step-trigger {
|
||||
padding: .5rem 0!important;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row
|
||||
}
|
||||
.bs-stepper .bs-stepper-header .line {
|
||||
display: none
|
||||
}
|
||||
.bs-stepper.vertical {
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-header {
|
||||
-webkit-box-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-content {
|
||||
padding-top: 1.5rem
|
||||
}
|
||||
}
|
||||
208
assets/plugins/bs-stepper/css/bs-stepper.css
Normal file
208
assets/plugins/bs-stepper/css/bs-stepper.css
Normal file
@@ -0,0 +1,208 @@
|
||||
/*!
|
||||
* bsStepper v{version} (https://github.com/Johann-S/bs-stepper)
|
||||
* Copyright 2018 - {year} Johann-S <johann.servoire@gmail.com>
|
||||
* Licensed under MIT (https://github.com/Johann-S/bs-stepper/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
.bs-stepper .step-trigger {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
gap: 1rem;
|
||||
padding: 16px 0px;
|
||||
color: #6c757d;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
background-color: rgb(246 246 246 / 0%);
|
||||
border: none;
|
||||
border-radius: .25rem;
|
||||
transition: background-color .15s ease-out, color .15s ease-out;
|
||||
}
|
||||
|
||||
.bs-stepper .step-trigger:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bs-stepper .step-trigger:disabled,
|
||||
.bs-stepper .step-trigger.disabled {
|
||||
pointer-events: none;
|
||||
opacity: .65;
|
||||
}
|
||||
|
||||
.bs-stepper .step-trigger:focus {
|
||||
color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.bs-stepper .step-trigger:hover {
|
||||
text-decoration: none;
|
||||
background-color: rgba(0, 0, 0, .0);
|
||||
}
|
||||
|
||||
|
||||
.bs-stepper-label {
|
||||
display: inline-block;
|
||||
margin: .25rem;
|
||||
}
|
||||
|
||||
.bs-stepper-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.bs-stepper-line,
|
||||
.bs-stepper .line {
|
||||
flex: 1 0 32px;
|
||||
min-width: 1px;
|
||||
min-height: 1px;
|
||||
margin: auto;
|
||||
background-color: rgba(0, 0, 0, .12);
|
||||
}
|
||||
|
||||
.bs-stepper.vertical .bs-stepper-content .content:not(.active), .bs-stepper.vertical .bs-stepper-header .line {
|
||||
display: none
|
||||
}
|
||||
|
||||
.bs-stepper-circle {
|
||||
width: 2.7rem;
|
||||
height: 2.7rem;
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #969595;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.step .steper-title {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.step .steper-sub-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.active .bs-stepper-circle {
|
||||
color: #ffffff;
|
||||
background-color: #007bff;
|
||||
}
|
||||
|
||||
.bs-stepper-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width:992px) {
|
||||
.bs-stepper.vertical {
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column
|
||||
}
|
||||
.bs-stepper.vertical .bs-stepper-header {
|
||||
-webkit-box-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.bs-stepper-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-stepper.vertical {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bs-stepper.vertical .bs-stepper-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
background-color: #fbfbfb;
|
||||
border-right: 1px solid #EBE9F1;
|
||||
border-bottom: none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.bs-stepper.vertical .bs-stepper-pane,
|
||||
.bs-stepper.vertical .content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bs-stepper.vertical .bs-stepper-pane:not(.fade),
|
||||
.bs-stepper.vertical .content:not(.fade) {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.bs-stepper-pane:not(.fade),
|
||||
.bs-stepper .content:not(.fade) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bs-stepper .content.fade,
|
||||
.bs-stepper-pane.fade {
|
||||
visibility: hidden;
|
||||
transition-duration: .3s;
|
||||
transition-property: opacity;
|
||||
}
|
||||
|
||||
.bs-stepper-pane.fade.active,
|
||||
.bs-stepper .content.fade.active {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bs-stepper-pane.active:not(.fade),
|
||||
.bs-stepper .content.active:not(.fade) {
|
||||
display: block;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.bs-stepper-pane.dstepper-block,
|
||||
.bs-stepper .content.dstepper-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bs-stepper:not(.vertical) .bs-stepper-pane.dstepper-none,
|
||||
.bs-stepper:not(.vertical) .content.dstepper-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vertical .bs-stepper-pane.fade.dstepper-none,
|
||||
.vertical .content.fade.dstepper-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* dark theme */
|
||||
|
||||
html.dark-theme .bs-stepper.vertical .bs-stepper-header{
|
||||
background-color: #31373c;
|
||||
border-right: 1px solid rgb(255 255 255 / 12%);
|
||||
}
|
||||
|
||||
|
||||
html.dark-theme .step .steper-title {
|
||||
color: #cdcdce;
|
||||
}
|
||||
|
||||
html.dark-theme .step .steper-sub-title {
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
html.dark-theme .bs-stepper-circle {
|
||||
color: #c7cdcf;
|
||||
background-color: #212529;
|
||||
}
|
||||
1
assets/plugins/bs-stepper/css/custom.js
Normal file
1
assets/plugins/bs-stepper/css/custom.js
Normal file
File diff suppressed because one or more lines are too long
7
assets/plugins/bs-stepper/js/bs-stepper.min.js
vendored
Normal file
7
assets/plugins/bs-stepper/js/bs-stepper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
127
assets/plugins/bs-stepper/js/index.js
Normal file
127
assets/plugins/bs-stepper/js/index.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { show, customProperty, detectAnimation, ClassName } from './util'
|
||||
import { buildClickStepLinearListener, buildClickStepNonLinearListener } from './listeners'
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
linear: true,
|
||||
animation: false,
|
||||
selectors: {
|
||||
steps: '.step',
|
||||
trigger: '.step-trigger',
|
||||
stepper: '.bs-stepper'
|
||||
}
|
||||
}
|
||||
|
||||
class Stepper {
|
||||
constructor (element, _options = {}) {
|
||||
this._element = element
|
||||
this._currentIndex = 0
|
||||
this._stepsContents = []
|
||||
|
||||
this.options = {
|
||||
...DEFAULT_OPTIONS,
|
||||
..._options
|
||||
}
|
||||
|
||||
this.options.selectors = {
|
||||
...DEFAULT_OPTIONS.selectors,
|
||||
...this.options.selectors
|
||||
}
|
||||
|
||||
if (this.options.linear) {
|
||||
this._element.classList.add(ClassName.LINEAR)
|
||||
}
|
||||
|
||||
this._steps = [].slice.call(this._element.querySelectorAll(this.options.selectors.steps))
|
||||
|
||||
this._steps.filter(step => step.hasAttribute('data-target'))
|
||||
.forEach(step => {
|
||||
this._stepsContents.push(
|
||||
this._element.querySelector(step.getAttribute('data-target'))
|
||||
)
|
||||
})
|
||||
|
||||
detectAnimation(this._stepsContents, this.options)
|
||||
this._setLinkListeners()
|
||||
Object.defineProperty(this._element, customProperty, {
|
||||
value: this,
|
||||
writable: true
|
||||
})
|
||||
|
||||
if (this._steps.length) {
|
||||
show(this._element, this._currentIndex, this.options, () => {})
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_setLinkListeners () {
|
||||
this._steps.forEach(step => {
|
||||
const trigger = step.querySelector(this.options.selectors.trigger)
|
||||
|
||||
if (this.options.linear) {
|
||||
this._clickStepLinearListener = buildClickStepLinearListener(this.options)
|
||||
trigger.addEventListener('click', this._clickStepLinearListener)
|
||||
} else {
|
||||
this._clickStepNonLinearListener = buildClickStepNonLinearListener(this.options)
|
||||
trigger.addEventListener('click', this._clickStepNonLinearListener)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
next () {
|
||||
const nextStep = (this._currentIndex + 1) <= this._steps.length - 1 ? this._currentIndex + 1 : (this._steps.length - 1)
|
||||
|
||||
show(this._element, nextStep, this.options, () => {
|
||||
this._currentIndex = nextStep
|
||||
})
|
||||
}
|
||||
|
||||
previous () {
|
||||
const previousStep = (this._currentIndex - 1) >= 0 ? this._currentIndex - 1 : 0
|
||||
|
||||
show(this._element, previousStep, this.options, () => {
|
||||
this._currentIndex = previousStep
|
||||
})
|
||||
}
|
||||
|
||||
to (stepNumber) {
|
||||
const tempIndex = stepNumber - 1
|
||||
const nextStep = tempIndex >= 0 && tempIndex < this._steps.length
|
||||
? tempIndex
|
||||
: 0
|
||||
|
||||
show(this._element, nextStep, this.options, () => {
|
||||
this._currentIndex = nextStep
|
||||
})
|
||||
}
|
||||
|
||||
reset () {
|
||||
show(this._element, 0, this.options, () => {
|
||||
this._currentIndex = 0
|
||||
})
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this._steps.forEach(step => {
|
||||
const trigger = step.querySelector(this.options.selectors.trigger)
|
||||
|
||||
if (this.options.linear) {
|
||||
trigger.removeEventListener('click', this._clickStepLinearListener)
|
||||
} else {
|
||||
trigger.removeEventListener('click', this._clickStepNonLinearListener)
|
||||
}
|
||||
})
|
||||
|
||||
this._element[customProperty] = undefined
|
||||
this._element = undefined
|
||||
this._currentIndex = undefined
|
||||
this._steps = undefined
|
||||
this._stepsContents = undefined
|
||||
this._clickStepLinearListener = undefined
|
||||
this._clickStepNonLinearListener = undefined
|
||||
}
|
||||
}
|
||||
|
||||
export default Stepper
|
||||
677
assets/plugins/bs-stepper/js/index.spec.js
Normal file
677
assets/plugins/bs-stepper/js/index.spec.js
Normal file
@@ -0,0 +1,677 @@
|
||||
/* eslint-env jasmine */
|
||||
import Stepper from './index'
|
||||
|
||||
describe('Stepper', () => {
|
||||
let fixture
|
||||
|
||||
beforeAll(() => {
|
||||
const fixureNode = document.createElement('div')
|
||||
fixureNode.setAttribute('id', 'fixture')
|
||||
|
||||
document.body.appendChild(fixureNode)
|
||||
fixture = document.getElementById('fixture')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fixture.innerHTML = ''
|
||||
})
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should create a stepper', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger" id="trigger1">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger" id="trigger2">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
expect(stepperNode.classList.contains('linear')).toBe(true)
|
||||
expect(stepper._steps.length).toEqual(2)
|
||||
expect(stepperNode['bsStepper']).toEqual(stepper)
|
||||
expect(document.querySelector('.step').classList.contains('active')).toBe(true)
|
||||
expect(document.getElementById('trigger1').getAttribute('aria-selected')).toEqual('true')
|
||||
expect(document.getElementById('trigger2').getAttribute('aria-selected')).toEqual('false')
|
||||
expect(stepper.options).toEqual({
|
||||
linear: true,
|
||||
animation: false,
|
||||
selectors: {
|
||||
steps: '.step',
|
||||
trigger: '.step-trigger',
|
||||
stepper: '.bs-stepper'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should do nothing if there is no step', () => {
|
||||
fixture.innerHTML = '<div id="myStepper" class="bs-stepper"></div>'
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
expect(stepperNode.classList.contains('linear')).toBe(true)
|
||||
expect(stepper._steps.length).toEqual(0)
|
||||
expect(stepperNode['bsStepper']).toEqual(stepper)
|
||||
})
|
||||
|
||||
it('should create a non linear stepper', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode, {
|
||||
linear: false
|
||||
})
|
||||
|
||||
expect(stepperNode.classList.contains('linear')).toBe(false)
|
||||
expect(stepper._steps.length).toEqual(2)
|
||||
expect(document.querySelector('.step').classList.contains('active')).toBe(true)
|
||||
expect(stepperNode['bsStepper']).toEqual(stepper)
|
||||
expect(stepper._clickStepLinearListener).toBeUndefined()
|
||||
expect(stepper._clickStepNonLinearListener).toBeTruthy()
|
||||
expect(stepper.options).toEqual({
|
||||
linear: false,
|
||||
animation: false,
|
||||
selectors: {
|
||||
steps: '.step',
|
||||
trigger: '.step-trigger',
|
||||
stepper: '.bs-stepper'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should go to the next step when user click on a step for non linear stepper', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button id="trigger1" class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button id="trigger2" class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode, {
|
||||
linear: false
|
||||
})
|
||||
|
||||
const trigger2 = document.querySelector('#trigger2')
|
||||
trigger2.click()
|
||||
|
||||
expect(document.querySelector('#test1').classList.contains('active')).toBe(false)
|
||||
expect(document.querySelector('#test2').classList.contains('active')).toBe(true)
|
||||
expect(document.getElementById('trigger1').getAttribute('aria-selected')).toEqual('false')
|
||||
expect(document.getElementById('trigger2').getAttribute('aria-selected')).toEqual('true')
|
||||
expect(stepper._currentIndex).toEqual(1)
|
||||
})
|
||||
|
||||
it('should call preventDefault when user click on a step for linear stepper', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button id="trigger2" class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
const trigger2 = document.querySelector('#trigger2')
|
||||
trigger2.removeAttribute('disabled')
|
||||
const clickEvent = document.createEvent('Event')
|
||||
clickEvent.initEvent('click', true, true)
|
||||
|
||||
spyOn(clickEvent, 'preventDefault')
|
||||
|
||||
trigger2.dispatchEvent(clickEvent)
|
||||
|
||||
expect(clickEvent.preventDefault).toHaveBeenCalled()
|
||||
expect(stepper._currentIndex).toEqual(0)
|
||||
})
|
||||
|
||||
it('should create a stepper with fade animation', done => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode, {
|
||||
animation: true
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
expect(stepper.options).toEqual({
|
||||
linear: true,
|
||||
animation: true,
|
||||
selectors: {
|
||||
steps: '.step',
|
||||
trigger: '.step-trigger',
|
||||
stepper: '.bs-stepper'
|
||||
}
|
||||
})
|
||||
expect(document.querySelector('#test1').classList.contains('fade')).toBe(true)
|
||||
expect(document.querySelector('#test2').classList.contains('fade')).toBe(true)
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
|
||||
it('should add event listeners on triggers', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button id="trigger1" class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button id="trigger2" class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const trigger1 = document.querySelector('#trigger1')
|
||||
const trigger2 = document.querySelector('#trigger2')
|
||||
|
||||
spyOn(trigger1, 'addEventListener')
|
||||
spyOn(trigger2, 'addEventListener')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
expect(trigger1.addEventListener).toHaveBeenCalled()
|
||||
expect(trigger2.addEventListener).toHaveBeenCalled()
|
||||
expect(stepperNode['bsStepper']).toEqual(stepper)
|
||||
})
|
||||
|
||||
it('should allow css selector configuration', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="custom-bs-stepper">',
|
||||
' <div class="custom-step" data-target="#test1">',
|
||||
' <button id="trigger1" class="custom-step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="custom-step" data-target="#test2">',
|
||||
' <button id="trigger2" class="custom-step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode, {
|
||||
selectors: {
|
||||
steps: '.custom-step',
|
||||
trigger: '.custom-step-trigger',
|
||||
stepper: '.custom-bs-stepper'
|
||||
}
|
||||
})
|
||||
|
||||
expect(stepper.options).toEqual({
|
||||
linear: true,
|
||||
animation: false,
|
||||
selectors: {
|
||||
steps: '.custom-step',
|
||||
trigger: '.custom-step-trigger',
|
||||
stepper: '.custom-bs-stepper'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('next', () => {
|
||||
it('should go to the next step', done => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
stepperNode.addEventListener('show.bs-stepper', function (event) {
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
expect(event.detail.to).toEqual(1)
|
||||
expect(event.detail.from).toEqual(0)
|
||||
})
|
||||
stepperNode.addEventListener('shown.bs-stepper', function (event) {
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
expect(event.detail.to).toEqual(1)
|
||||
expect(event.detail.from).toEqual(0)
|
||||
expect(document.querySelector('#test1').classList.contains('active')).toBe(false)
|
||||
expect(document.querySelector('#test2').classList.contains('active')).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
stepper.next()
|
||||
})
|
||||
|
||||
it('should go to the next step with css selector configuration', done => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="custom-bs-stepper">',
|
||||
' <div class="custom-step" data-target="#test1">',
|
||||
' <button id="trigger1" class="custom-step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="custom-step" data-target="#test2">',
|
||||
' <button id="trigger2" class="custom-step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode, {
|
||||
selectors: {
|
||||
steps: '.custom-step',
|
||||
trigger: '.custom-step-trigger',
|
||||
stepper: '.custom-bs-stepper'
|
||||
}
|
||||
})
|
||||
|
||||
expect(stepper.options).toEqual({
|
||||
linear: true,
|
||||
animation: false,
|
||||
selectors: {
|
||||
steps: '.custom-step',
|
||||
trigger: '.custom-step-trigger',
|
||||
stepper: '.custom-bs-stepper'
|
||||
}
|
||||
})
|
||||
|
||||
stepperNode.addEventListener('show.bs-stepper', function (event) {
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
})
|
||||
stepperNode.addEventListener('shown.bs-stepper', function (event) {
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
expect(document.querySelector('#test1').classList.contains('active')).toBe(false)
|
||||
expect(document.querySelector('#test2').classList.contains('active')).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
stepper.next()
|
||||
})
|
||||
|
||||
it('should not go to the next step if the show event is default prevented', done => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
const listeners = {
|
||||
show (event) {
|
||||
event.preventDefault()
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
|
||||
setTimeout(() => {
|
||||
expect(listeners.shown).not.toHaveBeenCalled()
|
||||
expect(stepper._currentIndex).toEqual(0)
|
||||
done()
|
||||
}, 10)
|
||||
},
|
||||
shown () {
|
||||
console.warn('shown called but it should not be the case')
|
||||
}
|
||||
}
|
||||
|
||||
spyOn(listeners, 'shown')
|
||||
|
||||
stepperNode.addEventListener('show.bs-stepper', listeners.show)
|
||||
stepperNode.addEventListener('shown.bs-stepper', listeners.shown)
|
||||
|
||||
stepper.next()
|
||||
})
|
||||
|
||||
it('should stay at the end if we call next', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
stepper.next()
|
||||
stepper.next()
|
||||
|
||||
expect(document.querySelector('#test1').classList.contains('active')).toBe(false)
|
||||
expect(document.querySelector('#test2').classList.contains('active')).toBe(true)
|
||||
})
|
||||
|
||||
it('should keep block class on previous steps for vertical stepper without fade', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper vertical">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
stepper.next()
|
||||
|
||||
expect(document.querySelector('#test2').classList.contains('active')).toBe(true)
|
||||
expect(document.querySelector('#test2').classList.contains('dstepper-block')).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('previous', () => {
|
||||
it('should return to the previous step', done => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
const test1 = document.querySelector('#test1')
|
||||
const test2 = document.querySelector('#test2')
|
||||
|
||||
stepper.next()
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(false)
|
||||
expect(test2.classList.contains('active')).toBe(true)
|
||||
|
||||
stepperNode.addEventListener('show.bs-stepper', function (event) {
|
||||
expect(event.detail.indexStep).toEqual(0)
|
||||
expect(event.detail.to).toEqual(0)
|
||||
expect(event.detail.from).toEqual(1)
|
||||
})
|
||||
|
||||
stepperNode.addEventListener('shown.bs-stepper', function (event) {
|
||||
expect(event.detail.indexStep).toEqual(0)
|
||||
expect(event.detail.to).toEqual(0)
|
||||
expect(event.detail.from).toEqual(1)
|
||||
expect(test1.classList.contains('active')).toBe(true)
|
||||
expect(test2.classList.contains('active')).toBe(false)
|
||||
done()
|
||||
})
|
||||
|
||||
stepper.previous()
|
||||
})
|
||||
|
||||
it('should stay at the first step if previous called', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const test1 = document.querySelector('#test1')
|
||||
const test2 = document.querySelector('#test2')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
stepper.previous()
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(true)
|
||||
expect(test2.classList.contains('active')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('to', () => {
|
||||
it('should go to the step number', done => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
const test1 = document.querySelector('#test1')
|
||||
const test2 = document.querySelector('#test2')
|
||||
|
||||
stepperNode.addEventListener('show.bs-stepper', event => {
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
expect(event.detail.to).toEqual(1)
|
||||
expect(event.detail.from).toEqual(0)
|
||||
})
|
||||
|
||||
stepperNode.addEventListener('shown.bs-stepper', event => {
|
||||
expect(event.detail.indexStep).toEqual(1)
|
||||
expect(event.detail.to).toEqual(1)
|
||||
expect(event.detail.from).toEqual(0)
|
||||
expect(test1.classList.contains('active')).toBe(false)
|
||||
expect(test2.classList.contains('active')).toBe(true)
|
||||
done()
|
||||
})
|
||||
|
||||
stepper.to(2)
|
||||
})
|
||||
|
||||
it('should handle wrong inputs', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test3">',
|
||||
' <button class="step-trigger">3</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
' <div id="test3">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
const test1 = document.querySelector('#test1')
|
||||
const test2 = document.querySelector('#test2')
|
||||
const test3 = document.querySelector('#test3')
|
||||
|
||||
stepper.to(-5)
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(true)
|
||||
expect(test2.classList.contains('active')).toBe(false)
|
||||
expect(test3.classList.contains('active')).toBe(false)
|
||||
|
||||
stepper.to(2)
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(false)
|
||||
expect(test2.classList.contains('active')).toBe(true)
|
||||
expect(test3.classList.contains('active')).toBe(false)
|
||||
|
||||
stepper.to(stepper._steps.length + 1)
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(true)
|
||||
expect(test2.classList.contains('active')).toBe(false)
|
||||
expect(test3.classList.contains('active')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('reset', () => {
|
||||
it('should return to the first step', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
const test1 = document.querySelector('#test1')
|
||||
const test2 = document.querySelector('#test2')
|
||||
|
||||
stepper.next()
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(false)
|
||||
expect(test2.classList.contains('active')).toBe(true)
|
||||
|
||||
stepper.reset()
|
||||
|
||||
expect(test1.classList.contains('active')).toBe(true)
|
||||
expect(test2.classList.contains('active')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('destroy', () => {
|
||||
it('should clear properties', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode)
|
||||
|
||||
expect(stepperNode['bsStepper']).toEqual(stepper)
|
||||
expect(stepper._element).toEqual(stepperNode)
|
||||
expect(stepper._currentIndex).toEqual(0)
|
||||
expect(stepper._steps.length).toEqual(2)
|
||||
expect(stepper._stepsContents.length).toEqual(2)
|
||||
expect(stepper._clickStepLinearListener).toBeTruthy()
|
||||
expect(stepper._clickStepNonLinearListener).toBeUndefined()
|
||||
|
||||
stepper.destroy()
|
||||
|
||||
expect(stepperNode.bsStepper).toBeUndefined()
|
||||
expect(stepper._element).toBeUndefined()
|
||||
expect(stepper._currentIndex).toBeUndefined()
|
||||
expect(stepper._steps).toBeUndefined()
|
||||
expect(stepper._stepsContents).toBeUndefined()
|
||||
expect(stepper._clickStepLinearListener).toBeUndefined()
|
||||
expect(stepper._clickStepNonLinearListener).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should remove event listeners on triggers', () => {
|
||||
fixture.innerHTML = [
|
||||
'<div id="myStepper" class="bs-stepper">',
|
||||
' <div class="step" data-target="#test1">',
|
||||
' <button id="trigger1" class="step-trigger">1</button>',
|
||||
' </div>',
|
||||
' <div class="step" data-target="#test2">',
|
||||
' <button id="trigger2" class="step-trigger">2</button>',
|
||||
' </div>',
|
||||
' <div id="test1">1</div>',
|
||||
' <div id="test2">2</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const stepperNode = document.getElementById('myStepper')
|
||||
const stepper = new Stepper(stepperNode, { linear: false })
|
||||
|
||||
const trigger1 = document.querySelector('#trigger1')
|
||||
const trigger2 = document.querySelector('#trigger2')
|
||||
|
||||
spyOn(trigger1, 'removeEventListener')
|
||||
spyOn(trigger2, 'removeEventListener')
|
||||
|
||||
stepper.destroy()
|
||||
|
||||
expect(trigger1.removeEventListener).toHaveBeenCalled()
|
||||
expect(trigger2.removeEventListener).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
24
assets/plugins/bs-stepper/js/listeners.js
Normal file
24
assets/plugins/bs-stepper/js/listeners.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { closest } from './polyfill'
|
||||
import { customProperty, show } from './util'
|
||||
|
||||
const buildClickStepLinearListener = () => function clickStepLinearListener (event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
const buildClickStepNonLinearListener = options => function clickStepNonLinearListener (event) {
|
||||
event.preventDefault()
|
||||
|
||||
const step = closest(event.target, options.selectors.steps)
|
||||
const stepperNode = closest(step, options.selectors.stepper)
|
||||
const stepper = stepperNode[customProperty]
|
||||
const stepIndex = stepper._steps.indexOf(step)
|
||||
|
||||
show(stepperNode, stepIndex, options, () => {
|
||||
stepper._currentIndex = stepIndex
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
buildClickStepLinearListener,
|
||||
buildClickStepNonLinearListener
|
||||
}
|
||||
49
assets/plugins/bs-stepper/js/main.js
Normal file
49
assets/plugins/bs-stepper/js/main.js
Normal file
@@ -0,0 +1,49 @@
|
||||
var stepper1
|
||||
var stepper2
|
||||
//var stepper3
|
||||
var stepper4
|
||||
var stepperForm
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
stepper1 = new Stepper(document.querySelector('#stepper1'))
|
||||
stepper2 = new Stepper(document.querySelector('#stepper2'), {
|
||||
linear: false
|
||||
})
|
||||
|
||||
stepper3 = new Stepper(document.querySelector('#stepper3'))
|
||||
|
||||
var stepperFormEl = document.querySelector('#stepperForm')
|
||||
stepperForm = new Stepper(stepperFormEl, {
|
||||
animation: true
|
||||
})
|
||||
|
||||
var btnNextList = [].slice.call(document.querySelectorAll('.btn-next-form'))
|
||||
var stepperPanList = [].slice.call(stepperFormEl.querySelectorAll('.bs-stepper-pane'))
|
||||
var inputMailForm = document.getElementById('inputMailForm')
|
||||
var inputPasswordForm = document.getElementById('inputPasswordForm')
|
||||
var form = stepperFormEl.querySelector('.bs-stepper-content form')
|
||||
|
||||
btnNextList.forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
stepperForm.next()
|
||||
})
|
||||
})
|
||||
|
||||
stepperFormEl.addEventListener('show.bs-stepper', function (event) {
|
||||
form.classList.remove('was-validated')
|
||||
var nextStep = event.detail.indexStep
|
||||
var currentStep = nextStep
|
||||
|
||||
if (currentStep > 0) {
|
||||
currentStep--
|
||||
}
|
||||
|
||||
var stepperPan = stepperPanList[currentStep]
|
||||
|
||||
if ((stepperPan.getAttribute('id') === 'test-form-1' && !inputMailForm.value.length)
|
||||
|| (stepperPan.getAttribute('id') === 'test-form-2' && !inputPasswordForm.value.length)) {
|
||||
event.preventDefault()
|
||||
form.classList.add('was-validated')
|
||||
}
|
||||
})
|
||||
})
|
||||
74
assets/plugins/bs-stepper/js/polyfill.js
Normal file
74
assets/plugins/bs-stepper/js/polyfill.js
Normal file
@@ -0,0 +1,74 @@
|
||||
let matches = window.Element.prototype.matches
|
||||
let closest = (element, selector) => element.closest(selector)
|
||||
let WinEvent = (inType, params) => new window.Event(inType, params)
|
||||
let createCustomEvent = (eventName, params) => {
|
||||
const cEvent = new window.CustomEvent(eventName, params)
|
||||
|
||||
return cEvent
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function polyfill () {
|
||||
if (!window.Element.prototype.matches) {
|
||||
matches = window.Element.prototype.msMatchesSelector ||
|
||||
window.Element.prototype.webkitMatchesSelector
|
||||
}
|
||||
|
||||
if (!window.Element.prototype.closest) {
|
||||
closest = (element, selector) => {
|
||||
if (!document.documentElement.contains(element)) {
|
||||
return null
|
||||
}
|
||||
|
||||
do {
|
||||
if (matches.call(element, selector)) {
|
||||
return element
|
||||
}
|
||||
|
||||
element = element.parentElement || element.parentNode
|
||||
} while (element !== null && element.nodeType === 1)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.Event || typeof window.Event !== 'function') {
|
||||
WinEvent = (inType, params) => {
|
||||
params = params || {}
|
||||
const e = document.createEvent('Event')
|
||||
e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable))
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window.CustomEvent !== 'function') {
|
||||
const originPreventDefault = window.Event.prototype.preventDefault
|
||||
|
||||
createCustomEvent = (eventName, params) => {
|
||||
const evt = document.createEvent('CustomEvent')
|
||||
|
||||
params = params || { bubbles: false, cancelable: false, detail: null }
|
||||
evt.initCustomEvent(eventName, params.bubbles, params.cancelable, params.detail)
|
||||
evt.preventDefault = function () {
|
||||
if (!this.cancelable) {
|
||||
return
|
||||
}
|
||||
|
||||
originPreventDefault.call(this)
|
||||
Object.defineProperty(this, 'defaultPrevented', {
|
||||
get: function () { return true }
|
||||
})
|
||||
}
|
||||
|
||||
return evt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
polyfill()
|
||||
|
||||
export {
|
||||
closest,
|
||||
WinEvent,
|
||||
createCustomEvent
|
||||
}
|
||||
168
assets/plugins/bs-stepper/js/util.js
Normal file
168
assets/plugins/bs-stepper/js/util.js
Normal file
@@ -0,0 +1,168 @@
|
||||
import { WinEvent, createCustomEvent } from './polyfill'
|
||||
|
||||
const MILLISECONDS_MULTIPLIER = 1000
|
||||
|
||||
const ClassName = {
|
||||
ACTIVE: 'active',
|
||||
LINEAR: 'linear',
|
||||
BLOCK: 'dstepper-block',
|
||||
NONE: 'dstepper-none',
|
||||
FADE: 'fade',
|
||||
VERTICAL: 'vertical'
|
||||
}
|
||||
|
||||
const transitionEndEvent = 'transitionend'
|
||||
const customProperty = 'bsStepper'
|
||||
|
||||
const show = (stepperNode, indexStep, options, done) => {
|
||||
const stepper = stepperNode[customProperty]
|
||||
|
||||
if (stepper._steps[indexStep].classList.contains(ClassName.ACTIVE) || stepper._stepsContents[indexStep].classList.contains(ClassName.ACTIVE)) {
|
||||
return
|
||||
}
|
||||
|
||||
const showEvent = createCustomEvent('show.bs-stepper', {
|
||||
cancelable: true,
|
||||
detail: {
|
||||
from: stepper._currentIndex,
|
||||
to: indexStep,
|
||||
indexStep
|
||||
}
|
||||
})
|
||||
stepperNode.dispatchEvent(showEvent)
|
||||
|
||||
const activeStep = stepper._steps.filter(step => step.classList.contains(ClassName.ACTIVE))
|
||||
const activeContent = stepper._stepsContents.filter(content => content.classList.contains(ClassName.ACTIVE))
|
||||
|
||||
if (showEvent.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
|
||||
if (activeStep.length) {
|
||||
activeStep[0].classList.remove(ClassName.ACTIVE)
|
||||
}
|
||||
if (activeContent.length) {
|
||||
activeContent[0].classList.remove(ClassName.ACTIVE)
|
||||
|
||||
if (!stepperNode.classList.contains(ClassName.VERTICAL) && !stepper.options.animation) {
|
||||
activeContent[0].classList.remove(ClassName.BLOCK)
|
||||
}
|
||||
}
|
||||
|
||||
showStep(stepperNode, stepper._steps[indexStep], stepper._steps, options)
|
||||
showContent(stepperNode, stepper._stepsContents[indexStep], stepper._stepsContents, activeContent, done)
|
||||
}
|
||||
|
||||
const showStep = (stepperNode, step, stepList, options) => {
|
||||
stepList.forEach(step => {
|
||||
const trigger = step.querySelector(options.selectors.trigger)
|
||||
|
||||
trigger.setAttribute('aria-selected', 'false')
|
||||
// if stepper is in linear mode, set disabled attribute on the trigger
|
||||
if (stepperNode.classList.contains(ClassName.LINEAR)) {
|
||||
trigger.setAttribute('disabled', 'disabled')
|
||||
}
|
||||
})
|
||||
|
||||
step.classList.add(ClassName.ACTIVE)
|
||||
const currentTrigger = step.querySelector(options.selectors.trigger)
|
||||
|
||||
currentTrigger.setAttribute('aria-selected', 'true')
|
||||
// if stepper is in linear mode, remove disabled attribute on current
|
||||
if (stepperNode.classList.contains(ClassName.LINEAR)) {
|
||||
currentTrigger.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
|
||||
const showContent = (stepperNode, content, contentList, activeContent, done) => {
|
||||
const stepper = stepperNode[customProperty]
|
||||
const toIndex = contentList.indexOf(content)
|
||||
const shownEvent = createCustomEvent('shown.bs-stepper', {
|
||||
cancelable: true,
|
||||
detail: {
|
||||
from: stepper._currentIndex,
|
||||
to: toIndex,
|
||||
indexStep: toIndex
|
||||
}
|
||||
})
|
||||
|
||||
function complete () {
|
||||
content.classList.add(ClassName.BLOCK)
|
||||
content.removeEventListener(transitionEndEvent, complete)
|
||||
stepperNode.dispatchEvent(shownEvent)
|
||||
done()
|
||||
}
|
||||
|
||||
if (content.classList.contains(ClassName.FADE)) {
|
||||
content.classList.remove(ClassName.NONE)
|
||||
const duration = getTransitionDurationFromElement(content)
|
||||
|
||||
content.addEventListener(transitionEndEvent, complete)
|
||||
if (activeContent.length) {
|
||||
activeContent[0].classList.add(ClassName.NONE)
|
||||
}
|
||||
|
||||
content.classList.add(ClassName.ACTIVE)
|
||||
emulateTransitionEnd(content, duration)
|
||||
} else {
|
||||
content.classList.add(ClassName.ACTIVE)
|
||||
content.classList.add(ClassName.BLOCK)
|
||||
stepperNode.dispatchEvent(shownEvent)
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
const getTransitionDurationFromElement = element => {
|
||||
if (!element) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Get transition-duration of the element
|
||||
let transitionDuration = window.getComputedStyle(element).transitionDuration
|
||||
const floatTransitionDuration = parseFloat(transitionDuration)
|
||||
|
||||
// Return 0 if element or transition duration is not found
|
||||
if (!floatTransitionDuration) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If multiple durations are defined, take the first
|
||||
transitionDuration = transitionDuration.split(',')[0]
|
||||
|
||||
return parseFloat(transitionDuration) * MILLISECONDS_MULTIPLIER
|
||||
}
|
||||
|
||||
const emulateTransitionEnd = (element, duration) => {
|
||||
let called = false
|
||||
const durationPadding = 5
|
||||
const emulatedDuration = duration + durationPadding
|
||||
function listener () {
|
||||
called = true
|
||||
element.removeEventListener(transitionEndEvent, listener)
|
||||
}
|
||||
|
||||
element.addEventListener(transitionEndEvent, listener)
|
||||
window.setTimeout(() => {
|
||||
if (!called) {
|
||||
element.dispatchEvent(WinEvent(transitionEndEvent))
|
||||
}
|
||||
|
||||
element.removeEventListener(transitionEndEvent, listener)
|
||||
}, emulatedDuration)
|
||||
}
|
||||
|
||||
const detectAnimation = (contentList, options) => {
|
||||
if (options.animation) {
|
||||
contentList.forEach(content => {
|
||||
content.classList.add(ClassName.FADE)
|
||||
content.classList.add(ClassName.NONE)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
show,
|
||||
ClassName,
|
||||
customProperty,
|
||||
detectAnimation
|
||||
}
|
||||
Reference in New Issue
Block a user