Skip to content
Snippets Groups Projects
Unverified Commit c5a856af authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Forms Refactoring 2 — Extract shared utils (#24460)

parent 01018d40
No related branches found
No related tags found
No related merge requests found
......@@ -5,7 +5,7 @@ import PropTypes from "prop-types";
import { connect } from "react-redux";
import { createSelector } from "reselect";
import { reduxForm, getValues, initialize, change } from "redux-form";
import { getIn, assocIn } from "icepick";
import { assocIn } from "icepick";
import _ from "underscore";
import { t } from "ttag";
......@@ -20,6 +20,8 @@ export {
CustomFormSection as FormSection,
} from "metabase/components/form/CustomForm";
import { makeFormObject, getValue } from "./formUtils";
let FORM_ID = 0;
// use makeMapStateToProps so each component gets it's own unique formId
const makeMapStateToProps = () => {
......@@ -287,78 +289,3 @@ class Form extends React.Component {
}
export default connect(makeMapStateToProps)(Form);
// returns a function that takes an object
// apply the top level method (if any) to the whole object
// then apply each field's method (if any) to each value in object, setting the result if not undefined
//
// equivalent examples:
//
// form.initial is { foo: "bar" }
// form.initial is () => ({ foo: "bar" })
// form.fields[0] is { name: "foo", initial: "bar" }
// form.fields[0] is { name: "foo", initial: () => "bar" }
//
function makeFormMethod(form, methodName, defaultValues = {}, mergeFn) {
const originalMethod = form[methodName];
form[methodName] = (object, ...args) => {
// make a copy
const values = {
...(getValue(originalMethod, object, ...args) ||
getValue(defaultValues, object, ...args)),
};
for (const field of form.fields(object)) {
const value = getValue(
field[methodName],
object && getValueAtPath(object, field.name),
...args,
);
if (value !== undefined) {
setValueAtPath(values, field.name, value, mergeFn);
}
}
return values;
};
}
// if the first arg is a function, call it, otherwise return it.
function getValue(fnOrValue, ...args) {
return typeof fnOrValue === "function" ? fnOrValue(...args) : fnOrValue;
}
function makeFormObject(formDef) {
const form = {
...formDef,
fields: values => getValue(formDef.fields, values),
fieldNames: values => [
"id",
...form.fields(values).map(field => field.name),
],
};
// for validating the object, or individual values
makeFormMethod(form, "validate", {}, (a, b) =>
[a, b].filter(a => a).join(", "),
);
// for getting the initial values object, or getting individual values
makeFormMethod(form, "initial");
// for normalizeing the object before submitting, or normalizeing individual values
makeFormMethod(form, "normalize", object => object);
makeFormMethod(form, "hidden");
return form;
}
function getObjectPath(path) {
return typeof path === "string" ? path.split(".") : path;
}
function getValueAtPath(object, path) {
return getIn(object, getObjectPath(path));
}
function setValueAtPath(object, path, value, mergeFn = (a, b) => b) {
path = getObjectPath(path);
for (let i = 0; i < path.length; i++) {
if (i === path.length - 1) {
object[path[i]] = mergeFn(object[path[i]], value);
} else {
object = object[path[i]] = object[path[i]] || {};
}
}
}
import { getIn } from "icepick";
// returns a function that takes an object
// apply the top level method (if any) to the whole object
// then apply each field's method (if any) to each value in object, setting the result if not undefined
//
// equivalent examples:
//
// form.initial is { foo: "bar" }
// form.initial is () => ({ foo: "bar" })
// form.fields[0] is { name: "foo", initial: "bar" }
// form.fields[0] is { name: "foo", initial: () => "bar" }
function makeFormMethod(form, methodName, defaultValues = {}, mergeFn) {
const originalMethod = form[methodName];
form[methodName] = (object, ...args) => {
// make a copy
const values = {
...(getValue(originalMethod, object, ...args) ||
getValue(defaultValues, object, ...args)),
};
for (const field of form.fields(object)) {
const value = getValue(
field[methodName],
object && getValueAtPath(object, field.name),
...args,
);
if (value !== undefined) {
setValueAtPath(values, field.name, value, mergeFn);
}
}
return values;
};
}
// if the first arg is a function, call it, otherwise return it.
export function getValue(fnOrValue, ...args) {
return typeof fnOrValue === "function" ? fnOrValue(...args) : fnOrValue;
}
export function makeFormObject(formDef) {
const form = {
...formDef,
fields: values => getValue(formDef.fields, values),
fieldNames: values => [
"id",
...form.fields(values).map(field => field.name),
],
};
// for validating the object, or individual values
makeFormMethod(form, "validate", {}, (a, b) =>
[a, b].filter(a => a).join(", "),
);
// for getting the initial values object, or getting individual values
makeFormMethod(form, "initial");
// for normalizeing the object before submitting, or normalizeing individual values
makeFormMethod(form, "normalize", object => object);
makeFormMethod(form, "hidden");
return form;
}
function getObjectPath(path) {
return typeof path === "string" ? path.split(".") : path;
}
function getValueAtPath(object, path) {
return getIn(object, getObjectPath(path));
}
function setValueAtPath(object, path, value, mergeFn = (a, b) => b) {
path = getObjectPath(path);
for (let i = 0; i < path.length; i++) {
if (i === path.length - 1) {
object[path[i]] = mergeFn(object[path[i]], value);
} else {
object = object[path[i]] = object[path[i]] || {};
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment