Skip to content

Commit

Permalink
fix(configurator): fix bugs and document the format (#379)
Browse files Browse the repository at this point in the history
This wasn't documented before and many people made various mistakes in
it. Some options were simply wrong so there are fixes for that. It also
includes a test to prevent some common mistakes, like setting the step
bigger than the range or initial value outside of the range, in the
future.
  • Loading branch information
Thomaash authored Jan 25, 2020
1 parent 607df90 commit b409c96
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 3 deletions.
29 changes: 26 additions & 3 deletions lib/network/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,30 @@ let allOptions = {
allOptions.groups.__any__ = allOptions.nodes;
allOptions.manipulation.controlNodeStyle = allOptions.nodes;


/**
* This provides ranges, initial values, steps and dropdown menu choices for the
* configuration.
*
* @remarks
* Checkbox: `boolean`
* The value supllied will be used as the initial value.
*
* Text field: `string`
* The passed text will be used as the initial value. Any text will be
* accepted afterwards.
*
* Number range: `[number, number, number, number]`
* The meanings are `[initial value, min, max, step]`.
*
* Dropdown: `[Exclude<string, "color">, ...(string | number | boolean)[]]`
* Translations for people with poor understanding of TypeScript: the first
* value always has to be a string but never `"color"`, the rest can be any
* combination of strings, numbers and booleans.
*
* Color picker: `["color", string]`
* The first value says this will be a color picker not a dropdown menu. The
* next value is the initial color.
*/
let configureOptions = {
nodes: {
borderWidth: [1, 0, 10, 1],
Expand Down Expand Up @@ -587,7 +610,7 @@ let configureOptions = {
selfReferenceSize: [20, 0, 200, 1],
selfReference: {
size: [20, 0, 200, 1],
angle: [Math.PI / 2, Math.PI * 2, 3 * Math.PI / 2, Math.PI],
angle: [Math.PI / 2, -6 * Math.PI, 6 * Math.PI, Math.PI / 8],
},
shadow: {
enabled: false,
Expand Down Expand Up @@ -639,7 +662,7 @@ let configureOptions = {
hoverConnectedEdges: true,
tooltipDelay: [300, 0, 1000, 25],
zoomView: true,
zoomSpeed: [1, 1, 1, 1]
zoomSpeed: [1, 0.1, 2, 0.1]
},
manipulation: {
enabled: false,
Expand Down
93 changes: 93 additions & 0 deletions test/configure-options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { expect } from "chai";

import { configureOptions } from "../lib/network/options";

/**
* Traverse the root and test all the nested options.
*
* @param root - An object containing potentially nested configure options.
* @param rootPath - The path that was used to get to the root. Can be omitted
* if the root is the top level object.
*/
function traverseCO(root: any, rootPath: string[] = []): void {
const impreciseNumberPS =
" (There's some wiggle room due to the fact that floating point numbers are not absolutely precise hence the unreadable test.)";

Object.entries(root).forEach(([key, value]): void => {
const path = [...rootPath, key];

if (typeof value === "object" && value !== null && !Array.isArray(value)) {
traverseCO(value, path);
} else {
it(path.join("."), function(): void {
if (Array.isArray(value) && typeof value[0] === "number") {
expect(
value,
"Range has to have the format [initial, min, max, step]."
).to.have.lengthOf(4);
expect(
typeof value[0] === "number" &&
typeof value[1] === "number" &&
typeof value[2] === "number" &&
typeof value[3] === "number",
"All the values in range have to be numbers."
).to.be.true;

const [initial, min, max, step] = value;
expect(
min,
"The minimum has to be less than the maximum."
).to.be.lessThan(max);
expect(step, "The step can't exceed the range.").to.be.lessThan(
max - min
);
expect(initial, "The initial value has to be within the range.")
.to.be.at.most(max)
.and.at.least(min);
expect(
((initial - min) / step) % 1,
"The initial value has to be at one of the steps." +
impreciseNumberPS
).to.not.be.approximately(0.5, 0.499999999);
expect(
((max - min) / step) % 1,
"Both the min and max have to be reachable by the step." +
impreciseNumberPS
).to.not.be.approximately(0.5, 0.499999999);
} else if (Array.isArray(value) && value[0] === "color") {
expect(
value,
"There has to be an initial color after the color keyword."
).to.to.have.lengthOf(2);

expect(
value,
"All values have to be color strings."
).to.satisfy((value: unknown[]): boolean =>
value.every((v): boolean => typeof v === "string")
);
} else if (Array.isArray(value) && typeof value[0] === "string") {
expect(
value,
"Only strings, numbers and booleans are allowed in dropdown menus."
).to.satisfy((value: unknown[]): boolean =>
value.every(
(v): boolean =>
typeof v === "string" ||
typeof v === "number" ||
typeof v === "boolean"
)
);
} else if (typeof value === "boolean" || typeof value === "string") {
// No problems here.
} else {
expect.fail(`Unrecognized format: ${JSON.stringify(value)}`);
}
});
}
});
}

describe("Configure Options", function(): void {
traverseCO(configureOptions);
});

0 comments on commit b409c96

Please sign in to comment.