forked from tsai/budibase
4 changed files with 272 additions and 0 deletions
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"presets": ["@babel/preset-env"], |
||||
|
"sourceMaps": "inline", |
||||
|
"retainLines": true, |
||||
|
"plugins": [ |
||||
|
["@babel/plugin-transform-runtime", |
||||
|
{ |
||||
|
"regenerator": true |
||||
|
} |
||||
|
] |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
import { |
||||
|
isString, |
||||
|
isBoolean, |
||||
|
isNumber, |
||||
|
isArray, |
||||
|
isUndefined |
||||
|
} from "lodash/fp"; |
||||
|
|
||||
|
import { assign } from "lodash"; |
||||
|
|
||||
|
export const createDefaultProps = (propsDefinition, derivedFromProps) => { |
||||
|
|
||||
|
const props = {}; |
||||
|
const errors = []; |
||||
|
|
||||
|
for(let propDef in propsDefinition) { |
||||
|
const parsedPropDef = parsePropDef(propsDefinition[propDef]); |
||||
|
if(parsedPropDef.error) |
||||
|
errors.push({propName:propDef, error:parsedPropDef.error}); |
||||
|
else |
||||
|
props[propDef] = parsedPropDef; |
||||
|
} |
||||
|
|
||||
|
if(derivedFromProps) { |
||||
|
assign(props, ...derivedFromProps); |
||||
|
} |
||||
|
|
||||
|
return ({ |
||||
|
props, errors |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const parsePropDef = propDef => { |
||||
|
const error = message => ({error:message}); |
||||
|
|
||||
|
if(isString(propDef)) { |
||||
|
if(!types[propDef]) |
||||
|
return error(`Do not recognise type ${propDef}`); |
||||
|
|
||||
|
return types[propDef].default(); |
||||
|
} |
||||
|
|
||||
|
if(!propDef.type) |
||||
|
return error("Property Definition must declare a type"); |
||||
|
|
||||
|
const type = types[propDef.type]; |
||||
|
if(!type) |
||||
|
return error(`Do not recognise type ${propDef.type}`); |
||||
|
|
||||
|
if(isUndefined(propDef.default)) |
||||
|
return type.default(propDef); |
||||
|
|
||||
|
if(!type.isOfType(propDef.default)) |
||||
|
return error(`${propDef.default} is not of type ${type}`); |
||||
|
|
||||
|
return propDef.default; |
||||
|
} |
||||
|
|
||||
|
const propType = (defaultValue, isOfType) => ({ |
||||
|
isOfType, default:defaultValue |
||||
|
}); |
||||
|
|
||||
|
const types = { |
||||
|
string: propType(() => "", isString), |
||||
|
bool: propType(() => false, isBoolean), |
||||
|
number: propType(() => 0, isNumber), |
||||
|
array: propType(() => [], isArray) |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
import { createDefaultProps } from "../src/userInterface/propsDefinitionParsing/createDefaultProps"; |
||||
|
import { |
||||
|
keys, some |
||||
|
} from "lodash/fp"; |
||||
|
|
||||
|
describe("createDefaultProps", () => { |
||||
|
|
||||
|
it("should create a object with single string value, when default string field set", () => { |
||||
|
const propDef = { |
||||
|
fieldName: {type:"string", default:"something"} |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toBe("something"); |
||||
|
expect(keys(props).length).toBe(1); |
||||
|
}); |
||||
|
|
||||
|
it("should create a object with single blank string value, when no default", () => { |
||||
|
const propDef = { |
||||
|
fieldName: {type:"string"} |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toBe(""); |
||||
|
}); |
||||
|
|
||||
|
it("should create a object with single blank string value, when prop definition is 'string' ", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "string" |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toBe(""); |
||||
|
}); |
||||
|
|
||||
|
it("should create a object with single fals value, when prop definition is 'bool' ", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "bool" |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toBe(false); |
||||
|
}); |
||||
|
|
||||
|
it("should create a object with single 0 value, when prop definition is 'number' ", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "number" |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toBe(0); |
||||
|
}); |
||||
|
|
||||
|
it("should create a object with single 0 value, when prop definition is 'array' ", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "array" |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toEqual([]); |
||||
|
}); |
||||
|
|
||||
|
it("should create an object with multiple prop names", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "string", |
||||
|
fieldLength: { type: "number", default: 500 } |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors).toEqual([]); |
||||
|
expect(props.fieldName).toBeDefined(); |
||||
|
expect(props.fieldName).toBe(""); |
||||
|
expect(props.fieldLength).toBeDefined(); |
||||
|
expect(props.fieldLength).toBe(500); |
||||
|
expect(keys(props).length).toBe(2); |
||||
|
}) |
||||
|
|
||||
|
it("should return error when invalid type", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "invalid type name", |
||||
|
fieldLength: { type: "invalid type name "} |
||||
|
}; |
||||
|
|
||||
|
const { errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors.length).toBe(2); |
||||
|
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy(); |
||||
|
expect(some(e => e.propName === "fieldLength")(errors)).toBeTruthy(); |
||||
|
}); |
||||
|
|
||||
|
it("should return error default value is not of declared type", () => { |
||||
|
const propDef = { |
||||
|
fieldName: {type:"string", default: 1} |
||||
|
}; |
||||
|
|
||||
|
const { errors } = createDefaultProps(propDef); |
||||
|
|
||||
|
expect(errors.length).toBe(1); |
||||
|
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy(); |
||||
|
}); |
||||
|
|
||||
|
it("should merge in derived props", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "string", |
||||
|
fieldLength: { type: "number", default: 500} |
||||
|
}; |
||||
|
|
||||
|
const derivedFrom = { |
||||
|
fieldName: "surname" |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef, [derivedFrom]); |
||||
|
|
||||
|
expect(errors.length).toBe(0); |
||||
|
expect(props.fieldName).toBe("surname"); |
||||
|
expect(props.fieldLength).toBe(500); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
it("should merge in derived props, last in list taking priority", () => { |
||||
|
const propDef = { |
||||
|
fieldName: "string", |
||||
|
fieldLength: { type: "number", default: 500} |
||||
|
}; |
||||
|
|
||||
|
const derivedFrom1 = { |
||||
|
fieldName: "surname", |
||||
|
fieldLength: 200 |
||||
|
}; |
||||
|
|
||||
|
const derivedFrom2 = { |
||||
|
fieldName: "forename" |
||||
|
}; |
||||
|
|
||||
|
const { props, errors } = createDefaultProps(propDef, [derivedFrom1, derivedFrom2]); |
||||
|
|
||||
|
expect(errors.length).toBe(0); |
||||
|
expect(props.fieldName).toBe("forename"); |
||||
|
expect(props.fieldLength).toBe(200); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}) |
||||
Loading…
Reference in new issue