node package manager
Easy collaboration. Discover, share, and reuse code in your team. Create a free org »

simple-ui_workflow

Workflow

Coverage Status Build Status Dependency Status npm version

simple-ui_workflow

Workflow is a utility to model logic-driven, state-based workflows. Workflow is used to define a series of steps where step progression can change dynamically. One sequence of work exists as an exterior index for the end-user (in the interface) and an interior index to control a more complex process workflow. A Workflow is an object which encapsulates all logic related to a set of steps and so provides helper methods to query the state of the workflow, to support easy interface control for templates.

Install

Install with npm.

npm install --save simple-ui_workflow

Install with bower.

bower install --save simple-ui_workflow

Lodash Dependency

This library requires a small set of lodash. Use lodash-modularize to limit how much of lodash is included in your project.

Quick Usage

import Workflow from "simple-ui_workflow"
 
var workflow = Workflow(['one', 'two', 'three']);
 
// many is* methods to control interface state 
assert(workflow.current() === 'one');
assert(workflow.isBeforeLast() === true);
 
// next will return the next value and advance the internal index 
assert(workflow.index === 0);
assert(workflow.next() === 'two');
assert(workflow.index === 1);

Usage

Workflow Scenarios

The following example(s) demonstrate the power of a Workflow and usefulness of managing two indices.

Form Validation

Every page/view within a multi-part form should have a validation method.

var workflow = Workflow({
  'step1': {
    // NOTE 'step1' is not valid, so the rule will keep the Workflow at index 0 
    isValid: function() { return false; },
    content: {}
  },
  'step2': {
    isValid: function() { return true; },
    content: {}
  }
});
 
// Rule: if the current view is not valid, assign the workflow to the current workflow 
workflow.reflow().rule(function(requestedIndex, requestedItem) {
  if (!this.current().isValid()) {
    return this.index;
  }
});
 
workflow.next(); // same as workflow.index = 1 (or any other index) 
workflow.index === 0; // the valid method at index 0, prevents progression 

User Permission

A Workflow can be used to construct the steps presented to an end-user.

var workflow = Workflow({
  'step1': {
    isPermitted: true,
    content: {}
  },
  'step2': {
    isPermitted: false,
    content: {}
  },
  'step3': {
    isPermitted: true,
    content: {}
  }
});
 
// Rule: Scan to next/previous permitted item 
workflow.reflow().rule(function(requestedIndex) {
 
  return this.scanIndex(function(scanIndex, scanItem) {
    return scanItem.isPermitted;
  }, (requestedIndex < this.index) ? -1 : 1);
 
});
 
workflow.next();
workflow.next();
workflow.index === 2; // it scanned to the next permitted item and not beyond 
 
workflow.previous();
workflow.previous();
workflow.index === 0; // it scanned to the previous permitted item and not beyond 

Isolated Parts

A Workflow may be initialized with multiple sets of reflow objects each defining progression rules. It is useful when providing a Workflow to a component that needs to swap progression rules based on application modes.

var workflow = Workflow([1, 2, 3, 4, 5, 6, 7, 8, 9]);
 
// Apply a reflow with no rules, normal +1 rules for next() apply 
workflow.reflow('normal-progression');
workflow.first();
workflow.next();
workflow.next();
 
// Apply a reflow with skipping odd 
workflow.reflow('skip-odd').rule(function(requestIndex) {
  return (requestIndex % 2 === 0)
    ? requestIndex
    : requestIndex + 1;
});
 
workflow.reflowWith('skip-odd');
workflow.first();
workflow.next();
workflow.next();
 
// Apply a reflow object directly 
workflow.reflowWith(workflow.reflow().rule(function(requestIndex) {
  return requestIndex + 1;
}));

Workflow API

Initialize a Workflow

A Workflow can progress over the following types:

  • integer which is converted to an array [0, 1, 2, 3, ..., <integer>]
  • array
  • object
assert((Workflow(3)).size() === 3);
assert((Workflow([2, 3, 4])).size() === 3);
assert((Workflow({ '3':4, '5':4, '1':2 })).size() === 3);

The Workflow object also doubles as a factory for itself.

var workflow = Workflow();
var workflow = Workflow.create();

Moving through a Workflow

A Workflow keeps track of an internal index. By default a Workflow maps it's index directly to the underlying source. With an array Workflow, if index = 2, the current() you return the item at index 2.

var workflow = Workflow([2, 3, 4]);
 
workflow.firstIndex() === 0;       // always 0 
workflow.lastIndex() === 2;        // last index of an array of 3 
workflow.index === 0;              // defaults to 0 
workflow.isFirst() === true;       // index is at 0 which is first 
workflow.isLast() === false;       // index is at 0 which is not the last 
workflow.isAfterFirst() === false  // index is at first, so it cannot be after it 
workflow.isBeforeLast() === true   // index is less than lastIndex() 

These methods are most useful in a template, this example uses an imaginary templating language.

<section class="wizard">
  {{ customViewAtIndex(workflow.index) }}
  <button *-show="workflow.isAfterFirst()">Previous</button>
  <button *-show="workflow.isBeforeLast()"
          *-click="workflow.next()">
    Next
  </button>
</section>

A Workflow bounds the index.

var workflow = Workflow([2, 3, 4]);
 
workflow.index = 1;
workflow.index === 1;
// assign an index under the first index, so we set to first 
workflow.index = -3;
workflow.index === 0;
// assign an index over the last index, so we set to last 
workflow.index = 6;
workflow.index === 2;
// assign a non-numerical index, leave the index at the same location 
workflow.index = 'as';
workflow.index === 2;

Workflows of Objects

The object stored at the index can be retrieved too.

var workflow = Workflow(['two', 'three', 'four']);
 
workflow.current() === 'two';  // defaults to 'two' 
workflow.peekFirst() === 'two';    // always the first object 
workflow.peekLast() === 'four';     // always the last object 

Workflow Size

The size of the object being progressed over.

var workflow = Workflow([2, 3, 4]);
 
workflow.length === 3;
workflow.size() === 3;

License

MIT © mwjaworski

This software is released under the MIT license:

Copyright (c) 2017 mwjaworski mjaworski@acm.org

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.))