packs-framework

2.0.10 • Public • Published

Packs Framework ✌️

The packs framework provides state management and a React-based container for loading packs survey components. The framework takes in a list of components, declarative JSON "modules" (one for each survey screen), an initial state object, a "wrapper" component for wrapping components across all screens, and a recorder function to store responses. It's recommended to use the packs cli tool for generating and managing surveys, instead of directly using the framework.

Packs components

Packs components are the building blocks of a packs app. Each component returns response data that advances the survey to the next screen, or survey "module", using the push function exposed from the framework.

Here is a simplified example of a component that asks for a binary response, 'A' or 'B' using radio buttons:

({ name='qux', options=['A', 'B'], push }) => (
  <div>
  {
    options.map((option, i) => (
      <label
        key={i}
        onClick={() => push({ [name]: option })}
      >
        {option}
        <input type="radio" />
      </label>
    ))
  }
  </div>
)

Each component can access variables from the global state / response object using a special $ syntax in its props, which is explained further below.

JSON modules

Each survey screen is defined by a declarative JSON object that references a specific component and provides parameters as props to the component. The following example references a component similar to the example component above, in the npm package "your-components". This component will return the response to the global state / response object under the variable "indent_preference" which can later be referenced inside props using the syntax "$indent_preference" or, embedded in a string, as "Really? You prefer ${indent_preference}??".

Example component:

{
  "component": "your-components/ChooseOne",
  "name": "indent_preference",
  "text": "Which do you prefer?",
  "options": [
    "Tabs",
    "Spaces"
  ]
}

Later example:

{
  "component": "your-components/Message",
  "text": "Really? You prefer ${indent_preference}??"
}

State management

The framework maintains an atomic state object that components can write to, in order to unmount, and read from during mounting. This works because surveys are generally a linear process. The StateList state manager also handles browser history between survey modules automatically. It also provides a temporary screenState object that modules can write to during instantiation through the exposed set function, which will temporarily overwrite the global state object. For example, a component with a text input could update itself through the set function:

const component = ({ value, set }) => (
  <input
    type="text"
    value={value}
    onChange={e => set({ v: e.target.value })}
  />
)

component.defaultProps = {
  value: '$v'
}

Afterwards the framework will reset the screenState object for the next module.

Recorder function

The framework uses a recorder function for storing response data which you can customize and pass in. The recorder function is initialized on page load and called after each push call. Here is a simplified example:

const recorder = (endpoint, responseObject, index) => {
  // you must initialize a session "key" so that the framework doesn't keep trying to initialize
  sessionStorage.setItem("key", "INITIALIZED");

  return new Promise((reject, resolve) => {
    // handle the responseObject
    resolve();
  });
}

A response object looks like:

{
  data: [{
    module_0_component: 'your-components/ChooseOne',
    module_0_t: 1508503142793,
    indent_preference: 'Spaces'
  }],
  screenState: [
    {
      indent_preference: 'Tabs',
      timestamp: 1508503141700
    },
    {
      indent_preference: 'Spaces',
      timestamp: 1508503142100
    }
  ],
  silent: false
}

The data field is an array in case the user goes back and changes their answer.

Silent components

Silent components are renderless components that simply process data, for example randomizing two states:

{
  "component": "packs-components/Randomize",
  "states": [
    { "coin": true },
    { "coin": false }
  ]
}

These components must pass a silent flag, by passing true as the second argument when calling push to ensure that StateList doesn't create a new history entry (or else going back wouldn't work since the component instantly returns). For example:

push({ coin: Math.random() > 0.5 }, true);

Wrapper component

A wrapper component is a special container component to style and house a survey. It can access the survey state and display a progress bar or other useful information.

Advanced

pack function

The pack function is a higher-order function that takes in a set of components and returns a special function that takes an initial state object, modules, element id where to mount, wrapper component and recorder function. It's recommended to use the packs cli tool instead of using this function directly.

Simplified example:

import { pack } from 'packs-framework'
import components from 'my-components'
import wrapper from 'my-wrapper'
import recorder from 'my-recorder'

const run = pack(components)

const initialState = {
  start: Date.now()
}

const modules = [
  {
    "component": "Markdown",
    "source": "### Instructions\nPlease be honest"
  },
  {
    "component": "ChooseOne",
    "name": "indent_preference",
    "text": "Which do you prefer?",
    "options": [
      "Tabs",
      "Spaces"
    ]
  },
  {
    "component": "NumberInput",
    "text": "How many spaces?",
    "condition": {
      "==": ["$indent_preference", "Spaces"]
    }
  }
]

const elementId = 'root' // id of div to mount app

run(initialState, modules, elementId, wrapper, recorder)

Browser support

Packwrap currently targets IE10+ and evergreen browsers. Compatibility table coming soon...

Thanks to BrowserStack for providing cross-browser testing for this project. They are an excellent service for testing your surveys across many browsers and devices and support open-source projects. browserstack

Version history

2.0.10

  • queueUrl may not exist (undefined).

2.0.9

  • Adding config.

2.0.8

  • Adding queueUrl.

2.0.7

  • update yarn

2.0.6

  • save module name

2.0.5

  • Remove lib from git, use eslint.

2.0.4

  • lib/stateList.js was in camelcase for some reason, change to lib/StateList.js as it should be.

2.0.3

  • Undo the changes in 2.0.2 which were done by accident.

2.0.2

  • Move various Babel dependencies from dev-dependencies, because they are needed when packing surveys.

2.0.1

Last Robbie version

Readme

Keywords

none

Package Sidebar

Install

npm i packs-framework

Weekly Downloads

0

Version

2.0.10

License

MIT

Unpacked Size

79.8 kB

Total Files

24

Last publish

Collaborators

  • zmbq
  • talyak
  • deborahn
  • dleksanov