sanity-plugin-workflow 1.0.0-beta.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -135,58 +135,99 @@ function UserAssignment(props) {
135
135
  onRemove: removeAssignee
136
136
  });
137
137
  }
138
- function useWorkflowMetadata(id, states) {
138
+ function useWorkflowMetadata(ids) {
139
139
  const {
140
- data: metadata,
140
+ data: rawData,
141
141
  loading,
142
142
  error
143
- } = sanityPluginUtils.useListeningQuery("*[_type == \"workflow.metadata\" && documentId == $id][0]", {
143
+ } = sanityPluginUtils.useListeningQuery("*[_type == \"workflow.metadata\" && documentId in $ids]{\n _id,\n _type,\n _rev,\n assignees,\n documentId,\n state,\n orderRank\n }", {
144
144
  params: {
145
- id
145
+ ids
146
+ },
147
+ options: {
148
+ apiVersion: API_VERSION
146
149
  }
147
150
  });
148
- if (metadata == null ? void 0 : metadata.state) {
149
- return {
150
- data: {
151
- metadata,
152
- state: states.find(s => s.id === metadata.state)
153
- },
154
- loading,
155
- error
156
- };
157
- }
151
+ const keyedMetadata = React.useMemo(() => {
152
+ if (!rawData || rawData.length === 0) return {};
153
+ return rawData.reduce((acc, cur) => {
154
+ return {
155
+ ...acc,
156
+ [cur.documentId]: cur
157
+ };
158
+ }, {});
159
+ }, [rawData]);
158
160
  return {
159
- data: {},
161
+ data: keyedMetadata,
160
162
  loading,
161
163
  error
162
164
  };
163
165
  }
164
- function AssignWorkflow(props, states) {
165
- var _a, _b;
166
+ const WorkflowContext = React.createContext({
167
+ data: {},
168
+ loading: false,
169
+ error: false,
170
+ ids: [],
171
+ addId: () => null,
172
+ removeId: () => null,
173
+ ...DEFAULT_CONFIG
174
+ });
175
+ function useWorkflowContext(id) {
176
+ const current = React.useContext(WorkflowContext);
177
+ return {
178
+ ...current,
179
+ metadata: id ? current.data[id] : null
180
+ };
181
+ }
182
+ function WorkflowProvider(props) {
183
+ const [ids, setIds] = React.useState([]);
184
+ const addId = React.useCallback(id => setIds(current => current.includes(id) ? current : [...current, id]), []);
185
+ const removeId = React.useCallback(id => setIds(current => current.filter(i => i !== id)), []);
186
+ const {
187
+ data,
188
+ loading,
189
+ error
190
+ } = useWorkflowMetadata(ids);
191
+ return /* @__PURE__ */jsxRuntime.jsx(WorkflowContext.Provider, {
192
+ value: {
193
+ data,
194
+ loading,
195
+ error,
196
+ ids,
197
+ addId,
198
+ removeId,
199
+ states: props.workflow.states,
200
+ schemaTypes: props.workflow.schemaTypes
201
+ },
202
+ children: props.renderDefault(props)
203
+ });
204
+ }
205
+ function AssignWorkflow(props) {
206
+ var _a;
166
207
  const {
167
208
  id
168
209
  } = props;
210
+ const {
211
+ metadata,
212
+ loading,
213
+ error
214
+ } = useWorkflowContext(id);
169
215
  const [isDialogOpen, setDialogOpen] = React.useState(false);
170
216
  const userList = sanityPluginUtils.useProjectUsers({
171
217
  apiVersion: API_VERSION
172
218
  });
173
- const {
174
- data,
175
- loading,
176
- error
177
- } = useWorkflowMetadata(id, states);
178
219
  if (error) {
179
220
  console.error(error);
180
221
  }
181
- if (!(data == null ? void 0 : data.metadata)) {
222
+ if (!metadata) {
182
223
  return null;
183
224
  }
184
225
  return {
185
226
  icon: icons.UsersIcon,
186
227
  type: "dialog",
187
- disabled: !data || loading || error,
228
+ disabled: !metadata || loading || error,
188
229
  label: "Assign",
189
- title: data ? null : "Document is not in Workflow",
230
+ title: metadata ? null : "Document is not in Workflow",
190
231
  dialog: isDialogOpen && {
191
232
  type: "popover",
192
233
  onClose: () => {
@@ -194,7 +235,7 @@ function AssignWorkflow(props, states) {
194
235
  },
195
236
  content: /* @__PURE__ */jsxRuntime.jsx(UserAssignment, {
196
237
  userList,
197
- assignees: (_b = (_a = data.metadata) == null ? void 0 : _a.assignees) != null ? _b : [],
238
+ assignees: ((_a = metadata == null ? void 0 : metadata.assignees) == null ? void 0 : _a.length) > 0 ? metadata.assignees : [],
198
239
  documentId: id
199
240
  })
200
241
  },
@@ -203,16 +244,17 @@ function AssignWorkflow(props, states) {
203
244
  }
204
245
  };
205
246
  }
206
- function BeginWorkflow(props, states) {
247
+ function BeginWorkflow(props) {
207
248
  const {
208
249
  id,
209
250
  draft
210
251
  } = props;
211
252
  const {
212
- data,
253
+ metadata,
213
254
  loading,
214
- error
215
- } = useWorkflowMetadata(id, states);
255
+ error,
256
+ states
257
+ } = useWorkflowContext(id);
216
258
  const client = sanity.useClient({
217
259
  apiVersion: API_VERSION
218
260
  });
@@ -233,10 +275,6 @@ function BeginWorkflow(props, states) {
233
275
  documentId: id,
234
276
  state: states[0].id,
235
277
  orderRank: lowestOrderFirstState ? lexorank.LexoRank.parse(lowestOrderFirstState).genNext().toString() : lexorank.LexoRank.min().toString()
236
- },
237
- // Faster!
238
- {
239
- visibility: "async"
240
278
  }).then(() => {
241
279
  toast.push({
242
280
  status: "success",
@@ -247,29 +285,29 @@ function BeginWorkflow(props, states) {
247
285
  setComplete(true);
248
286
  });
249
287
  }, [id, states, client, toast]);
250
- if (!draft || complete || data.metadata) {
288
+ if (!draft || complete || metadata) {
251
289
  return null;
252
290
  }
253
291
  return {
254
292
  icon: icons.SplitVerticalIcon,
255
293
  type: "dialog",
256
- disabled: (data == null ? void 0 : data.metadata) || loading || error || beginning || complete,
294
+ disabled: metadata || loading || error || beginning || complete,
257
295
  label: beginning ? "Beginning..." : "Begin Workflow",
258
296
  onHandle: () => {
259
297
  handle();
260
298
  }
261
299
  };
262
300
  }
263
- function CompleteWorkflow(props, states) {
264
- var _a;
301
+ function CompleteWorkflow(props) {
265
302
  const {
266
303
  id
267
304
  } = props;
268
305
  const {
269
- data,
306
+ metadata,
270
307
  loading,
271
- error
272
- } = useWorkflowMetadata(id, states);
308
+ error,
309
+ states
310
+ } = useWorkflowContext(id);
273
311
  const client = sanity.useClient({
274
312
  apiVersion: API_VERSION
275
313
  });
@@ -279,10 +317,11 @@ function CompleteWorkflow(props, states) {
279
317
  const handle = React.useCallback(() => {
280
318
  client.delete("workflow-metadata.".concat(id));
281
319
  }, [id, client]);
282
- const isLastState = ((_a = data == null ? void 0 : data.state) == null ? void 0 : _a.id) === states[states.length - 1].id;
283
- if (!data.metadata) {
320
+ if (!metadata) {
284
321
  return null;
285
322
  }
323
+ const state = states.find(s => s.id === metadata.state);
324
+ const isLastState = (state == null ? void 0 : state.id) === states[states.length - 1].id;
286
325
  return {
287
326
  icon: icons.CheckmarkIcon,
288
327
  type: "dialog",
@@ -298,7 +337,7 @@ function CompleteWorkflow(props, states) {
298
337
  function arraysContainMatchingString(one, two) {
299
338
  return one.some(item => two.includes(item));
300
339
  }
301
- function UpdateWorkflow(props, allStates, actionState) {
340
+ function UpdateWorkflow(props, actionState) {
302
341
  var _a, _b, _c, _d;
303
342
  const {
304
343
  id,
@@ -311,16 +350,15 @@ function UpdateWorkflow(props, allStates, actionState) {
311
350
  const toast = ui.useToast();
312
351
  const currentUser = sanity.useCurrentUser();
313
352
  const {
314
- data,
353
+ metadata,
315
354
  loading,
316
- error
317
- } = useWorkflowMetadata(id, allStates);
318
- const {
319
- state: currentState
320
- } = data;
355
+ error,
356
+ states
357
+ } = useWorkflowContext(id);
358
+ const currentState = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
321
359
  const {
322
360
  assignees = []
323
- } = (_a = data == null ? void 0 : data.metadata) != null ? _a : {};
361
+ } = metadata != null ? metadata : {};
324
362
  const {
325
363
  validation,
326
364
  isValidating
@@ -347,21 +385,21 @@ function UpdateWorkflow(props, allStates, actionState) {
347
385
  });
348
386
  });
349
387
  };
350
- if (!data.metadata || currentState && currentState.id === actionState.id) {
388
+ if (!metadata || currentState && currentState.id === actionState.id) {
351
389
  return null;
352
390
  }
353
- const currentStateIndex = allStates.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
354
- const actionStateIndex = allStates.findIndex(s => s.id === actionState.id);
391
+ const currentStateIndex = states.findIndex(s => s.id === (currentState == null ? void 0 : currentState.id));
392
+ const actionStateIndex = states.findIndex(s => s.id === actionState.id);
355
393
  const direction = actionStateIndex > currentStateIndex ? "promote" : "demote";
356
394
  const DirectionIcon = direction === "promote" ? icons.ArrowRightIcon : icons.ArrowLeftIcon;
357
395
  const directionLabel = direction === "promote" ? "Promote" : "Demote";
358
- const userRoleCanUpdateState = ((_b = user == null ? void 0 : user.roles) == null ? void 0 : _b.length) && ((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) ?
396
+ const userRoleCanUpdateState = ((_a = user == null ? void 0 : user.roles) == null ? void 0 : _a.length) && ((_b = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _b.length) ?
359
397
  // If the Action state is limited to specific roles
360
398
  // check that the current user has one of those roles
361
399
  arraysContainMatchingString(user.roles.map(r => r.name), actionState.roles) :
362
400
  // No roles specified on the next state, so anyone can update
363
- ((_d = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _d.length) !== 0;
364
- const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && currentState.transitions.length ?
401
+ ((_c = actionState == null ? void 0 : actionState.roles) == null ? void 0 : _c.length) !== 0;
402
+ const actionStateIsAValidTransition = (currentState == null ? void 0 : currentState.id) && ((_d = currentState == null ? void 0 : currentState.transitions) == null ? void 0 : _d.length) ?
365
403
  // If the Current State limits transitions to specific States
366
404
  // Check that the Action State is in Current State's transitions array
367
405
  currentState.transitions.includes(actionState.id) :
@@ -370,7 +408,7 @@ function UpdateWorkflow(props, allStates, actionState) {
370
408
  const userAssignmentCanUpdateState = actionState.requireAssignment ?
371
409
  // If the Action State requires assigned users
372
410
  // Check the current user ID is in the assignees array
373
- currentUser && assignees.length && assignees.includes(currentUser.id) :
411
+ currentUser && (assignees == null ? void 0 : assignees.length) && assignees.includes(currentUser.id) :
374
412
  // Otherwise this isn't a problem
375
413
  true;
376
414
  let title = "".concat(directionLabel, " State to \"").concat(actionState.title, "\"");
@@ -393,16 +431,13 @@ function UpdateWorkflow(props, allStates, actionState) {
393
431
  onHandle: () => onHandle(id, actionState)
394
432
  };
395
433
  }
396
- function AssigneesBadge(states, documentId, currentUser) {
434
+ function AssigneesBadge(documentId, currentUser) {
397
435
  var _a;
398
436
  const {
399
- data,
437
+ metadata,
400
438
  loading,
401
439
  error
402
- } = useWorkflowMetadata(documentId, states);
403
- const {
404
- metadata
405
- } = data;
440
+ } = useWorkflowContext(documentId);
406
441
  const userList = sanityPluginUtils.useProjectUsers({
407
442
  apiVersion: API_VERSION
408
443
  });
@@ -438,15 +473,14 @@ function AssigneesBadge(states, documentId, currentUser) {
438
473
  color: "primary"
439
474
  };
440
475
  }
441
- function StateBadge(states, documentId) {
476
+ function StateBadge(documentId) {
442
477
  const {
443
- data,
478
+ metadata,
444
479
  loading,
445
- error
446
- } = useWorkflowMetadata(documentId, states);
447
- const {
448
- state
449
- } = data;
480
+ error,
481
+ states
482
+ } = useWorkflowContext(documentId);
483
+ const state = states.find(s => s.id === (metadata == null ? void 0 : metadata.state));
450
484
  if (loading || error) {
451
485
  if (error) {
452
486
  console.error(error);
@@ -462,6 +496,25 @@ function StateBadge(states, documentId) {
462
496
  color: state == null ? void 0 : state.color
463
497
  };
464
498
  }
499
+ function WorkflowSignal(props) {
500
+ var _a;
501
+ const documentId = ((_a = props == null ? void 0 : props.value) == null ? void 0 : _a._id) ? props.value._id.replace("drafts.", "") : null;
502
+ const {
503
+ addId,
504
+ removeId
505
+ } = useWorkflowContext();
506
+ React.useEffect(() => {
507
+ if (documentId) {
508
+ addId(documentId);
509
+ }
510
+ return () => {
511
+ if (documentId) {
512
+ removeId(documentId);
513
+ }
514
+ };
515
+ }, [documentId, addId, removeId]);
516
+ return props.renderDefault(props);
517
+ }
465
518
  function EditButton(props) {
466
519
  const {
467
520
  id,
@@ -1072,7 +1125,8 @@ function DocumentCard(props) {
1072
1125
  children: [/* @__PURE__ */jsxRuntime.jsx(ui.Box, {
1073
1126
  flex: 1,
1074
1127
  children: /* @__PURE__ */jsxRuntime.jsx(sanity.Preview, {
1075
- layout: "block",
1128
+ layout: "default",
1129
+ skipVisibilityCheck: true,
1076
1130
  value: item,
1077
1131
  schemaType: schema.get(item._type)
1078
1132
  })
@@ -1126,6 +1180,21 @@ function DocumentCard(props) {
1126
1180
  })]
1127
1181
  });
1128
1182
  }
1183
+ function getStyle(draggableStyle, virtualItem) {
1184
+ let transform = "translateY(".concat(virtualItem.start, "px)");
1185
+ if (draggableStyle && draggableStyle.transform) {
1186
+ const draggableTransformY = parseInt(draggableStyle.transform.split(",")[1].split("px")[0], 10);
1187
+ transform = "translateY(".concat(virtualItem.start + draggableTransformY, "px)");
1188
+ }
1189
+ return {
1190
+ position: "absolute",
1191
+ top: 0,
1192
+ left: 0,
1193
+ width: "100%",
1194
+ height: "".concat(virtualItem.size, "px"),
1195
+ transform
1196
+ };
1197
+ }
1129
1198
  function DocumentList(props) {
1130
1199
  const {
1131
1200
  data = [],
@@ -1144,17 +1213,20 @@ function DocumentList(props) {
1144
1213
  return data.length ? filterItemsAndSort(data, state.id, selectedUserIds, selectedSchemaTypes) : [];
1145
1214
  }, [data, selectedSchemaTypes, selectedUserIds, state.id]);
1146
1215
  const parentRef = React.useRef(null);
1147
- const rowVirtualizer = reactVirtual.useVirtualizer({
1148
- count: data.length,
1216
+ const virtualizer = reactVirtual.useVirtualizer({
1217
+ count: dataFiltered.length,
1149
1218
  getScrollElement: () => parentRef.current,
1150
1219
  getItemKey: index => {
1151
1220
  var _a, _b, _c;
1152
1221
  return (_c = (_b = (_a = dataFiltered[index]) == null ? void 0 : _a._metadata) == null ? void 0 : _b.documentId) != null ? _c : index;
1153
1222
  },
1154
- estimateSize: () => 113,
1155
- overscan: 10
1223
+ estimateSize: () => 115,
1224
+ overscan: 7,
1225
+ measureElement: element => {
1226
+ return element.getBoundingClientRect().height || 115;
1227
+ }
1156
1228
  });
1157
- if (!data.length) {
1229
+ if (!data.length || !dataFiltered.length) {
1158
1230
  return null;
1159
1231
  }
1160
1232
  return /* @__PURE__ */jsxRuntime.jsx("div", {
@@ -1162,44 +1234,53 @@ function DocumentList(props) {
1162
1234
  style: {
1163
1235
  height: "100%",
1164
1236
  overflow: "auto",
1165
- paddingTop: 1,
1166
1237
  // Smooths scrollbar behaviour
1167
1238
  overflowAnchor: "none",
1168
- scrollBehavior: "auto"
1239
+ scrollBehavior: "auto",
1240
+ paddingTop: 1
1169
1241
  },
1170
- children: rowVirtualizer.getVirtualItems().map(virtualItem => {
1171
- var _a;
1172
- const item = dataFiltered[virtualItem.index];
1173
- const {
1174
- documentId,
1175
- assignees
1176
- } = (_a = item == null ? void 0 : item._metadata) != null ? _a : {};
1177
- if (!documentId) {
1178
- return null;
1179
- }
1180
- const isInvalid = invalidDocumentIds.includes(documentId);
1181
- const meInAssignees = (user == null ? void 0 : user.id) ? assignees == null ? void 0 : assignees.includes(user.id) : false;
1182
- const isDragDisabled = patchingIds.includes(documentId) || !userRoleCanDrop || isInvalid || !(state.requireAssignment ? state.requireAssignment && meInAssignees : true);
1183
- return /* @__PURE__ */jsxRuntime.jsx(dnd.Draggable, {
1184
- draggableId: documentId,
1185
- index: virtualItem.index,
1186
- isDragDisabled,
1187
- children: (draggableProvided, draggableSnapshot) => /* @__PURE__ */jsxRuntime.jsx("div", {
1188
- ref: draggableProvided.innerRef,
1189
- ...draggableProvided.draggableProps,
1190
- ...draggableProvided.dragHandleProps,
1191
- children: /* @__PURE__ */jsxRuntime.jsx(DocumentCard, {
1192
- userRoleCanDrop,
1193
- isDragDisabled,
1194
- isPatching: patchingIds.includes(documentId),
1195
- isDragging: draggableSnapshot.isDragging,
1196
- item,
1197
- toggleInvalidDocumentId,
1198
- userList,
1199
- states
1242
+ children: /* @__PURE__ */jsxRuntime.jsx("div", {
1243
+ style: {
1244
+ height: "".concat(virtualizer.getTotalSize(), "px"),
1245
+ width: "100%",
1246
+ position: "relative"
1247
+ },
1248
+ children: virtualizer.getVirtualItems().map(virtualItem => {
1249
+ var _a;
1250
+ const item = dataFiltered[virtualItem.index];
1251
+ const {
1252
+ documentId,
1253
+ assignees
1254
+ } = (_a = item == null ? void 0 : item._metadata) != null ? _a : {};
1255
+ const isInvalid = invalidDocumentIds.includes(documentId);
1256
+ const meInAssignees = (user == null ? void 0 : user.id) ? assignees == null ? void 0 : assignees.includes(user.id) : false;
1257
+ const isDragDisabled = patchingIds.includes(documentId) || !userRoleCanDrop || isInvalid || !(state.requireAssignment ? state.requireAssignment && meInAssignees : true);
1258
+ return /* @__PURE__ */jsxRuntime.jsx(dnd.Draggable, {
1259
+ draggableId: documentId,
1260
+ index: virtualItem.index,
1261
+ isDragDisabled,
1262
+ children: (draggableProvided, draggableSnapshot) => /* @__PURE__ */jsxRuntime.jsx("div", {
1263
+ ref: draggableProvided.innerRef,
1264
+ ...draggableProvided.draggableProps,
1265
+ ...draggableProvided.dragHandleProps,
1266
+ style: getStyle(draggableProvided.draggableProps.style, virtualItem),
1267
+ children: /* @__PURE__ */jsxRuntime.jsx("div", {
1268
+ ref: virtualizer.measureElement,
1269
+ "data-index": virtualItem.index,
1270
+ children: /* @__PURE__ */jsxRuntime.jsx(DocumentCard, {
1271
+ userRoleCanDrop,
1272
+ isDragDisabled,
1273
+ isPatching: patchingIds.includes(documentId),
1274
+ isDragging: draggableSnapshot.isDragging,
1275
+ item,
1276
+ toggleInvalidDocumentId,
1277
+ userList,
1278
+ states
1279
+ })
1280
+ })
1200
1281
  })
1201
- })
1202
- }, virtualItem.key);
1282
+ }, virtualItem.key);
1283
+ })
1203
1284
  })
1204
1285
  });
1205
1286
  }
@@ -1926,7 +2007,6 @@ function WorkflowTool(props) {
1926
2007
  ref: provided.innerRef,
1927
2008
  tone: snapshot.isDraggingOver ? "primary" : defaultCardTone,
1928
2009
  height: "fill",
1929
- paddingTop: 1,
1930
2010
  children: [loading ? /* @__PURE__ */jsxRuntime.jsx(ui.Flex, {
1931
2011
  padding: 5,
1932
2012
  align: "center",
@@ -1974,7 +2054,10 @@ const workflow = sanity.definePlugin(function () {
1974
2054
  ...config
1975
2055
  };
1976
2056
  if (!(states == null ? void 0 : states.length)) {
1977
- throw new Error("Workflow: Missing states in config");
2057
+ throw new Error("Workflow plugin: Missing \"states\" in config");
2058
+ }
2059
+ if (!(schemaTypes == null ? void 0 : schemaTypes.length)) {
2060
+ throw new Error("Workflow plugin: Missing \"schemaTypes\" in config");
1978
2061
  }
1979
2062
  return {
1980
2063
  name: "sanity-plugin-workflow",
@@ -1983,12 +2066,33 @@ const workflow = sanity.definePlugin(function () {
1983
2066
  },
1984
2067
  // TODO: Remove 'workflow.metadata' from list of new document types
1985
2068
  // ...
2069
+ studio: {
2070
+ components: {
2071
+ layout: props => WorkflowProvider({
2072
+ ...props,
2073
+ workflow: {
2074
+ schemaTypes,
2075
+ states
2076
+ }
2077
+ })
2078
+ }
2079
+ },
2080
+ form: {
2081
+ components: {
2082
+ input: props => {
2083
+ if (props.id === "root" && sanity.isObjectInputProps(props) && schemaTypes.includes(props.schemaType.name)) {
2084
+ return WorkflowSignal(props);
2085
+ }
2086
+ return props.renderDefault(props);
2087
+ }
2088
+ }
2089
+ },
1986
2090
  document: {
1987
2091
  actions: (prev, context) => {
1988
2092
  if (!schemaTypes.includes(context.schemaType)) {
1989
2093
  return prev;
1990
2094
  }
1991
- return [props => BeginWorkflow(props, states), props => AssignWorkflow(props, states), ...states.map(state => props => UpdateWorkflow(props, states, state)), props => CompleteWorkflow(props, states), ...prev];
2095
+ return [props => BeginWorkflow(props), props => AssignWorkflow(props), ...states.map(state => props => UpdateWorkflow(props, state)), props => CompleteWorkflow(props), ...prev];
1992
2096
  },
1993
2097
  badges: (prev, context) => {
1994
2098
  if (!schemaTypes.includes(context.schemaType)) {
@@ -2001,10 +2105,12 @@ const workflow = sanity.definePlugin(function () {
2001
2105
  if (!documentId) {
2002
2106
  return prev;
2003
2107
  }
2004
- return [() => StateBadge(states, documentId), () => AssigneesBadge(states, documentId, currentUser), ...prev];
2108
+ return [() => StateBadge(documentId), () => AssigneesBadge(documentId, currentUser), ...prev];
2005
2109
  }
2006
2110
  },
2007
- tools: [workflowTool({
2111
+ tools: [
2112
+ // TODO: These configs could be read from Context
2113
+ workflowTool({
2008
2114
  schemaTypes,
2009
2115
  states
2010
2116
  })]