cleanup and refactoring
parent
9d7833b71c
commit
781ac33b10
|
@ -26,6 +26,7 @@ module CeleryScript
|
||||||
FRIENDLY_ERRORS = {
|
FRIENDLY_ERRORS = {
|
||||||
nothing: {
|
nothing: {
|
||||||
write_pin: "You must select a Peripheral in the Control Peripheral step.",
|
write_pin: "You must select a Peripheral in the Control Peripheral step.",
|
||||||
|
toggle_pin: "You must select a Peripheral in the Toggle Peripheral step.",
|
||||||
variable_declaration: MISSING_VAR,
|
variable_declaration: MISSING_VAR,
|
||||||
parameter_declaration: MISSING_PARAM,
|
parameter_declaration: MISSING_PARAM,
|
||||||
read_pin: "You must select a Sensor in the Read Sensor step.",
|
read_pin: "You must select a Sensor in the Read Sensor step.",
|
||||||
|
|
|
@ -300,7 +300,9 @@ export namespace ToolTips {
|
||||||
trim(`Power cycle FarmBot's onboard computer or microcontroller.`);
|
trim(`Power cycle FarmBot's onboard computer or microcontroller.`);
|
||||||
|
|
||||||
export const SET_SERVO_ANGLE =
|
export const SET_SERVO_ANGLE =
|
||||||
trim(`Move a servo to the provided angle.`);
|
trim(`Move a servo to the provided angle. An angle of 90 degrees
|
||||||
|
corresponds to the servo midpoint (or, for a continuous rotation
|
||||||
|
servo, no movement).`);
|
||||||
|
|
||||||
export const TOGGLE_PIN =
|
export const TOGGLE_PIN =
|
||||||
trim(`Toggle a digital pin on or off.`);
|
trim(`Toggle a digital pin on or off.`);
|
||||||
|
|
|
@ -23,6 +23,12 @@
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
input[type="radio"] {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
margin-top: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,7 +113,7 @@
|
||||||
background: $blue;
|
background: $blue;
|
||||||
}
|
}
|
||||||
&.toggle-pin-step {
|
&.toggle-pin-step {
|
||||||
background: $yellow;
|
background: $orange;
|
||||||
}
|
}
|
||||||
&.set-zero-step {
|
&.set-zero-step {
|
||||||
background: $blue;
|
background: $blue;
|
||||||
|
@ -124,6 +130,12 @@
|
||||||
&.system-action-step {
|
&.system-action-step {
|
||||||
background: $brown;
|
background: $brown;
|
||||||
}
|
}
|
||||||
|
&.emergency-stop-step {
|
||||||
|
background: $brown;
|
||||||
|
}
|
||||||
|
&.reboot-step {
|
||||||
|
background: $brown;
|
||||||
|
}
|
||||||
&.unknown-step {
|
&.unknown-step {
|
||||||
background: $gray;
|
background: $gray;
|
||||||
}
|
}
|
||||||
|
@ -221,7 +233,7 @@
|
||||||
background: $light_blue;
|
background: $light_blue;
|
||||||
}
|
}
|
||||||
&.toggle-pin-step {
|
&.toggle-pin-step {
|
||||||
background: $light_yellow;
|
background: $light_orange;
|
||||||
}
|
}
|
||||||
&.set-zero-step {
|
&.set-zero-step {
|
||||||
background: $light_blue;
|
background: $light_blue;
|
||||||
|
@ -238,6 +250,12 @@
|
||||||
&.system-action-step {
|
&.system-action-step {
|
||||||
background: $light_brown;
|
background: $light_brown;
|
||||||
}
|
}
|
||||||
|
&.emergency-stop-step {
|
||||||
|
background: $light_brown;
|
||||||
|
}
|
||||||
|
&.reboot-step {
|
||||||
|
background: $light_brown;
|
||||||
|
}
|
||||||
&.unknown-step {
|
&.unknown-step {
|
||||||
background: $light_gray;
|
background: $light_gray;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ jest.mock("../../draggable/actions", () => ({
|
||||||
stepGet: jest.fn(() => () => mockStepGetResult),
|
stepGet: jest.fn(() => () => mockStepGetResult),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { FolderNode } from "../constants";
|
import { FolderNode } from "../interfaces";
|
||||||
import { ingest } from "../data_transfer";
|
import { ingest } from "../data_transfer";
|
||||||
import {
|
import {
|
||||||
collapseAll,
|
collapseAll,
|
||||||
|
@ -240,11 +240,11 @@ describe("deleteFolder", () => {
|
||||||
|
|
||||||
describe("updateSearchTerm", () => {
|
describe("updateSearchTerm", () => {
|
||||||
it("updates a search term", () => {
|
it("updates a search term", () => {
|
||||||
const argss =
|
const args =
|
||||||
(payload: string | undefined) => ({ type: "FOLDER_SEARCH", payload });
|
(payload: string | undefined) => ({ type: "FOLDER_SEARCH", payload });
|
||||||
[undefined, "foo"].map(term => {
|
[undefined, "foo"].map(term => {
|
||||||
updateSearchTerm(term);
|
updateSearchTerm(term);
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(argss(term));
|
expect(store.dispatch).toHaveBeenCalledWith(args(term));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,7 +35,7 @@ import {
|
||||||
FolderNameInputProps,
|
FolderNameInputProps,
|
||||||
FolderNodeMedial,
|
FolderNodeMedial,
|
||||||
FolderNodeTerminal,
|
FolderNodeTerminal,
|
||||||
} from "../constants";
|
} from "../interfaces";
|
||||||
import {
|
import {
|
||||||
updateSearchTerm, toggleAll, moveSequence, dropSequence,
|
updateSearchTerm, toggleAll, moveSequence, dropSequence,
|
||||||
sequenceEditMaybeSave,
|
sequenceEditMaybeSave,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FolderNode } from "../constants";
|
import { FolderNode } from "../interfaces";
|
||||||
import { ingest } from "../data_transfer";
|
import { ingest } from "../data_transfer";
|
||||||
import { climb } from "../climb";
|
import { climb } from "../climb";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { TEST_GRAPH } from "./actions_test";
|
import { TEST_GRAPH } from "./actions_test";
|
||||||
import { searchFolderTree, FolderSearchProps } from "../search_folder_tree";
|
import { searchFolderTree, FolderSearchProps } from "../search_folder_tree";
|
||||||
import { TaggedResource } from "farmbot";
|
import { TaggedResource } from "farmbot";
|
||||||
import { FolderUnion } from "../constants";
|
import { FolderUnion } from "../interfaces";
|
||||||
|
|
||||||
describe("searchFolderTree", () => {
|
describe("searchFolderTree", () => {
|
||||||
const searchFor = (input: string) => searchFolderTree({
|
const searchFor = (input: string) => searchFolderTree({
|
||||||
|
@ -19,7 +19,7 @@ describe("searchFolderTree", () => {
|
||||||
expect(before).toEqual(after); // Prevent mutation of original data.
|
expect(before).toEqual(after); // Prevent mutation of original data.
|
||||||
});
|
});
|
||||||
|
|
||||||
it("finds an `inital` folder", () => {
|
it("finds an `initial` folder", () => {
|
||||||
const results = searchFor("one").map(x => x.name);
|
const results = searchFor("one").map(x => x.name);
|
||||||
expect(results.length).toEqual(1);
|
expect(results.length).toEqual(1);
|
||||||
expect(results).toContain("One");
|
expect(results).toContain("One");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { RootFolderNode as Tree } from "./constants";
|
import { RootFolderNode as Tree } from "./interfaces";
|
||||||
import { cloneAndClimb } from "./climb";
|
import { cloneAndClimb } from "./climb";
|
||||||
import { Color, SpecialStatus, TaggedSequence } from "farmbot";
|
import { Color, SpecialStatus, TaggedSequence } from "farmbot";
|
||||||
import { store } from "../redux/store";
|
import { store } from "../redux/store";
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { RootFolderNode, FolderUnion, FolderNodeMedial, FolderNodeInitial } from "./constants";
|
import {
|
||||||
|
RootFolderNode, FolderUnion, FolderNodeMedial, FolderNodeInitial
|
||||||
|
} from "./interfaces";
|
||||||
import { defensiveClone } from "../util";
|
import { defensiveClone } from "../util";
|
||||||
|
|
||||||
interface TreeClimberState {
|
interface TreeClimberState {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
FolderButtonClusterProps,
|
FolderButtonClusterProps,
|
||||||
FolderNameInputProps,
|
FolderNameInputProps,
|
||||||
SequenceDropAreaState,
|
SequenceDropAreaState,
|
||||||
} from "./constants";
|
} from "./interfaces";
|
||||||
import {
|
import {
|
||||||
createFolder,
|
createFolder,
|
||||||
deleteFolder,
|
deleteFolder,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
FolderNodeTerminal,
|
FolderNodeTerminal,
|
||||||
RootFolderNode,
|
RootFolderNode,
|
||||||
FolderMeta,
|
FolderMeta,
|
||||||
} from "./constants";
|
} from "./interfaces";
|
||||||
import { sortBy } from "lodash";
|
import { sortBy } from "lodash";
|
||||||
|
|
||||||
type FoldersIndexedByParentId = Record<number, FolderNode[]>;
|
type FoldersIndexedByParentId = Record<number, FolderNode[]>;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FolderProps } from "./constants";
|
import { FolderProps } from "./interfaces";
|
||||||
import { selectAllSequences } from "../resources/selectors";
|
import { selectAllSequences } from "../resources/selectors";
|
||||||
import { TaggedSequence } from "farmbot";
|
import { TaggedSequence } from "farmbot";
|
||||||
import { resourceUsageList } from "../resources/in_use";
|
import { resourceUsageList } from "../resources/in_use";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { TaggedResource, TaggedSequence } from "farmbot";
|
import { TaggedResource, TaggedSequence } from "farmbot";
|
||||||
import { RootFolderNode, FolderUnion } from "./constants";
|
import { RootFolderNode, FolderUnion } from "./interfaces";
|
||||||
|
|
||||||
export interface FolderSearchProps {
|
export interface FolderSearchProps {
|
||||||
references: Record<string, TaggedResource | undefined>;
|
references: Record<string, TaggedResource | undefined>;
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { HelpState } from "../help/reducer";
|
||||||
import { UsageIndex } from "./in_use";
|
import { UsageIndex } from "./in_use";
|
||||||
import { SequenceMeta } from "./sequence_meta";
|
import { SequenceMeta } from "./sequence_meta";
|
||||||
import { AlertReducerState } from "../messages/interfaces";
|
import { AlertReducerState } from "../messages/interfaces";
|
||||||
import { RootFolderNode, FolderMeta } from "../folders/constants";
|
import { RootFolderNode, FolderMeta } from "../folders/interfaces";
|
||||||
|
|
||||||
export type UUID = string;
|
export type UUID = string;
|
||||||
export type VariableNameSet = Record<string, SequenceMeta | undefined>;
|
export type VariableNameSet = Record<string, SequenceMeta | undefined>;
|
||||||
|
|
|
@ -33,7 +33,9 @@ import { get } from "lodash";
|
||||||
import { Actions } from "../constants";
|
import { Actions } from "../constants";
|
||||||
import { getFbosConfig } from "./getters";
|
import { getFbosConfig } from "./getters";
|
||||||
import { ingest, PARENTLESS as NO_PARENT } from "../folders/data_transfer";
|
import { ingest, PARENTLESS as NO_PARENT } from "../folders/data_transfer";
|
||||||
import { FolderNode, FolderMeta, FolderNodeTerminal, FolderNodeMedial } from "../folders/constants";
|
import {
|
||||||
|
FolderNode, FolderMeta, FolderNodeTerminal, FolderNodeMedial
|
||||||
|
} from "../folders/interfaces";
|
||||||
import { climb } from "../folders/climb";
|
import { climb } from "../folders/climb";
|
||||||
|
|
||||||
export function findByUuid(index: ResourceIndex, uuid: string): TaggedResource {
|
export function findByUuid(index: ResourceIndex, uuid: string): TaggedResource {
|
||||||
|
|
|
@ -52,7 +52,7 @@ export function StepButtonCluster(props: StepButtonProps) {
|
||||||
step={{
|
step={{
|
||||||
kind: "toggle_pin",
|
kind: "toggle_pin",
|
||||||
args: {
|
args: {
|
||||||
pin_number: 4
|
pin_number: NOTHING_SELECTED
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
color="orange">
|
color="orange">
|
||||||
|
|
|
@ -97,61 +97,6 @@ describe("renderCeleryNode()", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEST_DATA: TestData[] = [
|
const TEST_DATA: TestData[] = [
|
||||||
{
|
|
||||||
expected: "MarkPlantasx = 300",
|
|
||||||
node: {
|
|
||||||
kind: "resource_update",
|
|
||||||
args: {
|
|
||||||
resource_id: 23,
|
|
||||||
resource_type: "Plant",
|
|
||||||
label: "x",
|
|
||||||
value: 300
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expected: "System",
|
|
||||||
node: {
|
|
||||||
kind: "check_updates",
|
|
||||||
args: {
|
|
||||||
package: "farmbot_os"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expected: "System",
|
|
||||||
node: {
|
|
||||||
kind: "factory_reset",
|
|
||||||
args: {
|
|
||||||
package: "farmbot_os"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expected: "Unlocking a device requires user intervention",
|
|
||||||
node: {
|
|
||||||
kind: "emergency_lock",
|
|
||||||
args: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expected: "Unable to properly display this step",
|
|
||||||
node: {
|
|
||||||
kind: "power_off",
|
|
||||||
args: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expected: "Unable to properly display this step",
|
|
||||||
node: { kind: "read_status", args: {} }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expected: "",
|
|
||||||
node: {
|
|
||||||
kind: "install_first_party_farmware",
|
|
||||||
args: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
kind: "_if",
|
kind: "_if",
|
||||||
|
@ -166,31 +111,13 @@ describe("renderCeleryNode()", () => {
|
||||||
expected: "Then Execute"
|
expected: "Then Execute"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: { kind: "execute_script", args: { label: "farmware-to-execute" } },
|
||||||
kind: "execute_script",
|
|
||||||
args: { label: "farmware-to-execute" }
|
|
||||||
},
|
|
||||||
expected: "Manual Input"
|
expected: "Manual Input"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: { kind: "execute", args: { sequence_id: 0 } },
|
||||||
kind: "execute",
|
|
||||||
args: {
|
|
||||||
sequence_id: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expected: "Select a sequence"
|
expected: "Select a sequence"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "find_home",
|
|
||||||
args: {
|
|
||||||
speed: 100,
|
|
||||||
axis: "all"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expected: "Find x"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
kind: "move_absolute",
|
kind: "move_absolute",
|
||||||
|
@ -213,40 +140,37 @@ describe("renderCeleryNode()", () => {
|
||||||
expected: "Message"
|
expected: "Message"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: { kind: "take_photo", args: {} },
|
||||||
kind: "take_photo",
|
|
||||||
args: {}
|
|
||||||
},
|
|
||||||
expected: "Photo"
|
expected: "Photo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: { kind: "wait", args: { milliseconds: 100 } },
|
||||||
kind: "wait",
|
|
||||||
args: {
|
|
||||||
milliseconds: 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expected: "milliseconds"
|
expected: "milliseconds"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
kind: "set_servo_angle",
|
kind: "resource_update",
|
||||||
args: {
|
args: {
|
||||||
pin_number: 4,
|
resource_id: 23,
|
||||||
pin_value: 90,
|
resource_type: "Plant",
|
||||||
|
label: "x",
|
||||||
|
value: 300
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
expected: "MarkPlantasx = 300"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "set_servo_angle", args: { pin_number: 4, pin_value: 90, } },
|
||||||
expected: "Servo"
|
expected: "Servo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: { kind: "toggle_pin", args: { pin_number: 13 } },
|
||||||
kind: "toggle_pin",
|
|
||||||
args: {
|
|
||||||
pin_number: 13
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expected: "Pin"
|
expected: "Pin"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "find_home", args: { speed: 100, axis: "all" } },
|
||||||
|
expected: "Find x"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
node: { kind: "zero", args: { axis: "all" } },
|
node: { kind: "zero", args: { axis: "all" } },
|
||||||
expected: "Zero x"
|
expected: "Zero x"
|
||||||
|
@ -259,52 +183,36 @@ describe("renderCeleryNode()", () => {
|
||||||
node: { kind: "home", args: { axis: "all", speed: 100, } },
|
node: { kind: "home", args: { axis: "all", speed: 100, } },
|
||||||
expected: "Home x"
|
expected: "Home x"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
node: { kind: "reboot", args: { package: "farmbot_os" } },
|
|
||||||
expected: "System"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "check_updates", args: { package: "farmbot_os" }
|
|
||||||
},
|
|
||||||
expected: "System"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "factory_reset", args: { package: "farmbot_os" }
|
|
||||||
},
|
|
||||||
expected: "System"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "sync",
|
|
||||||
args: {}
|
|
||||||
},
|
|
||||||
expected: ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "dump_info",
|
|
||||||
args: {}
|
|
||||||
},
|
|
||||||
expected: ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "power_off",
|
|
||||||
args: {}
|
|
||||||
},
|
|
||||||
expected: ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
kind: "read_status",
|
|
||||||
args: {}
|
|
||||||
},
|
|
||||||
expected: ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
node: { kind: "emergency_lock", args: {} },
|
node: { kind: "emergency_lock", args: {} },
|
||||||
|
expected: "Unlocking a device requires user intervention"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "reboot", args: { package: "farmbot_os" } },
|
||||||
|
expected: "entire system"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "check_updates", args: { package: "farmbot_os" } },
|
||||||
|
expected: "System"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "factory_reset", args: { package: "farmbot_os" } },
|
||||||
|
expected: "System"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "sync", args: {} },
|
||||||
|
expected: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "dump_info", args: {} },
|
||||||
|
expected: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "power_off", args: {} },
|
||||||
|
expected: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: { kind: "read_status", args: {} },
|
||||||
expected: ""
|
expected: ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -312,19 +220,12 @@ describe("renderCeleryNode()", () => {
|
||||||
expected: ""
|
expected: ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: { kind: "install_first_party_farmware", args: {} },
|
||||||
kind: "install_first_party_farmware", args: {}
|
|
||||||
},
|
|
||||||
expected: ""
|
expected: ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
// tslint:disable-next-line: no-any
|
||||||
kind: "unknown",
|
node: { kind: "unknown", args: { unknown: 0 } } as any,
|
||||||
args: {
|
|
||||||
unknown: 0
|
|
||||||
}
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
} as any,
|
|
||||||
expected: "unknown"
|
expected: "unknown"
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
jest.mock("../../api/crud", () => {
|
jest.mock("../../../api/crud", () => ({ editStep: jest.fn() }));
|
||||||
return { editStep: jest.fn() };
|
|
||||||
});
|
import { TileReboot, editTheRebootStep, rebootExecutor } from "../tile_reboot";
|
||||||
import { TileReboot, editTheRebootStep, rebootExecutor, MultiChoiceRadio } from "../step_tiles/tile_reboot";
|
import { render } from "enzyme";
|
||||||
import { render, mount } from "enzyme";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { StepParams } from "../interfaces";
|
import { StepParams } from "../../interfaces";
|
||||||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||||
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
|
import { buildResourceIndex } from "../../../__test_support__/resource_index_builder";
|
||||||
import { editStep } from "../../api/crud";
|
import { editStep } from "../../../api/crud";
|
||||||
import { Reboot } from "farmbot";
|
import { Reboot } from "farmbot";
|
||||||
|
|
||||||
const fakeProps = (): StepParams => {
|
const fakeProps = (): StepParams => {
|
||||||
|
@ -66,21 +65,3 @@ describe("<TileReboot/>", () => {
|
||||||
expect(step.args.package).toBe("arduino_firmware");
|
expect(step.args.package).toBe("arduino_firmware");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("MultiChoiceRadio", () => {
|
|
||||||
it("triggers callbacks", () => {
|
|
||||||
const PACKAGE_CHOICES = {
|
|
||||||
"a": "1",
|
|
||||||
"b": "2"
|
|
||||||
};
|
|
||||||
const onChange = jest.fn();
|
|
||||||
const el = mount(<MultiChoiceRadio
|
|
||||||
uuid={"WHATEVER"}
|
|
||||||
choices={PACKAGE_CHOICES}
|
|
||||||
currentChoice={"a"}
|
|
||||||
onChange={onChange} />);
|
|
||||||
const radio = el.find("input[type='radio']").first();
|
|
||||||
radio.simulate("change", "a");
|
|
||||||
expect(onChange).toHaveBeenCalledWith("a");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,7 +1,9 @@
|
||||||
jest.mock("../../../api/crud", () => ({ editStep: jest.fn() }));
|
jest.mock("../../../api/crud", () => ({ editStep: jest.fn() }));
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { TileSetServoAngle, pinNumberChanger, createServoEditFn, ServoPinSelection } from "../tile_set_servo_angle";
|
import {
|
||||||
|
TileSetServoAngle, pinNumberChanger, createServoEditFn, ServoPinSelection
|
||||||
|
} from "../tile_set_servo_angle";
|
||||||
import { mount } from "enzyme";
|
import { mount } from "enzyme";
|
||||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||||
import { SetServoAngle } from "farmbot";
|
import { SetServoAngle } from "farmbot";
|
||||||
|
@ -33,8 +35,8 @@ describe("<TileSetServoAngle/>", () => {
|
||||||
const inputs = block.find("input");
|
const inputs = block.find("input");
|
||||||
const labels = block.find("label");
|
const labels = block.find("label");
|
||||||
const stepArgs = props.currentStep.args as SetServoAngle["args"];
|
const stepArgs = props.currentStep.args as SetServoAngle["args"];
|
||||||
expect(inputs.length).toEqual(4);
|
expect(inputs.length).toEqual(6);
|
||||||
expect(labels.length).toEqual(4);
|
expect(labels.length).toEqual(6);
|
||||||
expect(inputs.first().props().placeholder).toEqual("Control Servo");
|
expect(inputs.first().props().placeholder).toEqual("Control Servo");
|
||||||
expect(labels.at(0).text()).toContain("Servo angle (0-180)");
|
expect(labels.at(0).text()).toContain("Servo angle (0-180)");
|
||||||
expect(inputs.at(1).props().value).toEqual(stepArgs.pin_value);
|
expect(inputs.at(1).props().value).toEqual(stepArgs.pin_value);
|
||||||
|
@ -53,7 +55,7 @@ describe("<TileSetServoAngle/>", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("dissallows named_pins", () => {
|
it("disallows named_pins", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
const step = p.currentStep;
|
const step = p.currentStep;
|
||||||
if (step.kind === "set_servo_angle") {
|
if (step.kind === "set_servo_angle") {
|
||||||
|
|
|
@ -157,10 +157,11 @@ export function renderCeleryNode(props: StepParams) {
|
||||||
case "home": return <TileMoveHome {...props} />;
|
case "home": return <TileMoveHome {...props} />;
|
||||||
case "reboot": return <TileReboot {...props} />;
|
case "reboot": return <TileReboot {...props} />;
|
||||||
case "emergency_lock": return <TileEmergencyStop {...props} />;
|
case "emergency_lock": return <TileEmergencyStop {...props} />;
|
||||||
case "install_first_party_farmware": return <TileSystemAction {...props} />;
|
|
||||||
case "assertion": return <TileAssertion {...props} />;
|
case "assertion": return <TileAssertion {...props} />;
|
||||||
case "check_updates":
|
case "sync": case "dump_info": case "power_off": case "read_status":
|
||||||
case "factory_reset":
|
case "emergency_unlock": case "install_first_party_farmware":
|
||||||
|
return <TileSystemAction {...props} />;
|
||||||
|
case "check_updates": case "factory_reset":
|
||||||
return <TileFirmwareAction {...props} />;
|
return <TileFirmwareAction {...props} />;
|
||||||
default:
|
default:
|
||||||
return <TileUnknown {...props} />;
|
return <TileUnknown {...props} />;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ToolTips, Content } from "../../constants";
|
||||||
import {
|
import {
|
||||||
StepWrapper, StepHeader, StepContent, conflictsString, StepWarning
|
StepWrapper, StepHeader, StepContent, conflictsString, StepWarning
|
||||||
} from "../step_ui/index";
|
} from "../step_ui/index";
|
||||||
import { StepRadio } from "../step_ui/step_radio";
|
import { AxisStepRadio } from "../step_ui/step_radio";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { Xyz, Calibrate, TaggedSequence } from "farmbot";
|
import { Xyz, Calibrate, TaggedSequence } from "farmbot";
|
||||||
import { some } from "lodash";
|
import { some } from "lodash";
|
||||||
|
@ -81,7 +81,7 @@ class InnerTileCalibrate extends React.Component<CalibrateParams, {}> {
|
||||||
conflicts={this.settingConflicts} />}
|
conflicts={this.settingConflicts} />}
|
||||||
</StepHeader>
|
</StepHeader>
|
||||||
<StepContent className={className}>
|
<StepContent className={className}>
|
||||||
<StepRadio
|
<AxisStepRadio
|
||||||
currentSequence={currentSequence}
|
currentSequence={currentSequence}
|
||||||
currentStep={currentStep}
|
currentStep={currentStep}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { t } from "../../i18next_wrapper";
|
||||||
|
|
||||||
export function TileEmergencyStop(props: StepParams) {
|
export function TileEmergencyStop(props: StepParams) {
|
||||||
const { dispatch, currentStep, index, currentSequence } = props;
|
const { dispatch, currentStep, index, currentSequence } = props;
|
||||||
const className = "take-photo-step";
|
const className = "emergency-stop-step";
|
||||||
return <StepWrapper>
|
return <StepWrapper>
|
||||||
<StepHeader
|
<StepHeader
|
||||||
className={className}
|
className={className}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
StepWrapper, StepHeader, StepContent, StepWarning, conflictsString
|
StepWrapper, StepHeader, StepContent, StepWarning, conflictsString
|
||||||
} from "../step_ui/index";
|
} from "../step_ui/index";
|
||||||
import { some } from "lodash";
|
import { some } from "lodash";
|
||||||
import { StepRadio } from "../step_ui/step_radio";
|
import { AxisStepRadio } from "../step_ui/step_radio";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
|
||||||
export function TileFindHome(props: StepParams) {
|
export function TileFindHome(props: StepParams) {
|
||||||
|
@ -80,7 +80,7 @@ class InnerFindHome extends React.Component<FindHomeParams, {}> {
|
||||||
conflicts={this.settingConflicts} />}
|
conflicts={this.settingConflicts} />}
|
||||||
</StepHeader>
|
</StepHeader>
|
||||||
<StepContent className={className}>
|
<StepContent className={className}>
|
||||||
<StepRadio
|
<AxisStepRadio
|
||||||
currentSequence={currentSequence}
|
currentSequence={currentSequence}
|
||||||
currentStep={currentStep}
|
currentStep={currentStep}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
|
|
@ -2,10 +2,11 @@ import * as React from "react";
|
||||||
import { StepParams } from "../interfaces";
|
import { StepParams } from "../interfaces";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||||
import { StepRadio } from "../step_ui/step_radio";
|
import { AxisStepRadio } from "../step_ui/step_radio";
|
||||||
import { StepInputBox } from "../inputs/step_input_box";
|
import { StepInputBox } from "../inputs/step_input_box";
|
||||||
import { Row, Col } from "../../ui";
|
import { Row, Col } from "../../ui";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import { Home } from "farmbot";
|
||||||
|
|
||||||
export function TileMoveHome(props: StepParams) {
|
export function TileMoveHome(props: StepParams) {
|
||||||
const { dispatch, currentStep, index, currentSequence } = props;
|
const { dispatch, currentStep, index, currentSequence } = props;
|
||||||
|
@ -20,9 +21,9 @@ export function TileMoveHome(props: StepParams) {
|
||||||
index={index}
|
index={index}
|
||||||
confirmStepDeletion={props.confirmStepDeletion} />
|
confirmStepDeletion={props.confirmStepDeletion} />
|
||||||
<StepContent className={className}>
|
<StepContent className={className}>
|
||||||
<StepRadio
|
<AxisStepRadio
|
||||||
currentSequence={currentSequence}
|
currentSequence={currentSequence}
|
||||||
currentStep={currentStep}
|
currentStep={currentStep as Home}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
index={index}
|
index={index}
|
||||||
label={t("Home")} />
|
label={t("Home")} />
|
||||||
|
|
|
@ -3,52 +3,18 @@ import { StepParams } from "../interfaces";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { Row, Col } from "../../ui";
|
|
||||||
import { ALLOWED_PACKAGES, SequenceBodyItem, Reboot } from "farmbot";
|
import { ALLOWED_PACKAGES, SequenceBodyItem, Reboot } from "farmbot";
|
||||||
import { editStep } from "../../api/crud";
|
import { editStep } from "../../api/crud";
|
||||||
|
import { StepRadio } from "../step_ui/step_radio";
|
||||||
|
|
||||||
type StringMap = Record<string, string>;
|
const PACKAGE_CHOICES = (): Record<ALLOWED_PACKAGES, string> => ({
|
||||||
|
"arduino_firmware": t("Just the Arduino"),
|
||||||
interface MultiChoiceRadioProps<T extends StringMap> {
|
"farmbot_os": t("Entire system")
|
||||||
uuid: string;
|
});
|
||||||
choices: T;
|
|
||||||
currentChoice: keyof T;
|
|
||||||
onChange(key: keyof T): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MultiChoiceRadio =
|
|
||||||
<T extends StringMap>(props: MultiChoiceRadioProps<T>) => {
|
|
||||||
const choices = Object.keys(props.choices);
|
|
||||||
return <Row>
|
|
||||||
<Col xs={12}>
|
|
||||||
<div className="bottom-content">
|
|
||||||
<div className="channel-fields">
|
|
||||||
<form>
|
|
||||||
{choices.map((choice, i) =>
|
|
||||||
<div key={`${props.uuid} ${i}`} style={{ display: "inline" }}>
|
|
||||||
<label>
|
|
||||||
<input type="radio"
|
|
||||||
value={choice}
|
|
||||||
onChange={() => props.onChange(choice)}
|
|
||||||
checked={props.currentChoice === choice} />
|
|
||||||
{t(props.choices[choice])}
|
|
||||||
</label>
|
|
||||||
</div>)}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</Row>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const PACKAGE_CHOICES: Record<ALLOWED_PACKAGES, string> = {
|
|
||||||
"arduino_firmware": "Just the Arduino",
|
|
||||||
"farmbot_os": "Entire system"
|
|
||||||
};
|
|
||||||
|
|
||||||
function assertReboot(x: SequenceBodyItem): asserts x is Reboot {
|
function assertReboot(x: SequenceBodyItem): asserts x is Reboot {
|
||||||
if (x.kind !== "reboot") {
|
if (x.kind !== "reboot") {
|
||||||
throw new Error("Impossible");
|
throw new Error(`${x.kind} is not "reboot"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +40,7 @@ export const editTheRebootStep =
|
||||||
|
|
||||||
export function TileReboot(props: StepParams) {
|
export function TileReboot(props: StepParams) {
|
||||||
const { dispatch, currentStep, index, currentSequence } = props;
|
const { dispatch, currentStep, index, currentSequence } = props;
|
||||||
const className = "take-photo-step";
|
const className = "reboot-step";
|
||||||
assertReboot(currentStep);
|
assertReboot(currentStep);
|
||||||
return <StepWrapper>
|
return <StepWrapper>
|
||||||
<StepHeader
|
<StepHeader
|
||||||
|
@ -86,9 +52,9 @@ export function TileReboot(props: StepParams) {
|
||||||
index={index}
|
index={index}
|
||||||
confirmStepDeletion={props.confirmStepDeletion} />
|
confirmStepDeletion={props.confirmStepDeletion} />
|
||||||
<StepContent className={className}>
|
<StepContent className={className}>
|
||||||
<MultiChoiceRadio
|
<StepRadio
|
||||||
uuid={currentSequence.uuid + index}
|
choices={Object.keys(PACKAGE_CHOICES())}
|
||||||
choices={PACKAGE_CHOICES}
|
choiceLabelLookup={PACKAGE_CHOICES()}
|
||||||
currentChoice={currentStep.args.package as ALLOWED_PACKAGES}
|
currentChoice={currentStep.args.package as ALLOWED_PACKAGES}
|
||||||
onChange={editTheRebootStep(props)} />
|
onChange={editTheRebootStep(props)} />
|
||||||
</StepContent>
|
</StepContent>
|
||||||
|
|
|
@ -2,17 +2,18 @@ import * as React from "react";
|
||||||
import { StepInputBox } from "../inputs/step_input_box";
|
import { StepInputBox } from "../inputs/step_input_box";
|
||||||
import { StepParams } from "../interfaces";
|
import { StepParams } from "../interfaces";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
import { StepWrapper, StepHeader, StepContent } from "../step_ui";
|
||||||
import { Row, Col } from "../../ui/index";
|
import { Row, Col } from "../../ui/index";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { MultiChoiceRadio } from "./tile_reboot";
|
|
||||||
import { SetServoAngle } from "farmbot";
|
import { SetServoAngle } from "farmbot";
|
||||||
import { editStep } from "../../api/crud";
|
import { editStep } from "../../api/crud";
|
||||||
|
import { StepRadio } from "../step_ui/step_radio";
|
||||||
|
|
||||||
const PACKAGE_CHOICES: Record<string, string> = {
|
const PIN_CHOICES = ["4", "5", "6", "11"];
|
||||||
"4": "Pin 4",
|
const CHOICE_LABELS = () => PIN_CHOICES.reduce((acc, pinNumber) => {
|
||||||
"5": "Pin 5",
|
acc[pinNumber] = `${t("Pin")} ${pinNumber}`;
|
||||||
};
|
return acc;
|
||||||
|
}, {} as Record<string, string>);
|
||||||
|
|
||||||
type Keys =
|
type Keys =
|
||||||
| "dispatch"
|
| "dispatch"
|
||||||
|
@ -35,14 +36,14 @@ export const pinNumberChanger = (props: Props) => (y: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ServoPinSelection(props: Props) {
|
export function ServoPinSelection(props: Props) {
|
||||||
const { currentSequence, index, currentStep } = props;
|
const { currentStep } = props;
|
||||||
const num = (currentStep as SetServoAngle).args.pin_number;
|
const num = (currentStep as SetServoAngle).args.pin_number;
|
||||||
if (typeof num !== "number") { throw new Error("NO!"); }
|
if (typeof num !== "number") { throw new Error("NO!"); }
|
||||||
const onChange = pinNumberChanger(props);
|
const onChange = pinNumberChanger(props);
|
||||||
|
|
||||||
return <MultiChoiceRadio
|
return <StepRadio
|
||||||
uuid={currentSequence.uuid + index}
|
choices={PIN_CHOICES}
|
||||||
choices={PACKAGE_CHOICES}
|
choiceLabelLookup={CHOICE_LABELS()}
|
||||||
currentChoice={"" + num}
|
currentChoice={"" + num}
|
||||||
onChange={onChange} />;
|
onChange={onChange} />;
|
||||||
}
|
}
|
||||||
|
@ -79,5 +80,4 @@ export function TileSetServoAngle(props: StepParams) {
|
||||||
</Row>
|
</Row>
|
||||||
</StepContent>
|
</StepContent>
|
||||||
</StepWrapper>;
|
</StepWrapper>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@ import * as React from "react";
|
||||||
import { StepParams } from "../interfaces";
|
import { StepParams } from "../interfaces";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||||
import { StepRadio } from "../step_ui/step_radio";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import { AxisStepRadio } from "../step_ui/step_radio";
|
||||||
|
import { Zero } from "farmbot";
|
||||||
|
|
||||||
export function TileSetZero(props: StepParams) {
|
export function TileSetZero(props: StepParams) {
|
||||||
const { dispatch, currentStep, index, currentSequence } = props;
|
const { dispatch, currentStep, index, currentSequence } = props;
|
||||||
|
@ -18,9 +19,9 @@ export function TileSetZero(props: StepParams) {
|
||||||
index={index}
|
index={index}
|
||||||
confirmStepDeletion={props.confirmStepDeletion} />
|
confirmStepDeletion={props.confirmStepDeletion} />
|
||||||
<StepContent className={className}>
|
<StepContent className={className}>
|
||||||
<StepRadio
|
<AxisStepRadio
|
||||||
currentSequence={currentSequence}
|
currentSequence={currentSequence}
|
||||||
currentStep={currentStep}
|
currentStep={currentStep as Zero}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
index={index}
|
index={index}
|
||||||
label={t("Zero")} />
|
label={t("Zero")} />
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
jest.mock("../../../api/crud", () => ({ overwrite: jest.fn() }));
|
let mockStep = {};
|
||||||
|
jest.mock("../../../api/crud", () => ({
|
||||||
|
editStep: jest.fn(x => x.executor(mockStep)),
|
||||||
|
}));
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { mount } from "enzyme";
|
import { mount } from "enzyme";
|
||||||
import { StepRadio, StepRadioProps } from "../step_radio";
|
import { AxisStepRadio, AxisStepRadioProps } from "../step_radio";
|
||||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||||
import { FindHome, Calibrate, Zero } from "farmbot";
|
import { FindHome, Calibrate, Zero } from "farmbot";
|
||||||
import { overwrite } from "../../../api/crud";
|
|
||||||
|
|
||||||
describe("<StepRadio />", () => {
|
describe("<StepRadio />", () => {
|
||||||
const currentStep: FindHome = {
|
const findHomeStep: FindHome = {
|
||||||
kind: "find_home",
|
kind: "find_home",
|
||||||
args: {
|
args: {
|
||||||
speed: 100,
|
speed: 100,
|
||||||
|
@ -16,55 +18,55 @@ describe("<StepRadio />", () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fakeProps = (): StepRadioProps => ({
|
const fakeProps = (): AxisStepRadioProps => ({
|
||||||
currentSequence: fakeSequence(),
|
currentSequence: fakeSequence(),
|
||||||
currentStep,
|
currentStep: findHomeStep,
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
index: 0,
|
index: 0,
|
||||||
label: "Find",
|
label: "Find",
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
const wrapper = mount(<StepRadio {...fakeProps()} />);
|
const wrapper = mount(<AxisStepRadio {...fakeProps()} />);
|
||||||
expect(wrapper.find("input").length).toEqual(4);
|
expect(wrapper.find("input").length).toEqual(4);
|
||||||
expect(wrapper.text()).toContain("Find");
|
expect(wrapper.text()).toContain("Find");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles update for find_home", () => {
|
it("handles update for find_home", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
const wrapper = mount(<StepRadio {...p} />);
|
mockStep = p.currentStep;
|
||||||
|
const wrapper = mount(<AxisStepRadio {...p} />);
|
||||||
wrapper.find("input").last().simulate("change");
|
wrapper.find("input").last().simulate("change");
|
||||||
const expectedStep: FindHome = {
|
const expectedStep: FindHome = {
|
||||||
kind: "find_home",
|
kind: "find_home",
|
||||||
args: { speed: 100, axis: "all" }
|
args: { speed: 100, axis: "all" }
|
||||||
};
|
};
|
||||||
expect(overwrite).toHaveBeenCalledWith(p.currentSequence,
|
expect(mockStep).toEqual(expectedStep);
|
||||||
expect.objectContaining({ body: [expectedStep] }));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles update for calibrate", () => {
|
it("handles update for calibrate", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.currentStep = { kind: "calibrate", args: { axis: "x" } };
|
p.currentStep = { kind: "calibrate", args: { axis: "x" } };
|
||||||
const wrapper = mount(<StepRadio {...p} />);
|
mockStep = p.currentStep;
|
||||||
|
const wrapper = mount(<AxisStepRadio {...p} />);
|
||||||
wrapper.find("input").last().simulate("change");
|
wrapper.find("input").last().simulate("change");
|
||||||
const expectedStep: Calibrate = {
|
const expectedStep: Calibrate = {
|
||||||
kind: "calibrate",
|
kind: "calibrate",
|
||||||
args: { axis: "all" }
|
args: { axis: "all" }
|
||||||
};
|
};
|
||||||
expect(overwrite).toHaveBeenCalledWith(p.currentSequence,
|
expect(mockStep).toEqual(expectedStep);
|
||||||
expect.objectContaining({ body: [expectedStep] }));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles update for zero", () => {
|
it("handles update for zero", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.currentStep = { kind: "zero", args: { axis: "x" } };
|
p.currentStep = { kind: "zero", args: { axis: "x" } };
|
||||||
const wrapper = mount(<StepRadio {...p} />);
|
mockStep = p.currentStep;
|
||||||
|
const wrapper = mount(<AxisStepRadio {...p} />);
|
||||||
wrapper.find("input").last().simulate("change");
|
wrapper.find("input").last().simulate("change");
|
||||||
const expectedStep: Zero = {
|
const expectedStep: Zero = {
|
||||||
kind: "zero",
|
kind: "zero",
|
||||||
args: { axis: "all" }
|
args: { axis: "all" }
|
||||||
};
|
};
|
||||||
expect(overwrite).toHaveBeenCalledWith(p.currentSequence,
|
expect(mockStep).toEqual(expectedStep);
|
||||||
expect.objectContaining({ body: [expectedStep] }));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,57 +1,32 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Row, Col } from "../../ui/index";
|
import { Row, Col } from "../../ui/index";
|
||||||
import {
|
|
||||||
TaggedSequence, SequenceBodyItem, ALLOWED_AXIS
|
|
||||||
} from "farmbot";
|
|
||||||
import { overwrite } from "../../api/crud";
|
|
||||||
import { defensiveClone } from "../../util";
|
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import {
|
||||||
|
TaggedSequence, ALLOWED_AXIS, FindHome, Home, Calibrate, Zero
|
||||||
|
} from "farmbot";
|
||||||
|
import { editStep } from "../../api/crud";
|
||||||
|
|
||||||
export interface StepRadioProps {
|
export interface StepRadioProps<T extends string> {
|
||||||
currentSequence: TaggedSequence;
|
choices: T[];
|
||||||
currentStep: SequenceBodyItem;
|
choiceLabelLookup: Record<T, string>;
|
||||||
dispatch: Function;
|
currentChoice: T;
|
||||||
index: number;
|
onChange(key: T): void;
|
||||||
label: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AXIS_CHOICES: ALLOWED_AXIS[] = ["x", "y", "z", "all"];
|
export const StepRadio = <T extends string>(props: StepRadioProps<T>) =>
|
||||||
|
<Row>
|
||||||
export function StepRadio(props: StepRadioProps) {
|
|
||||||
const isSelected = (choice: ALLOWED_AXIS) => {
|
|
||||||
if (props.currentStep.kind === "find_home"
|
|
||||||
|| props.currentStep.kind === "calibrate"
|
|
||||||
|| props.currentStep.kind === "zero") {
|
|
||||||
return props.currentStep.args.axis === choice;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdate = (choice: ALLOWED_AXIS) => {
|
|
||||||
const update = defensiveClone(props.currentStep);
|
|
||||||
if (update.kind === "find_home"
|
|
||||||
|| update.kind === "calibrate"
|
|
||||||
|| update.kind === "zero") {
|
|
||||||
const nextSequence = defensiveClone(props.currentSequence).body;
|
|
||||||
update.args.axis = choice;
|
|
||||||
(nextSequence.body || [])[props.index] = update;
|
|
||||||
props.dispatch(overwrite(props.currentSequence, nextSequence));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Row>
|
|
||||||
<Col xs={12}>
|
<Col xs={12}>
|
||||||
<div className="bottom-content">
|
<div className="bottom-content">
|
||||||
<div className="channel-fields">
|
<div className="channel-fields">
|
||||||
<form>
|
<form>
|
||||||
{AXIS_CHOICES.map((choice, i) =>
|
{props.choices.map((choice, i) =>
|
||||||
<div key={i} style={{ display: "inline" }}>
|
<div key={i} style={{ display: "inline" }}>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio"
|
<input type="radio"
|
||||||
value={choice}
|
value={choice}
|
||||||
onChange={e =>
|
onChange={() => props.onChange(choice)}
|
||||||
handleUpdate(e.currentTarget.value as typeof choice)}
|
checked={props.currentChoice === choice} />
|
||||||
checked={isSelected(choice)} />
|
{t(props.choiceLabelLookup[choice])}
|
||||||
{` ${t(props.label)} ${choice}`}
|
|
||||||
</label>
|
</label>
|
||||||
</div>)}
|
</div>)}
|
||||||
</form>
|
</form>
|
||||||
|
@ -59,4 +34,37 @@ export function StepRadio(props: StepRadioProps) {
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>;
|
</Row>;
|
||||||
|
|
||||||
|
type AxisStep = FindHome | Home | Calibrate | Zero;
|
||||||
|
|
||||||
|
export interface AxisStepRadioProps {
|
||||||
|
currentSequence: TaggedSequence;
|
||||||
|
currentStep: AxisStep;
|
||||||
|
dispatch: Function;
|
||||||
|
index: number;
|
||||||
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const AxisStepRadio = (props: AxisStepRadioProps) => {
|
||||||
|
const AXIS_CHOICES: ALLOWED_AXIS[] = ["x", "y", "z", "all"];
|
||||||
|
const CHOICE_LABELS = AXIS_CHOICES.reduce((acc, axis) => {
|
||||||
|
acc[axis] = `${t(props.label)} ${axis}`;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<ALLOWED_AXIS, string>);
|
||||||
|
|
||||||
|
const handleUpdate = (axis: ALLOWED_AXIS) => {
|
||||||
|
const { currentStep, index, currentSequence } = props;
|
||||||
|
props.dispatch(editStep({
|
||||||
|
step: currentStep,
|
||||||
|
index,
|
||||||
|
sequence: currentSequence,
|
||||||
|
executor: (step: AxisStep) => step.args.axis = axis,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return <StepRadio
|
||||||
|
choices={AXIS_CHOICES}
|
||||||
|
choiceLabelLookup={CHOICE_LABELS}
|
||||||
|
currentChoice={props.currentStep.args.axis}
|
||||||
|
onChange={handleUpdate} />;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue