Skip to content

Commit

Permalink
onStepChange added with tests and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Aapeli committed Aug 1, 2017
1 parent 60b914a commit d1e91a5
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 63 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -92,6 +92,9 @@ nextTextOnFinalActionStep: "Save"
// its recommended that you use basic javascript validation (i.e simple validation implemented inside your step component. But stepzilla steps can also use 'react-validation-mixin' which wraps your steps as higher order components. If you use this then you need to specify those steps indexes that use 'react-validation-mixin' below in this array)
hocValidationAppliedTo: [1, 2]
// function, which is called every time the index of the current step changes (it uses a zero based index)
onStepChange: (step) => console.log(step)
```

example options usage:
Expand Down
115 changes: 54 additions & 61 deletions dist/main.js
Expand Up @@ -4,8 +4,6 @@ Object.defineProperty(exports, "__esModule", {
value: true
});

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
Expand Down Expand Up @@ -172,72 +170,66 @@ var StepZilla = function (_Component) {
// a child step wants to invoke a jump between steps. in this case 'evt' is the numeric step number and not the JS event
this.setNavState(evt);
} else {
var _ret = function () {
// the main navigation step ui is invoking a jump between steps
if (!_this3.props.stepsNavigation || evt.target.value == _this3.state.compState) {
// if stepsNavigation is turned off or user clicked on existing step again (on step 2 and clicked on 2 again) then ignore
evt.preventDefault();
evt.stopPropagation();

return {
v: void 0
};
}
// the main navigation step ui is invoking a jump between steps
if (!this.props.stepsNavigation || evt.target.value == this.state.compState) {
// if stepsNavigation is turned off or user clicked on existing step again (on step 2 and clicked on 2 again) then ignore
evt.preventDefault();
evt.stopPropagation();

evt.persist(); // evt is a react event so we need to persist it as we deal with aync promises which nullifies these events (https://facebook.github.io/react/docs/events.html#event-pooling)
return;
}

var movingBack = evt.target.value < _this3.state.compState; // are we trying to move back or front?
var passThroughStepsNotValid = false; // if we are jumping forward, only allow that if inbetween steps are all validated. This flag informs the logic...
var proceed = false; // flag on if we should move on
evt.persist(); // evt is a react event so we need to persist it as we deal with aync promises which nullifies these events (https://facebook.github.io/react/docs/events.html#event-pooling)

_this3.abstractStepMoveAllowedToPromise(movingBack).then(function () {
var valid = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
// validation was a success (promise or sync validation). In it was a Promise's resolve() then proceed will be undefined, so make it true. Or else 'proceed' will carry the true/false value from sync v
proceed = valid;
var movingBack = evt.target.value < this.state.compState; // are we trying to move back or front?
var passThroughStepsNotValid = false; // if we are jumping forward, only allow that if inbetween steps are all validated. This flag informs the logic...
var proceed = false; // flag on if we should move on

if (!movingBack) {
_this3.updateStepValidationFlag(proceed);
}
this.abstractStepMoveAllowedToPromise(movingBack).then(function () {
var valid = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
// validation was a success (promise or sync validation). In it was a Promise's resolve() then proceed will be undefined, so make it true. Or else 'proceed' will carry the true/false value from sync v
proceed = valid;

if (proceed) {
if (!movingBack) {
// looks like we are moving forward, 'reduce' a new array of step>validated values we need to check and 'some' that to get a decision on if we should allow moving forward
passThroughStepsNotValid = _this3.props.steps.reduce(function (a, c, i) {
if (i >= _this3.state.compState && i < evt.target.value) {
a.push(c.validated);
}
return a;
}, []).some(function (c) {
return c === false;
});
}
}
}).catch(function (e) {
// Promise based validation was a fail (i.e reject())
if (!movingBack) {
_this3.updateStepValidationFlag(proceed);
}

if (proceed) {
if (!movingBack) {
_this3.updateStepValidationFlag(false);
}
}).then(function () {
// this is like finally(), executes if error no no error
if (proceed && !passThroughStepsNotValid) {
if (evt.target.value === _this3.props.steps.length - 1 && _this3.state.compState === _this3.props.steps.length - 1) {
_this3.setNavState(_this3.props.steps.length);
} else {
_this3.setNavState(evt.target.value);
}
}
}).catch(function (e) {
if (e) {
// see note below called "CatchRethrowing"
// ... plus the finally then() above is what throws the JS Error so we need to catch that here specifically
setTimeout(function () {
throw e;
// looks like we are moving forward, 'reduce' a new array of step>validated values we need to check and 'some' that to get a decision on if we should allow moving forward
passThroughStepsNotValid = _this3.props.steps.reduce(function (a, c, i) {
if (i >= _this3.state.compState && i < evt.target.value) {
a.push(c.validated);
}
return a;
}, []).some(function (c) {
return c === false;
});
}
});
}();

if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
}
}).catch(function (e) {
// Promise based validation was a fail (i.e reject())
if (!movingBack) {
_this3.updateStepValidationFlag(false);
}
}).then(function () {
// this is like finally(), executes if error no no error
if (proceed && !passThroughStepsNotValid) {
if (evt.target.value === _this3.props.steps.length - 1 && _this3.state.compState === _this3.props.steps.length - 1) {
_this3.setNavState(_this3.props.steps.length);
} else {
_this3.setNavState(evt.target.value);
}
}
}).catch(function (e) {
if (e) {
// see note below called "CatchRethrowing"
// ... plus the finally then() above is what throws the JS Error so we need to catch that here specifically
setTimeout(function () {
throw e;
});
}
});
}
}

Expand Down Expand Up @@ -485,5 +477,6 @@ StepZilla.propTypes = {
nextButtonCls: _propTypes2.default.string,
backButtonCls: _propTypes2.default.string,
backButtonText: _propTypes2.default.string,
hocValidationAppliedTo: _propTypes2.default.array
hocValidationAppliedTo: _propTypes2.default.array,
onStepChange: _propTypes2.default.func
};
2 changes: 2 additions & 0 deletions src/examples/Example.js
Expand Up @@ -55,6 +55,8 @@ export default class Example extends Component {
preventEnterSubmission={true}
nextTextOnFinalActionStep={"Save"}
hocValidationAppliedTo={[3]}
startAtStep={window.sessionStorage.getItem('step') ? parseFloat(window.sessionStorage.getItem('step')) : 0}
onStepChange={(step) => window.sessionStorage.setItem('step', step)}
/>
</div>
</div>
Expand Down
8 changes: 7 additions & 1 deletion src/examples/Step1.js
Expand Up @@ -30,7 +30,13 @@ export default class Step1 extends Component {
<div className="col-md-12">
<div className="col-md-6">
<h3>This example uses this custom config (which overwrites the default config):</h3>
<code>preventEnterSubmission=true<br />nextTextOnFinalActionStep="Save"<br />hocValidationAppliedTo=[3]</code>
<code>
preventEnterSubmission=true<br />
nextTextOnFinalActionStep="Save"<br />
hocValidationAppliedTo=[3]<br />
startAtStep=window.sessionStorage.getItem('step') ? parseFloat(window.sessionStorage.getItem('step')) : 0<br />
onStepChange=(step) => window.sessionStorage.setItem('step', step)
</code>
</div>
<div className="col-md-6">
<h3>The default config settings are...</h3>
Expand Down
6 changes: 5 additions & 1 deletion src/main.js
Expand Up @@ -90,6 +90,9 @@ export default class StepZilla extends Component {

// which step are we in?
checkNavState(currentStep) {
if (this.props.onStepChange) {
this.props.onStepChange(currentStep);
}
this.setState(this.getPrevNextBtnState(currentStep));
}

Expand Down Expand Up @@ -372,5 +375,6 @@ StepZilla.propTypes = {
nextButtonCls: PropTypes.string,
backButtonCls: PropTypes.string,
backButtonText: PropTypes.string,
hocValidationAppliedTo: PropTypes.array
hocValidationAppliedTo: PropTypes.array,
onStepChange: PropTypes.func
}
36 changes: 36 additions & 0 deletions tests/main.spec.js
@@ -1,5 +1,6 @@
import React from 'react';
import StepZilla from '../src/main';
import sinon from 'sinon'
const shallow = enzyme.shallow;

const makeFakeSteps = (num, makePure) => {
Expand Down Expand Up @@ -397,5 +398,40 @@ describe('StepZilla', () => {
expect(enzymeWrapper.find('.progtrckr').childAt(2).hasClass('progtrckr-doing')).to.be.true;
});
});

describe('onStepChange: not null use case', () => {
let onStepChange;
let enzymeWrapper;

beforeEach(() => {
onStepChange = sinon.spy();
enzymeWrapper = setup(3, {
startAtStep: 1,
onStepChange
}).enzymeWrapper;
})

it('should call onStepChange when clicked to next step', (done) => {

enzymeWrapper.find('.footer-buttons #next-button').simulate('click');

// click above is promise driven so it's async, setTimeout is probably not the best way to do this but it will do for now
setTimeout(() => {
expect(onStepChange.calledWith(2)).to.be.true;
done();
}, 10);
});

it('should call onStepChange when clicked to previous step', (done) => {

enzymeWrapper.find('.footer-buttons #prev-button').simulate('click');

// click above is promise driven so it's async, setTimeout is probably not the best way to do this but it will do for now
setTimeout(() => {
expect(onStepChange.calledWith(0)).to.be.true;
done();
}, 10);
});
})
}); // end - custom props based render group
});

0 comments on commit d1e91a5

Please sign in to comment.