diff --git a/frontend/__test_support__/fake_designer_state.ts b/frontend/__test_support__/fake_designer_state.ts
index b82b5e69ec..b05c304aec 100644
--- a/frontend/__test_support__/fake_designer_state.ts
+++ b/frontend/__test_support__/fake_designer_state.ts
@@ -1,5 +1,6 @@
import { DesignerState } from "../farm_designer/interfaces";
import { HelpState } from "../help/reducer";
+import { RunButtonMenuOpen } from "../sequences/interfaces";
export const fakeDesignerState = (): DesignerState => ({
selectedPoints: undefined,
@@ -56,3 +57,8 @@ export const fakeHelpState = (): HelpState => ({
currentTour: undefined,
currentTourStep: undefined,
});
+
+export const fakeMenuOpenState = (): RunButtonMenuOpen => ({
+ component: undefined,
+ uuid: undefined,
+});
diff --git a/frontend/__tests__/app_test.tsx b/frontend/__tests__/app_test.tsx
index a57271d01c..10cafdeb56 100644
--- a/frontend/__tests__/app_test.tsx
+++ b/frontend/__tests__/app_test.tsx
@@ -29,7 +29,9 @@ import { fakeTimeSettings } from "../__test_support__/fake_time_settings";
import { error, warning } from "../toast/toast";
import { fakePings } from "../__test_support__/fake_state/pings";
import { auth } from "../__test_support__/fake_state/token";
-import { fakeHelpState } from "../__test_support__/fake_designer_state";
+import {
+ fakeHelpState, fakeMenuOpenState,
+} from "../__test_support__/fake_designer_state";
import { Path } from "../internal_urls";
import { push } from "../history";
import { app } from "../__test_support__/fake_state/app";
@@ -65,7 +67,7 @@ const fakeProps = (): AppProps => ({
feeds: [],
peripherals: [],
sequences: [],
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
});
describe(": Loading", () => {
diff --git a/frontend/app.tsx b/frontend/app.tsx
index a402016ee8..f503b7ed09 100644
--- a/frontend/app.tsx
+++ b/frontend/app.tsx
@@ -37,7 +37,7 @@ import { FirmwareConfig } from "farmbot/dist/resources/configs/firmware";
import { getFirmwareConfig, getFbosConfig } from "./resources/getters";
import { intersection, isString, uniq } from "lodash";
import { t } from "./i18next_wrapper";
-import { ResourceIndex, UUID } from "./resources/interfaces";
+import { ResourceIndex } from "./resources/interfaces";
import { getAllAlerts } from "./messages/state_to_props";
import { PingDictionary } from "./devices/connectivity/qos";
import { getEnv } from "./farmware/state_to_props";
@@ -55,6 +55,7 @@ import { AppState } from "./reducer";
import {
sourceFbosConfigValue, sourceFwConfigValue,
} from "./settings/source_config_value";
+import { RunButtonMenuOpen } from "./sequences/interfaces";
export interface AppProps {
dispatch: Function;
@@ -83,7 +84,7 @@ export interface AppProps {
feeds: TaggedWebcamFeed[];
peripherals: TaggedPeripheral[];
sequences: TaggedSequence[];
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
appState: AppState;
children?: React.ReactNode;
}
diff --git a/frontend/controls/__tests__/controls_test.tsx b/frontend/controls/__tests__/controls_test.tsx
index 4c5b985957..a53cbde1cb 100644
--- a/frontend/controls/__tests__/controls_test.tsx
+++ b/frontend/controls/__tests__/controls_test.tsx
@@ -11,6 +11,7 @@ import { DesignerControlsProps } from "../interfaces";
import { fakeMovementState } from "../../__test_support__/fake_bot_data";
import { app } from "../../__test_support__/fake_state/app";
import { Actions } from "../../constants";
+import { fakeMenuOpenState } from "../../__test_support__/fake_designer_state";
describe("", () => {
const fakeProps = (): DesignerControlsProps => ({
@@ -20,7 +21,7 @@ describe("", () => {
peripherals: [],
sequences: [],
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
firmwareSettings: bot.hardware.mcu_params,
getConfigValue: jest.fn(),
sourceFwConfig: () => ({ value: 0, consistent: true }),
@@ -54,7 +55,7 @@ describe("", () => {
peripherals: [],
sequences: [],
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
firmwareSettings: bot.hardware.mcu_params,
});
diff --git a/frontend/controls/__tests__/pinned_sequence_list_test.tsx b/frontend/controls/__tests__/pinned_sequence_list_test.tsx
index 2a4ecfb352..1d19020511 100644
--- a/frontend/controls/__tests__/pinned_sequence_list_test.tsx
+++ b/frontend/controls/__tests__/pinned_sequence_list_test.tsx
@@ -4,13 +4,14 @@ import { PinnedSequences } from "../pinned_sequence_list";
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
import { PinnedSequencesProps } from "../interfaces";
import { fakeSequence } from "../../__test_support__/fake_state/resources";
+import { fakeMenuOpenState } from "../../__test_support__/fake_designer_state";
describe("", () => {
const fakeProps = (): PinnedSequencesProps => ({
syncStatus: undefined,
sequences: [],
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
dispatch: jest.fn(),
});
diff --git a/frontend/controls/controls.tsx b/frontend/controls/controls.tsx
index 09d3225cd2..3a2e1b5585 100644
--- a/frontend/controls/controls.tsx
+++ b/frontend/controls/controls.tsx
@@ -18,10 +18,11 @@ import {
FirmwareHardware, McuParams, TaggedLog, TaggedPeripheral, TaggedSequence,
TaggedWebcamFeed,
} from "farmbot";
-import { ResourceIndex, UUID } from "../resources/interfaces";
+import { ResourceIndex } from "../resources/interfaces";
import { t } from "../i18next_wrapper";
import { push } from "../history";
import { Path } from "../internal_urls";
+import { RunButtonMenuOpen } from "../sequences/interfaces";
export class RawDesignerControls
extends React.Component {
@@ -52,7 +53,7 @@ export interface ControlsPanelProps {
peripherals: TaggedPeripheral[];
sequences: TaggedSequence[];
resources: ResourceIndex;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
firmwareSettings: McuParams;
}
diff --git a/frontend/controls/interfaces.ts b/frontend/controls/interfaces.ts
index 2c0237fb84..0601d7b558 100644
--- a/frontend/controls/interfaces.ts
+++ b/frontend/controls/interfaces.ts
@@ -5,10 +5,11 @@ import {
Vector3, McuParams, Xyz, AxisState, SyncStatus, TaggedSequence,
FirmwareHardware, TaggedPeripheral, TaggedWebcamFeed, TaggedLog,
} from "farmbot";
-import { ResourceIndex, UUID } from "../resources/interfaces";
+import { ResourceIndex } from "../resources/interfaces";
import { GetWebAppConfigValue } from "../config_storage/actions";
import { MovementState } from "../interfaces";
import { PinBindingListItems } from "../settings/pin_bindings/interfaces";
+import { RunButtonMenuOpen } from "../sequences/interfaces";
export interface AxisDisplayGroupProps {
position: BotPosition;
@@ -58,7 +59,7 @@ export interface PinnedSequencesProps {
syncStatus: SyncStatus | undefined;
sequences: TaggedSequence[];
resources: ResourceIndex;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
dispatch: Function;
}
@@ -69,7 +70,7 @@ export interface DesignerControlsProps {
peripherals: TaggedPeripheral[];
sequences: TaggedSequence[];
resources: ResourceIndex;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
firmwareSettings: McuParams;
getConfigValue: GetWebAppConfigValue;
sourceFwConfig: SourceFwConfig;
diff --git a/frontend/controls/pinned_sequence_list.tsx b/frontend/controls/pinned_sequence_list.tsx
index 3ad69bf479..3d9f6ba109 100644
--- a/frontend/controls/pinned_sequence_list.tsx
+++ b/frontend/controls/pinned_sequence_list.tsx
@@ -22,7 +22,7 @@ export const PinnedSequences = (props: PinnedSequencesProps) => {
- ({
kind: "initial",
@@ -111,7 +112,7 @@ describe("", () => {
sequenceMetas: {},
getWebAppConfigValue: jest.fn(),
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
syncStatus: undefined,
});
@@ -258,7 +259,7 @@ describe("", () => {
inUse: false,
getWebAppConfigValue: jest.fn(),
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
syncStatus: undefined,
searchTerm: undefined,
});
@@ -523,7 +524,7 @@ describe("", () => {
sequenceMetas: {},
getWebAppConfigValue: jest.fn(),
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
syncStatus: undefined,
searchTerm: undefined,
dragging: false,
diff --git a/frontend/folders/component.tsx b/frontend/folders/component.tsx
index 1b0414d617..d01fe00bc9 100644
--- a/frontend/folders/component.tsx
+++ b/frontend/folders/component.tsx
@@ -51,7 +51,7 @@ import {
} from "../sequences/sequence_editor_middle_active";
import { Path } from "../internal_urls";
import { copySequence } from "../sequences/actions";
-import { TestButton } from "../sequences/test_button";
+import { TestButton, isMenuOpen } from "../sequences/test_button";
import { TaggedSequence } from "farmbot";
export const FolderListItem = (props: FolderItemProps) => {
@@ -63,7 +63,9 @@ export const FolderListItem = (props: FolderItemProps) => {
const active = Path.lastChunkEquals(urlFriendly(seqName)) ? "active" : "";
const [settingsOpen, setSettingsOpen] = React.useState(false);
const [descriptionOpen, setDescriptionOpen] = React.useState(false);
- const hovered = props.menuOpen == sequence.uuid || settingsOpen || descriptionOpen
+ const menuOpen = isMenuOpen(props.menuOpen,
+ { component: "list", uuid: sequence.uuid });
+ const hovered = menuOpen || settingsOpen || descriptionOpen
? "hovered"
: "";
const matched = (props.searchTerm &&
@@ -93,7 +95,7 @@ export const FolderListItem = (props: FolderItemProps) => {
draggable={false}>
{nameWithSaveIndicator}
- ;
getWebAppConfigValue: GetWebAppConfigValue;
resources: ResourceIndex;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
syncStatus: SyncStatus | undefined;
}
@@ -95,7 +96,7 @@ export interface FolderNodeProps {
sequenceMetas: Record;
getWebAppConfigValue: GetWebAppConfigValue;
resources: ResourceIndex;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
syncStatus: SyncStatus | undefined;
searchTerm: string | undefined;
}
@@ -127,7 +128,7 @@ export interface FolderItemProps {
inUse: boolean;
getWebAppConfigValue: GetWebAppConfigValue;
resources: ResourceIndex;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
syncStatus: SyncStatus | undefined;
searchTerm: string | undefined;
}
diff --git a/frontend/nav/__tests__/index_test.tsx b/frontend/nav/__tests__/index_test.tsx
index 911a76021c..ad42d17f84 100644
--- a/frontend/nav/__tests__/index_test.tsx
+++ b/frontend/nav/__tests__/index_test.tsx
@@ -23,7 +23,9 @@ import { fakePings } from "../../__test_support__/fake_state/pings";
import { Link } from "../../link";
import { refresh } from "../../api/crud";
import { push } from "../../history";
-import { fakeHelpState } from "../../__test_support__/fake_designer_state";
+import {
+ fakeHelpState, fakeMenuOpenState,
+} from "../../__test_support__/fake_designer_state";
import { Path } from "../../internal_urls";
import { fakePercentJob } from "../../__test_support__/fake_bot_data";
import {
@@ -55,7 +57,7 @@ describe("", () => {
sourceFbosConfig: jest.fn(),
firmwareConfig: fakeFirmwareConfig().body,
resources: buildResourceIndex([]).index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
env: {},
feeds: [],
peripherals: [],
diff --git a/frontend/nav/interfaces.ts b/frontend/nav/interfaces.ts
index af91b48a35..3d8620d68b 100644
--- a/frontend/nav/interfaces.ts
+++ b/frontend/nav/interfaces.ts
@@ -14,8 +14,9 @@ import { TimeSettings } from "../interfaces";
import { PingDictionary } from "../devices/connectivity/qos";
import { HelpState } from "../help/reducer";
import { AppState } from "../reducer";
-import { ResourceIndex, UUID } from "../resources/interfaces";
+import { ResourceIndex } from "../resources/interfaces";
import { FirmwareConfig } from "farmbot/dist/resources/configs/firmware";
+import { RunButtonMenuOpen } from "../sequences/interfaces";
export interface NavBarProps {
logs: TaggedLog[];
@@ -38,7 +39,7 @@ export interface NavBarProps {
helpState: HelpState;
telemetry: TaggedTelemetry[];
appState: AppState;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
env: UserEnv;
feeds: TaggedWebcamFeed[];
peripherals: TaggedPeripheral[];
diff --git a/frontend/sequences/__tests__/test_button_test.tsx b/frontend/sequences/__tests__/test_button_test.tsx
index 571139fc96..0faed09cb6 100644
--- a/frontend/sequences/__tests__/test_button_test.tsx
+++ b/frontend/sequences/__tests__/test_button_test.tsx
@@ -27,15 +27,17 @@ import { fakeVariableNameSet } from "../../__test_support__/fake_variables";
import { SequenceMeta } from "../../resources/sequence_meta";
import { clickButton } from "../../__test_support__/helpers";
import { fakeSequence } from "../../__test_support__/fake_state/resources";
+import { fakeMenuOpenState } from "../../__test_support__/fake_designer_state";
-describe("", () => {
+describe("", () => {
const fakeProps = (): TestBtnProps => ({
sequence: fakeSequence(),
syncStatus: "synced",
resources: buildResourceIndex().index,
- menuOpen: undefined,
+ menuOpen: fakeMenuOpenState(),
dispatch: jest.fn(),
+ component: "list",
});
it("doesn't fire if unsaved", () => {
@@ -88,13 +90,16 @@ describe("", () => {
expect(btn.hasClass("orange")).toBeTruthy();
expect(warning).not.toHaveBeenCalled();
expect(mockDevice.execSequence).not.toHaveBeenCalled();
- expect(props.dispatch).toHaveBeenCalledWith(setMenuOpen(props.sequence.uuid));
+ expect(props.dispatch).toHaveBeenCalledWith(setMenuOpen({
+ component: "list", uuid: props.sequence.uuid,
+ }));
});
it("has open parameter assignment menu", () => {
const props = fakeProps();
mockHasParameters = true;
- props.menuOpen = props.sequence.uuid;
+ props.menuOpen.component = "list";
+ props.menuOpen.uuid = props.sequence.uuid;
const result = mount();
const btn = result.find("button").first();
expect(btn.hasClass("gray")).toBeTruthy();
@@ -104,7 +109,8 @@ describe("", () => {
it("closes parameter assignment menu", () => {
const p = fakeProps();
- p.menuOpen = p.sequence.uuid;
+ p.menuOpen.component = "list";
+ p.menuOpen.uuid = p.sequence.uuid;
p.syncStatus = "synced";
p.sequence.specialStatus = SpecialStatus.SAVED;
p.sequence.body.id = 1;
@@ -115,7 +121,7 @@ describe("", () => {
expect(btn.hasClass("gray")).toBeTruthy();
expect(warning).not.toHaveBeenCalled();
expect(mockDevice.execSequence).not.toHaveBeenCalled();
- expect(p.dispatch).toHaveBeenCalledWith(setMenuOpen(undefined));
+ expect(p.dispatch).toHaveBeenCalledWith(setMenuOpen(fakeMenuOpenState()));
});
it("edits body variables", () => {
@@ -185,6 +191,6 @@ describe("", () => {
const props = fakeProps();
const wrapper = mount();
wrapper.unmount();
- expect(props.dispatch).toHaveBeenCalledWith(setMenuOpen(undefined));
+ expect(props.dispatch).toHaveBeenCalledWith(setMenuOpen(fakeMenuOpenState()));
});
});
diff --git a/frontend/sequences/interfaces.ts b/frontend/sequences/interfaces.ts
index cfb23341e1..6d65176efc 100644
--- a/frontend/sequences/interfaces.ts
+++ b/frontend/sequences/interfaces.ts
@@ -139,9 +139,14 @@ export const MESSAGE_TYPES = Object.keys(MessageType);
export const isMessageType = (x: any): x is ALLOWED_MESSAGE_TYPES =>
MESSAGE_TYPES.includes(x as string);
+export interface RunButtonMenuOpen {
+ component: "list" | "editor" | "pinned" | undefined;
+ uuid: UUID | undefined;
+}
+
export interface SequenceReducerState {
current: string | undefined;
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
stepIndex: number | undefined;
}
diff --git a/frontend/sequences/reducer.ts b/frontend/sequences/reducer.ts
index ad8ce476b8..b4ee40e2d9 100644
--- a/frontend/sequences/reducer.ts
+++ b/frontend/sequences/reducer.ts
@@ -1,12 +1,11 @@
-import { SequenceReducerState } from "./interfaces";
+import { RunButtonMenuOpen, SequenceReducerState } from "./interfaces";
import { generateReducer } from "../redux/generate_reducer";
import { TaggedResource } from "farmbot";
import { Actions } from "../constants";
-import { UUID } from "../resources/interfaces";
export const initialState: SequenceReducerState = {
current: undefined,
- menuOpen: undefined,
+ menuOpen: { component: undefined, uuid: undefined },
stepIndex: undefined,
};
@@ -23,7 +22,7 @@ export const sequenceReducer = generateReducer(initialStat
s.current = payload;
return s;
})
- .add(Actions.SET_SEQUENCE_POPUP_STATE, (s, { payload }) => {
+ .add(Actions.SET_SEQUENCE_POPUP_STATE, (s, { payload }) => {
s.menuOpen = payload;
return s;
})
diff --git a/frontend/sequences/sequence_editor_middle_active.tsx b/frontend/sequences/sequence_editor_middle_active.tsx
index 94643f33a7..1d7f13f1f0 100644
--- a/frontend/sequences/sequence_editor_middle_active.tsx
+++ b/frontend/sequences/sequence_editor_middle_active.tsx
@@ -275,7 +275,7 @@ export const SequenceBtnGroup = ({
dispatch(save(sequence.uuid)).then(() =>
push(Path.sequences(urlFriendly(sequence.body.name))))} />
- store.dispatch(selectSequence(uuid));
export function setActiveSequenceByName() {
const chunk = Path.getLastChunk();
- store.dispatch(setMenuOpen(undefined));
+ store.dispatch(setMenuOpen({ component: undefined, uuid: undefined }));
if (!chunk || chunk == "sequences") {
return;
}
diff --git a/frontend/sequences/test_button.tsx b/frontend/sequences/test_button.tsx
index 119465ae92..ea6f6fa050 100644
--- a/frontend/sequences/test_button.tsx
+++ b/frontend/sequences/test_button.tsx
@@ -13,13 +13,23 @@ import { t } from "../i18next_wrapper";
import { warning } from "../toast/toast";
import { forceOnline } from "../devices/must_be_online";
import { Popover } from "../ui";
+import { RunButtonMenuOpen } from "./interfaces";
+
+export const isMenuOpen = (
+ state: RunButtonMenuOpen,
+ current: RunButtonMenuOpen,
+): boolean => (state.component == current.component) && (state.uuid == current.uuid);
+
+const closedMenu = (): RunButtonMenuOpen => ({
+ component: undefined, uuid: undefined,
+});
/** Can't test without saving and syncing sequence. */
const saveAndSyncWarning = () =>
warning(t("Save sequence and sync device before running."));
/** Open or close the sequence test parameter assignment menu. */
-export const setMenuOpen = (payload: UUID | undefined) => ({
+export const setMenuOpen = (payload: RunButtonMenuOpen) => ({
type: Actions.SET_SEQUENCE_POPUP_STATE,
payload
});
@@ -43,12 +53,12 @@ class ParameterAssignmentMenu
extends React.Component {
componentWillUnmount() {
- this.props.dispatch(setMenuOpen(undefined));
+ this.props.dispatch(setMenuOpen(closedMenu()));
}
/** Click actions for test button inside parameter assignment menu. */
onClick = () => {
- this.props.dispatch(setMenuOpen(undefined));
+ this.props.dispatch(setMenuOpen(closedMenu()));
this.props.canTest
? execSequence(this.props.sequence.body.id, this.props.bodyVariables)
: saveAndSyncWarning();
@@ -98,7 +108,8 @@ export interface TestBtnProps {
sequence: TaggedSequence;
resources: ResourceIndex;
/** Parameter assignment menu open? */
- menuOpen: UUID | undefined;
+ menuOpen: RunButtonMenuOpen;
+ component: RunButtonMenuOpen["component"];
dispatch: Function;
}
@@ -131,14 +142,16 @@ export class TestButton extends React.Component {
/** Click actions for test button. */
onClick = () => {
- const { dispatch, menuOpen, sequence } = this.props;
+ const { dispatch, menuOpen, sequence, component } = this.props;
const sequenceBody = sequence.body;
const bodyVariables =
mergeParameterApplications(this.varData, this.state.bodyVariables);
this.setState({ bodyVariables });
/** Open the variable menu if the sequence has parameter declarations. */
isParameterized(sequenceBody) && dispatch(setMenuOpen(
- menuOpen == sequence.uuid ? undefined : sequence.uuid));
+ isMenuOpen(menuOpen, { component, uuid: sequence.uuid })
+ ? closedMenu()
+ : { component, uuid: sequence.uuid }));
this.canTest
/** Execute if sequence is synced, saved, and doesn't use parameters. */
? !isParameterized(sequenceBody) && execSequence(sequenceBody.id)
@@ -146,9 +159,9 @@ export class TestButton extends React.Component {
};
render() {
- const { menuOpen, sequence } = this.props;
+ const { menuOpen, sequence, component } = this.props;
const hasMenu = isParameterized(this.props.sequence.body);
- const isOpen = menuOpen == sequence.uuid;
+ const isOpen = isMenuOpen(menuOpen, { component, uuid: sequence.uuid });
return