diff --git a/packages/builder/src/components/ComponentOptions/components/Content/Loop/DesignWizard/FactorialModal.js b/packages/builder/src/components/ComponentOptions/components/Content/Loop/DesignWizard/FactorialModal.js
new file mode 100644
index 000000000..1c2c28cd0
--- /dev/null
+++ b/packages/builder/src/components/ComponentOptions/components/Content/Loop/DesignWizard/FactorialModal.js
@@ -0,0 +1,155 @@
+import React, { Fragment, Component } from 'react'
+
+import { Formik, Form, Field } from 'formik'
+import { ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'
+
+import Modal from '../../../../../Modal'
+import { Table, DefaultRow } from '../../../../../Form/table'
+import { Input } from '../../../../../Form'
+
+// Logic -----------------------------------------------------------------------
+// (copied from the library combinatorics implementation)
+
+const product = function* (...sets) {
+ let thresholds = sets
+ .map(s => Math.max(s.length, 1)).reverse()
+ .reduce(
+ (acc, current, i) => acc.concat([
+ (acc[i - 1] || 1) * current
+ ]), []
+ )
+ .reverse()
+
+ for (let counter = 0; counter < thresholds[0]; counter++) {
+ yield sets.map(
+ (s, i) => s[Math.floor(counter / (thresholds[i + 1] || 1)) % s.length]
+ )
+ }
+}
+
+const generateTable = (factors) => {
+ const names = factors.map(f => f.name)
+ const levels = factors.map(f => f.levels.split('\n').filter(c => c !== ''))
+
+ return {
+ columns: names.map(name => ({ name, type: 'string' })),
+ rows: Array.from(product(...levels)),
+ }
+}
+
+// UI --------------------------------------------------------------------------
+
+const TableRow = ({ name, arrayHelpers }) =>
+
+
+
+
+
+
+
+
+class FactorialModal extends Component {
+ constructor(props) {
+ super(props)
+ this.state = { isOpen: false }
+ this.promiseHandlers = {}
+ }
+
+ toggle() {
+ this.setState({ isOpen: !this.state.isOpen })
+ }
+
+ async show() {
+ this.toggle()
+ return new Promise(
+ (resolve, reject) => {
+ this.promiseHandlers = {
+ resolve: result => {
+ this.setState({ isOpen: false })
+ return resolve(result)
+ },
+ reject
+ }
+ }
+ )
+ }
+
+ render() {
+ return
+ Factor
+ Levels
+
+