add every_point to UI

pull/1094/head
gabrielburnworth 2019-01-13 15:43:12 -08:00
parent ed56ef7fb5
commit 622eaa4696
10 changed files with 171 additions and 72 deletions

View File

@ -1,7 +1,8 @@
import {
createSequenceMeta,
determineDropdown,
findVariableByName
findVariableByName,
determineVector
} from "../sequence_meta";
import {
fakeSequence,
@ -36,37 +37,70 @@ describe("determineDropdown", () => {
expect(r.label).toBe("Coordinate (0, 1, 2)");
expect(r.value).toBe("?");
});
});
it("Returns a label for `identifier`", () => {
const r = determineDropdown({
kind: "variable_declaration",
args: {
label: "x",
data_value: { kind: "identifier", args: { label: "parent1" } }
}
}, buildResourceIndex([]).index);
expect(r.label).toBe("Parent1");
expect(r.value).toBe("?");
});
it("Returns a label for `identifier`", () => {
const r = determineDropdown({
kind: "variable_declaration",
args: {
label: "x",
data_value: { kind: "identifier", args: { label: "parent1" } }
}
}, buildResourceIndex([]).index);
expect(r.label).toBe("Parent1");
expect(r.value).toBe("?");
});
it("Returns a label for `point`", () => {
const point = fakePoint();
const r = determineDropdown({
kind: "variable_declaration",
args: {
label: "x",
data_value: {
kind: "point",
args: {
pointer_id: point.body.id || -0,
pointer_type: "Point"
it("Returns a label for `every_point`", () => {
const r = determineDropdown({
kind: "variable_declaration",
args: {
label: "x",
data_value: { kind: "every_point", args: { every_point_type: "Plant" } }
}
}, buildResourceIndex([]).index);
expect(r.label).toBe("All plants");
expect(r.value).toBe("Plant");
});
it("Returns a label for `point`", () => {
const point = fakePoint();
const r = determineDropdown({
kind: "variable_declaration",
args: {
label: "x",
data_value: {
kind: "point",
args: {
pointer_id: point.body.id || -0,
pointer_type: "Point"
}
}
}
}
}, buildResourceIndex([point]).index);
expect(r.label).toBe(formatPoint(point).label);
expect(r.value).toBe("" + point.body.id);
}, buildResourceIndex([point]).index);
expect(r.label).toBe(formatPoint(point).label);
expect(r.value).toBe("" + point.body.id);
});
});
describe("determineVector()", () => {
it("determines vector for point", () => {
const point = fakePoint();
const v = determineVector({
kind: "variable_declaration",
args: {
label: "x",
data_value: {
kind: "point",
args: {
pointer_id: point.body.id || -0,
pointer_type: "Point"
}
}
}
}, buildResourceIndex([point]).index);
const { x, y, z } = point.body;
expect(v).toEqual(expect.objectContaining({ x, y, z }));
});
});
describe("createSequenceMeta", () => {

View File

@ -9,7 +9,7 @@ import { findPointerByTypeAndId } from "./selectors";
import { findSlotByToolId, findToolById } from "./selectors_by_id";
import { capitalize } from "lodash";
import {
formatPoint
formatPoint, safeEveryPointType, everyPointDDI
} from "../sequences/step_tiles/tile_move_absolute/generate_list";
export interface SequenceMeta {
@ -64,6 +64,9 @@ export const determineDropdown =
return { label: `Coordinate (${x}, ${y}, ${z})`, value: "?" };
case "identifier":
return { label: capitalize(data_value.args.label), value: "?" };
case "every_point":
const { every_point_type } = data_value.args;
return everyPointDDI(safeEveryPointType(every_point_type));
case "point":
const { pointer_id, pointer_type } = data_value.args;
const pointer =

View File

@ -51,7 +51,7 @@ describe("convertDDItoDeclaration()", () => {
const variable = convertDDItoDeclaration({ label: "parent" })(ddi);
const expected: ScopeDeclarationBodyItem = {
kind: "parameter_declaration",
args: { data_type: "point", label: "parent" }
args: { label: "parent", data_type: "point" }
};
expect(variable).toEqual(expected);
});
@ -64,9 +64,25 @@ describe("convertDDItoDeclaration()", () => {
const expected: ScopeDeclarationBodyItem = {
kind: "variable_declaration",
args: {
label: "parent",
data_value: {
kind: "identifier", args: { label: "parent0" }
}, label: "parent"
}
}
};
expect(variable).toEqual(expected);
});
it("returns location data: every_point", () => {
const ddi = ({ headingId: "every_point", label: "All Plants", value: "Plant" });
const variable = convertDDItoDeclaration({ label: "label" })(ddi);
const expected: ScopeDeclarationBodyItem = {
kind: "variable_declaration",
args: {
label: "label",
data_value: {
kind: "every_point", args: { every_point_type: "Plant" }
}
}
};
expect(variable).toEqual(expected);

View File

@ -14,7 +14,7 @@ import {
} from "../locals_list_support";
import { difference } from "lodash";
import { VariableNameSet } from "../../../resources/interfaces";
import { generateList } from "../../step_tiles/tile_move_absolute/generate_list";
import { locationFormList } from "../../step_tiles/tile_move_absolute/generate_list";
import { convertDDItoDeclaration } from "../handle_select";
describe("<LocationForm/>", () => {
@ -43,7 +43,7 @@ describe("<LocationForm/>", () => {
expect(selects.length).toBe(1);
const select = selects.first().props();
expect(select.allowEmpty).toBe(true);
const choices = generateList(p.resources, [PARENT]);
const choices = locationFormList(p.resources, [PARENT], true);
const actualLabels = select.list.map(x => x.label).sort();
const expectedLabels = choices.map(x => x.label).sort();
const diff = difference(actualLabels, expectedLabels);

View File

@ -9,13 +9,14 @@ import {
Dictionary,
Identifier,
Point,
Tool
Tool,
EveryPoint
} from "farmbot";
export const EMPTY_COORD: Coordinate =
({ kind: "coordinate", args: { x: 0, y: 0, z: 0 } });
type DataValue = Coordinate | Identifier | Point | Tool;
type DataValue = Coordinate | Identifier | Point | Tool | EveryPoint;
const createVariableDeclaration =
(label: string, data_value: DataValue): VariableDeclaration =>
({
@ -39,6 +40,14 @@ const pointVar = (
args: { pointer_type, pointer_id: parseInt("" + value) }
});
const everyPointVar =
(value: string | number) =>
({ label }: { label: string }): VariableDeclaration =>
createVariableDeclaration(label, {
kind: "every_point",
args: { every_point_type: "" + value }
});
const manualEntry = ({ label }: { label: string }): VariableDeclaration =>
createVariableDeclaration(label, {
kind: "coordinate", args: { x: 0, y: 0, z: 0 }
@ -71,13 +80,14 @@ const newDeclarationCreator = (ddi: DropDownItem):
newVarLabel?: string,
useIdentifier?: boolean
}) => ScopeDeclarationBodyItem | undefined => {
if (ddi.isNull) { return manualEntry; } // Caller decides X/Y/Z
if (ddi.isNull) { return manualEntry; } // Coordinate
switch (ddi.headingId) {
case "Plant":
case "GenericPointer": return pointVar(ddi.headingId, ddi.value);
case "Tool": return toolVar(ddi.value);
case "parameter": return newParameter; // Caller decides X/Y/Z
case "Other": return manualEntry;
case "every_point": return everyPointVar(ddi.value);
case "Other": return manualEntry; // Coordinate
}
return () => undefined;
};

View File

@ -1,12 +1,12 @@
import * as React from "react";
import { Row, Col, FBSelect, BlurableInput } from "../../ui";
import { t } from "i18next";
import { generateList } from "../step_tiles/tile_move_absolute/generate_list";
import { locationFormList } from "../step_tiles/tile_move_absolute/generate_list";
import {
convertDDItoDeclaration, addOrEditDeclaration
} from "../locals_list/handle_select";
import {
LocationFormProps, LocalsListProps, PARENT, AllowedDeclaration
LocationFormProps, LocalsListProps, PARENT, AllowedDeclaration,
} from "../locals_list/locals_list_support";
import { defensiveClone, betterCompact } from "../../util/util";
import {
@ -73,7 +73,8 @@ export const LocationForm =
(props: LocationFormProps) => {
const {
sequenceUuid, resources, onChange, declarations, variable,
hideVariableLabel, locationDropdownKey, allowedDeclarations
hideVariableLabel, locationDropdownKey, allowedDeclarations,
disallowGroups
} = props;
const { celeryNode, dropdown, vector } =
maybeUseStepData({
@ -84,7 +85,7 @@ export const LocationForm =
const useIdentifier = allowedDeclarations === AllowedDeclaration.identifier;
const variableListItems = (props.shouldDisplay(Feature.variables) &&
allowedDeclarations !== AllowedDeclaration.variable) ? [PARENT] : [];
const list = generateList(resources, variableListItems);
const list = locationFormList(resources, variableListItems, !disallowGroups);
/** Variable name. */
const { label } = celeryNode.args;
const declaration = defensiveClone(celeryNode);

View File

@ -38,6 +38,8 @@ interface CommonProps {
* chooses between reassignment vs. creation for new variables,
* and determines which variables to display in the form. */
allowedDeclarations: AllowedDeclaration;
/** Don't display group dropdown items. */
disallowGroups?: boolean;
}
export interface LocalsListProps extends CommonProps {

View File

@ -181,6 +181,7 @@ export class TileMoveAbsolute extends Component<StepParams, MoveAbsState> {
hideVariableLabel={true}
locationDropdownKey={JSON.stringify(this.props.currentSequence)}
allowedDeclarations={AllowedDeclaration.identifier}
disallowGroups={true}
width={3} />
SpeedForm = () =>

View File

@ -1,9 +1,9 @@
import { generateList, dropDownName } from "../generate_list";
import { locationFormList, dropDownName } from "../generate_list";
import { fakeResourceIndex } from "../test_helpers";
describe("generateList()", () => {
describe("locationFormList()", () => {
it("returns dropdown list", () => {
const items = generateList(fakeResourceIndex(), []);
const items = locationFormList(fakeResourceIndex(), []);
const toolHeading = items[0];
expect(toolHeading).toEqual({
headingId: "Tool",
@ -24,7 +24,7 @@ describe("generateList()", () => {
value: 0,
heading: true,
});
const plant = items[4];
const plant = items[3];
expect(plant).toEqual({
headingId: "Plant",
label: "Plant 1 (1, 2, 3)",

View File

@ -32,7 +32,7 @@ export function activeTools(resources: ResourceIndex): ToolAndLocation[] {
: undefined));
}
type DropdownHeadingId = PointerTypeName | typeof TOOL;
type DropdownHeadingId = PointerTypeName | typeof TOOL | "Other";
/** Location selection menu section names. */
export const NAME_MAP: Record<DropdownHeadingId, string> = {
@ -40,38 +40,47 @@ export const NAME_MAP: Record<DropdownHeadingId, string> = {
"Plant": "Plants",
"ToolSlot": "Tool Slots",
"Tool": "Tools",
"Other": "Other",
};
/** Location selection menu section headers. */
const HEADINGS: () => DropDownItem[] = () => [
...Object.keys(NAME_MAP)
.filter(x => x !== "ToolSlot")
.map((name: DropdownHeadingId) => {
return ({
label: t(NAME_MAP[name]),
heading: true,
value: 0,
headingId: name
});
})
];
const heading = (name: DropdownHeadingId) => ({
label: t(NAME_MAP[name]),
heading: true,
value: 0,
headingId: name
});
const ddiFrom = (points: TaggedPoint[], pointerType: PointerTypeName) => points
.filter(x => x.body.pointer_type === pointerType)
.map(formatPoint)
.filter(x => parseInt("" + x.value) > 0);
const maybeGroup = (display: boolean) =>
(groupDDI: DropDownItem): DropDownItem[] =>
display ? [groupDDI] : [];
/** Location selection menu items. */
export function generateList(input: ResourceIndex,
additionalItems: DropDownItem[]): DropDownItem[] {
const SORT_KEY: keyof DropDownItem = "headingId";
export function locationFormList(input: ResourceIndex,
additionalItems: DropDownItem[], displayGroups?: boolean): DropDownItem[] {
const points = selectAllActivePoints(input)
.filter(x => (x.body.pointer_type !== "ToolSlot"));
const toolDDI: DropDownItem[] =
activeTools(input).map(({ tool, location }) => formatTools(tool, location));
return _(points)
.map(formatPoint)
.filter(x => x.body.pointer_type !== "ToolSlot");
const plantDDI = ddiFrom(points, "Plant");
const genericPointerDDI = ddiFrom(points, "GenericPointer");
const toolDDI: DropDownItem[] = activeTools(input)
.map(({ tool, location }) => formatTools(tool, location))
.filter(x => parseInt("" + x.value) > 0);
const group = maybeGroup(!!displayGroups);
return _(heading("Tool"))
.concat(toolDDI)
.filter(x => parseInt("" + x.value) > 0)
.concat(HEADINGS())
.sortBy(SORT_KEY)
.reverse()
.concat({ label: t("Other"), heading: true, value: 0, headingId: "Other" })
.concat(group(everyPointDDI("Tool")))
.concat(group(everyPointDDI("ToolSlot")))
.concat(heading("Plant"))
.concat(plantDDI)
.concat(group(everyPointDDI("Plant")))
.concat(heading("GenericPointer"))
.concat(genericPointerDDI)
.concat(group(everyPointDDI("GenericPointer")))
.concat(heading("Other"))
.concat(additionalItems)
.value();
}
@ -102,3 +111,26 @@ export function dropDownName(name: string, v?: Vector3) {
if (v) { label += ` (${v.x}, ${v.y}, ${v.z})`; }
return capitalize(label);
}
export const EVERY_POINT_LABEL = {
"Plant": "All plants",
"GenericPointer": "All map points",
"Tool": "All tools",
"ToolSlot": "All tool slots",
};
export type EveryPointType = keyof typeof EVERY_POINT_LABEL;
const isEveryPointType = (x: string): x is EveryPointType =>
Object.keys(EVERY_POINT_LABEL).includes(x);
export const safeEveryPointType = (x: string): EveryPointType => {
if (isEveryPointType(x)) {
return x;
} else {
throw new Error(`'${x}' is not of type EveryPointType`);
}
};
export const everyPointDDI = (value: EveryPointType) =>
({ value, label: EVERY_POINT_LABEL[value], headingId: "every_point" });