regimen variable labels

pull/1254/head
gabrielburnworth 2019-06-28 12:05:19 -07:00
parent 46d7413610
commit db203a444a
9 changed files with 125 additions and 23 deletions

View File

@ -42,10 +42,11 @@
}
.regimen-event {
position: relative;
background: $gray;
padding: 0.7rem;
border-radius: 3px;
height: 3.5rem;
min-height: 3.5rem;
margin-bottom: 1rem;
}
@ -58,12 +59,18 @@
.regimen-event-time {
margin-left: 1.5rem;
margin-right: 1.5rem;
}
.regimen-event-variable {
display: inline-block;
margin-right: 1.5rem;
}
.regimen-control {
float: right;
margin-right: 0.7rem;
margin-top: 0.3rem;
position: absolute;
top: 1rem;
right: 1rem;
}
.regimen-days-label {

View File

@ -1,11 +1,15 @@
import { mapStateToProps } from "../state_to_props";
import { fakeState } from "../../__test_support__/fake_state";
import { TaggedResource } from "farmbot";
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
import {
buildResourceIndex
} from "../../__test_support__/resource_index_builder";
import { newTaggedResource } from "../../sync/actions";
import { selectAllRegimens } from "../../resources/selectors";
import { fakeVariableNameSet } from "../../__test_support__/fake_variables";
import { fakeRegimen, fakeSequence } from "../../__test_support__/fake_state/resources";
import {
fakeRegimen, fakeSequence
} from "../../__test_support__/fake_state/resources";
describe("mapStateToProps()", () => {
it("returns props: no regimen selected", () => {
@ -48,7 +52,9 @@ describe("mapStateToProps()", () => {
it("returns variableData", () => {
const reg = fakeRegimen();
const seq = fakeSequence();
reg.body.regimen_items = [{ sequence_id: seq.body.id || 0, time_offset: 1000 }];
reg.body.regimen_items = [{
sequence_id: seq.body.id || 0, time_offset: 1000
}];
const state = fakeState();
state.resources = buildResourceIndex([reg, seq]);
state.resources.consumers.regimens.currentRegimen = reg.uuid;
@ -57,4 +63,37 @@ describe("mapStateToProps()", () => {
const props = mapStateToProps(state);
expect(props.variableData).toEqual(varData);
});
it("returns calendar rows", () => {
const reg = fakeRegimen();
const seq = fakeSequence();
seq.body.body = [{
kind: "move_absolute", args: {
location: { kind: "identifier", args: { label: "variable" } },
offset: { kind: "coordinate", args: { x: 0, y: 0, z: 0 } },
speed: 100,
}
}];
seq.body.args.locals.body = [{
kind: "parameter_declaration",
args: {
label: "variable",
default_value: { kind: "coordinate", args: { x: 1, y: 2, z: 3 } }
}
}];
reg.body.regimen_items = [{
sequence_id: seq.body.id || 0, time_offset: 1000
}];
const state = fakeState();
state.resources = buildResourceIndex([reg, seq]);
state.resources.consumers.regimens.currentRegimen = reg.uuid;
const props = mapStateToProps(state);
expect(props.calendar).toEqual([{
day: "1",
items: [expect.objectContaining({
item: reg.body.regimen_items[0],
sortKey: 1000, variable: "variable"
})]
}]);
});
});

View File

@ -1,6 +1,4 @@
jest.mock("../../../api/crud", () => ({
overwrite: jest.fn(),
}));
jest.mock("../../../api/crud", () => ({ overwrite: jest.fn() }));
import * as React from "react";
import { mount } from "enzyme";
@ -18,8 +16,8 @@ import { Actions } from "../../../constants";
const testVariable: VariableDeclaration = {
kind: "variable_declaration",
args: {
label: "label", data_value: {
kind: "identifier", args: { label: "new_var" }
label: "variable", data_value: {
kind: "coordinate", args: { x: 1, y: 2, z: 3 }
}
}
};
@ -40,7 +38,8 @@ describe("<ActiveEditor />", () => {
regimen: fakeRegimen(),
item: {
sequence_id: 0, time_offset: 1000
}
},
variable: undefined,
}]
}],
resources: buildResourceIndex([]).index,
@ -115,6 +114,23 @@ describe("<ActiveEditor />", () => {
wrapper.instance().toggleVarShow();
expect(wrapper.state()).toEqual({ variablesCollapsed: true });
});
it("shows location variable label: coordinate", () => {
const p = fakeProps();
p.calendar[0].items[0].regimen.body.body = [testVariable];
p.calendar[0].items[0].variable = testVariable.args.label;
const wrapper = mount(<ActiveEditor {...p} />);
expect(wrapper.find(".regimen-event-variable").text())
.toEqual("Location Variable - Coordinate (1, 2, 3)");
});
it("doesn't show location variable label", () => {
const p = fakeProps();
p.calendar[0].items[0].regimen.body.body = [];
p.calendar[0].items[0].variable = "variable";
const wrapper = mount(<ActiveEditor {...p} />);
expect(wrapper.find(".regimen-event-variable").length).toEqual(0);
});
});
describe("editRegimenVariables()", () => {

View File

@ -37,7 +37,8 @@ describe("<RegimenEditor />", () => {
regimen: regimen,
item: {
sequence_id: 0, time_offset: 1000
}
},
variable: undefined,
}]
}],
resources: buildResourceIndex([]).index,

View File

@ -17,6 +17,9 @@ import {
import { addOrEditBodyVariables } from "../../sequences/locals_list/handle_select";
import { t } from "../../i18next_wrapper";
import { Actions } from "../../constants";
import { reduceVariables } from "../../sequences/locals_list/variable_support";
import { determineDropdown, withPrefix } from "../../resources/sequence_meta";
import { ResourceIndex } from "../../resources/interfaces";
/**
* The bottom half of the regimen editor panel (when there's something to
@ -60,7 +63,8 @@ export class ActiveEditor
<OpenSchedulerButton dispatch={this.props.dispatch} />
<RegimenRows {...this.regimenProps}
calendar={this.props.calendar}
varsCollapsed={this.state.variablesCollapsed} />
varsCollapsed={this.state.variablesCollapsed}
resources={this.props.resources} />
</div>;
}
}
@ -111,28 +115,32 @@ interface RegimenRowsProps {
calendar: CalendarRow[];
dispatch: Function;
varsCollapsed: boolean;
resources: ResourceIndex;
}
const RegimenRows = (props: RegimenRowsProps) =>
<div className="regimen" style={{
height: regimenSectionHeight(props.regimen, props.varsCollapsed)
}}>
{props.calendar.map(regimenDay(props.dispatch))}
{props.calendar.map(regimenDay(props.dispatch, props.resources))}
</div>;
const regimenDay = (dispatch: Function) =>
const regimenDay = (dispatch: Function, resources: ResourceIndex) =>
(group: CalendarRow, dayIndex: number) =>
<div className="regimen-day" key={dayIndex}>
<label> {t("Day {{day}}", { day: group.day })} </label>
{group.items.map(regimenItemRow(dispatch, dayIndex))}
{group.items.map(regimenItemRow(dispatch, resources, dayIndex))}
</div>;
const regimenItemRow = (dispatch: Function, dayIndex: number) =>
const regimenItemRow = (
dispatch: Function, resources: ResourceIndex, dayIndex: number
) =>
(row: RegimenItemCalendarRow, itemIndex: number) =>
<div className={`${row.color} regimen-event`}
key={`${dayIndex}.${itemIndex}`}>
<span className="regimen-event-title">{row.name}</span>
<span className="regimen-event-time">{row.hhmm}</span>
<DisplayVarValue row={row} resources={resources} />
<i className="fa fa-trash regimen-control" onClick={() =>
dispatch(removeRegimenItem(row.item, row.regimen))} />
</div>;
@ -142,3 +150,21 @@ const removeRegimenItem = (item: RegimenItem, r: TaggedRegimen) => {
copy.body.regimen_items = r.body.regimen_items.filter(x => x !== item);
return overwrite(r, copy.body);
};
interface DisplayVarValueProps {
row: RegimenItemCalendarRow;
resources: ResourceIndex;
}
const DisplayVarValue = (props: DisplayVarValueProps) => {
const { variable, regimen } = props.row;
if (variable) {
const variableNode = reduceVariables(regimen.body.body)[variable];
if (variableNode) {
return <span className="regimen-event-variable">
{withPrefix(determineDropdown(variableNode, props.resources).label)}
</span>;
}
}
return <span />;
};

View File

@ -38,6 +38,8 @@ export interface RegimenItemCalendarRow {
sortKey: number;
day: number;
dispatch: Function;
/** Variable label. */
variable: string | undefined;
}
/** Used by UI widgets that modify a regimen */

View File

@ -13,7 +13,7 @@ import {
findSequenceById,
maybeGetTimeSettings
} from "../resources/selectors";
import { TaggedRegimen } from "farmbot";
import { TaggedRegimen, TaggedSequence } from "farmbot";
import moment from "moment";
import { ResourceIndex, UUID, VariableNameSet } from "../resources/interfaces";
import {
@ -109,6 +109,7 @@ const createRows = (
(item: RegimenItem): RegimenItemCalendarRow => {
const uuid = findId(index, "Sequence", item.sequence_id);
const sequence = findSequence(index, uuid);
const variable = getParameterLabel(sequence);
const { time_offset } = item;
const d = moment.duration(time_offset);
const { name } = sequence.body;
@ -116,5 +117,13 @@ const createRows = (
const FORMAT = timeFormatString(timeSettings);
const hhmm = moment({ hour: d.hours(), minute: d.minutes() }).format(FORMAT);
const day = Math.floor(moment.duration(time_offset).asDays()) + 1;
return { name, hhmm, color, day, dispatch, regimen, item, sortKey: time_offset };
return {
name, hhmm, color, day, dispatch, regimen, item, variable,
sortKey: time_offset
};
};
const getParameterLabel = (sequence: TaggedSequence): string | undefined =>
(sequence.body.args.locals.body || [])
.filter(variable => variable.kind === "parameter_declaration")
.map(variable => variable.args.label)[0];

View File

@ -57,7 +57,9 @@ const maybeFindVariable = (
): SequenceMeta | undefined =>
uuid ? findVariableByName(resources, uuid, label) : undefined;
const withPrefix = (label: string) => `${t("Location Variable")} - ${label}`;
/** Add "Location Variable - " prefix to string. */
export const withPrefix = (label: string) =>
`${t("Location Variable")} - ${label}`;
interface DetermineVarDDILabelProps {
label: string;

View File

@ -51,7 +51,7 @@ export const addOrEditParamApps =
};
/** Convert array to a dictionary. */
const reduceVariables = <T extends VariableNode>(
export const reduceVariables = <T extends VariableNode>(
variables: T[]):
Dictionary<T> => {
const items: Dictionary<T> = {};