Skip to content

Activity Package

romainAA edited this page Aug 22, 2018 · 4 revisions

Structure

An activity package is an npm package which exports one or more activity types conforming to the ActivityPackageT specification. Each activity package carries with it its own metadata, configuration specification along with its version, its activity runner, and possibly a dashboard. Activity packages should live in ./ac/, and have an id and a directory name starting with ac-.

Add package to FROG

To make FROG aware of an activity package, it needs to be added to ./frog/package.json, as well as to ./frog/imports/activityTypes.js. This is done automatically by the createPackage.js script.

Type definition

The type definition of an activity type is as follows.

export type ActivityPackageT = {
  id: string,
  type: 'react-component',
  configVersion: number,
  upgradeFunctions?: { [version: string]: (Object) => Object },
  meta: {
    name: string,
    shortName?: string,
    shortDesc: string,
    description: string,
    exampleData?: {
      title: string,
      config?: Object,
      data?: any,
      learningItems?: any,
      type?: 'deeplink'
    }[],
    preview?: boolean
  },
  config: Object,
  configUI?: Object,
  dataStructure?: any,
  validateConfig?: validateConfigFnT[],
  mergeFunction?: (dataUnitStructT, Object, any, ?Object) => void,
  dashboards?: { [name: string]: DashboardT },
  exportData?: (config: Object, product: activityDataT) => string,
  formatProduct?: (
    config: Object,
    item: any,
    instanceId: string,
    username?: string
  ) => any,
  ConfigComponent?: React.ComponentType<{
    configData: Object,
    setConfigData: Object => void,
    formContext: Object
  }>,
  LearningItems?: LearningItemT<*>[]
};

This type definition should be explicitly imported into the index.js of an activity package, and used on the default export. Here is an example:

export default ({
  id: 'ac-video',
  type: 'react-component',
  configVersion: 1,
  meta,
  config,
  ActivityRunner,
  Dashboard: null,
  dataStructure,
  mergeFunction
}: ActivityPackageT);

This means that the shape of all the required or optional objects and functions are automatically checked, and ensures that you will find out if the central API changes in the future.

Metadata

All activity types have a unique id (id), which should be the same as the package directory name, and the package name in package.json (except in the case of a single activity package exporting several activity types).

Currently, the only supported type is 'react-component'.

The meta object contains the name, which will be shown in the graph editor and preview interface, a shortDesc and description, which will be shown in the activity picker in the graph editor, as well as an exampleData object.

Example data is used in the preview functionality (and might in the future be used for automatic testing). The structure should be for each array item, a title (shown in the preview picker), a config object, and a data object. For example from ac-chat:

const meta = {
  name: 'Chat',
  shortDesc: 'Chat component',
  description: 'Persistent text chat',
  exampleData: [
    {
      title: 'Empty chat',
      config: { title: 'Example chat' },
      data: []
    },
    {
      title: 'Chat with some messages',
      config: { title: 'Example chat' },
      data: [
        { id: '1', msg: 'This is the first message', user: 'Ole' },
        {
          id: '2',
          msg: "I don't agree, but we can discuss it",
          user: 'Petter'
        }
      ]
    }
  ]
};

Config

Activities are usually configurable - this could be necessary information, like the url for a video player, or modifying how the activity works, like whether the video should be autoplaying, whether the students should be allowed to enter new ideas in a brainstorming interface, etc. When the designer adds an activity to a graph, and chooses this activity type, they will be asked to configure the activity based on the configuration specification.

We use react-jsonschema-form to render the configuration forms. You can experiment with their live playground. In addition to the standard fields, we also offer a special fields, socialAttribute, which lets designers pick an incoming groupingKey that has not been used as the activity's primary groupingKey (see Flow of social structure. This could be used if you wanted students to see or have access to different actions based on their social structure.

Configuring display

You can configure the display of the form through configUI, for example to make fields conditional of other fields (ie. if condition is not fulfilled, a field will not be displayed). The condition can either be the string id of another field (which should be a boolean), or a function that takes formData as input, and outputs true/false. Example:

const configUI = {
  socialEdit: { conditional: 'formBoolean' },
  grouping: { conditional: formData => !formData.individual }
}

Validating

It is important to be able to validate the configuration of the activity, to make sure that activities will have the information it needs at run time. To do this, there are two approaches. The first is to add obligatory fields to the config definition itself. Here is an example:

const config = {
  type: 'object',
  required: ['text'],
  properties: {
    text: {
      type: 'string',
      title: 'Guidelines'
    },
    formBoolean: {
      type: 'boolean',
      title: 'Should students submit new ideas?',
      default: true
    }
  }
};

Fields are also automatically validated according to their types (string, number, etc). For more complex validations, we offer the field validateConfig, which should contain an array of functions taking formData as input, and returning either null, or an error object. Example:

const validateConfig = [
  data =>
    data.image && data.quadrants
      ? {
          err:
            'You cannot have both a background image and quadrants at the same time'
        }
      : null
];

Changing the format of the config

See Versioning Activities and Operators